{
"name": "ct-await",
"description": "Wait, observably and boundedly, for an external outcome: poll a gated read-only probe every --every seconds (default 5) until the condition is established — probe exit 0, or a required --ok-match pattern appearing in its output — or until an --err-match pattern appears (immediate ERROR, decisive over everything — a failed build reports in seconds, not after the timeout; exactly ct-test's matcher precedence, and a supplied --ok-match is the REQUIRED proof: exit 0 without it is still 'not yet'), or until the REQUIRED --timeout expires (ERROR; a wait is bounded by design, and no single probe run can outlive the bound — process-group kill). The probe is an argv after `--`, launched directly each tick (never through a shell) from the current directory; matchers see only what the probe prints (pair them with content-surfacing probes like cat or ct-view; self-classifying probes like ct-search --quiet need none); any non-zero probe exit simply means 'not yet' (a log file that does not exist yet is the normal waiting case) — only a probe that cannot launch, or a non-gated probe, is exit 2. Probes are the same fixed read-only set ct-each dispatches by default: the allowlist (cat ct-check ct-deps ct-outline ct-search ct-tree ct-view echo false file grep head ls pwd stat tail true wc) plus ct-test and ct-each (without --mutating); ct-check included means 'wait until the project's invariants hold' is one command. ct-await separates observation authority from execution authority: whoever owns the work (a human's mvn run, CI, a deploy) executes it; ct-await only watches its effects. ct-await itself is on no gate (it is a dispatcher, like ct-each). On ERROR the reason goes to stderr. --heartbeat pulses while waiting ({ELAPSED} {TOOL} {QUESTION} {CMD} {TICKS}); --emit/--emit-stderr templates take {RESULT} {ELAPSED} {TICKS} {REASON} {QUESTION} {CMD}. Exit: 0 the condition was established within the bound, 1 err-match matched or the timeout expired, 2 usage error / refused probe / launch failure. Invoke as `ct await ...` or `ct-await ...`.",
"input_schema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "Question this wait answers; printed as a '== ... ==' banner unless --quiet."
},
"every": {
"type": "number",
"description": "Seconds between probe runs (fractional allowed). Default: 5.",
"default": 5
},
"timeout": {
"type": "number",
"description": "Required hard bound on the whole wait in seconds (fractional allowed). Expiry is ERROR with a reason naming the bound and the probe-run count."
},
"ok-match": {
"type": "string",
"description": "SUCCESS when this pattern (substring->glob->regex promoted) appears in the probe's stdout or stderr. When supplied it is the required proof: a probe exiting 0 without it is still 'not yet' (fail-closed)."
},
"err-match": {
"type": "string",
"description": "End the wait immediately with ERROR when this pattern appears in the probe's output. Decisive over --ok-match and exit status, exactly as in ct-test."
},
"mode": {
"type": "string",
"enum": [
"literal",
"glob",
"regex"
],
"description": "Pin how --ok-match/--err-match patterns are interpreted; promotion off. Use literal for verbatim code anchors."
},
"emit": {
"type": "string",
"description": "Template written to stdout when the wait ends (alias: emit-stdout). Tokens: {RESULT} {ELAPSED} {TICKS} {REASON} {QUESTION} {CMD}."
},
"emit-stderr": {
"type": "string",
"description": "Template written to stderr when the wait ends. Same tokens as emit."
},
"quiet": {
"type": "boolean",
"description": "Suppress the banner and the default outcome line (stderr reasons and emits remain)."
},
"heartbeat": {
"type": "number",
"description": "Print a liveness pulse every SECS seconds (fractional allowed) while waiting."
},
"heartbeat-emit": {
"type": "string",
"description": "Heartbeat line template. Tokens: {ELAPSED} (whole seconds so far) {TOOL} {QUESTION} {CMD} {TICKS}. Default: \"[{ELAPSED}s]\"."
},
"heartbeat-to": {
"type": "string",
"enum": [
"stderr",
"stdout"
],
"description": "Stream heartbeat pulses are written to. Default: stderr."
},
"probe": {
"type": "array",
"items": {
"type": "string"
},
"description": "The probe argv (after `--`), run directly each tick — never through a shell. Exit 0 (or the required --ok-match appearing) ends the wait with SUCCESS; any other outcome means 'not yet'. Must be on the fixed read-only gate."
}
},
"required": [
"timeout",
"probe"
]
}
}