Table of contents
wf-cli
The operator surface of the executor: the wf binary. The library (lib.rs) holds the workflow model, execution engine, and event emitter; main.rs is a thin wrapper over commands::dispatch, so integration tests exercise exactly the surface the binary uses.
Commands
wf run <workflow.yaml> [args...] run a workflow (args reach steps as WF_ARGS, newline-joined)
wf check <workflow.yaml> validate a workflow without running it
wf help show help
wf check runs the full executor-core pipeline — parse, normalize, bind — and stops there. wf run executes.
Exit codes — stable contract
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | a step failed or timed out |
| 2 | usage error or invalid workflow (Parse/Validation) |
| 3 | gate rejected the run (Gate) |
| 4 | sandbox error (Sandbox) |
| 5 | harness error — step could not be executed (Harness) |
| 6 | i/o error (Io) |
The mapping lives in exit_code(&WorkflowError): one error kind from executor-core, one code. The numbering is a published contract (wf help prints it); scripts may rely on it.
JSON event stream
wf run emits structured progress events as one self-contained JSON object per line on stdout — incrementally consumable with jq -c or a log shipper, and the same stream the integration tests parse. Diagnostics go to stderr, never mixed into the event stream.
Events observed on a run:
run_started— workflow name, step countstep_started— step name, toolstep_finished— step, exit code, handover bytes, duration, captured stdoutstep_failed— step, exit code, duration, captured stdout
Step stdout echoed inside an event is clipped at 64 KiB; past that the stdout field is truncated and stdout_truncated is set — the full stream is never silently dropped.
Verification
The crate carries an integration suite that drives the same dispatch the binary uses and parses the emitted event lines, so the exit-code and event contracts are checked against the real surface, not a mock.