exit step is a plan’s escape hatch: a gate that ends execution early with an explicit success or error state — skipping every remaining step and the solver. Gates make plans CI-shaped: guard clauses, assertions, and judgment calls live in the plan instead of in shell scripting around it.
{passed: true, …} and the plan continues.
Logical gates: when
A single comparison — value (usually a template; typed splice keeps numbers numeric) against to:
Need OR? Use two exit steps. Need more? Use an inferred gate, or compute in a user tool step first. And when the answer to a gate should be running different steps rather than ending the plan, use a
decide step — same gate grammar (spelled if), fork instead of stop; to keep only some items of a list, use a filter step — same grammar again (spelled where), evaluated per item; to run steps per item of a list, use map/reduce.
Inferred gates: infer
A yes/no question judged by the judge model role (falls back to default — point it at a cheap model):
{verdict, reason}; the gate fires only on a yes, the model’s reason is appended to the message and carried in the envelope, and a no leaves {passed: true, verdict: false, reason} as the step result for downstream reference. One cheap inference, only when the gate is reached.
Add model: to override the model for this verdict — a [models.named] entry, a role name, or default. It defaults to the judge role. Use it to pin a specific gate to a cheaper or stronger model without changing the global judge role:
model: is ignored without infer.
For reusable or complex judgments, the composition form keeps reasoning as first-class data: a prompt tool step returning a verdict, followed by a logical exit when {{Ex.verdict}} eq true.
In the workbench, a fired gate is visible at a glance — everything it pre-empted shows ⊘ skipped:
A fired success exit: the gate held, so the diff step and the solver never ran — and the pane says so.
Exit semantics
plan_and_execute’s planner also has the exit tool, with a standing instruction to exit gracefully rather than fabricate results when data comes up empty.
Omitting both when and infer makes the exit unconditional — useful as the terminal step after a judgment chain.