Skip to main content
A decide step forks the plan: it evaluates a gate — the same gate grammar as an exit gate, spelled if/infer — and runs the then branch when it holds, the else branch otherwise. Execution then rejoins the plan’s normal step sequence. Where exit answers “should this plan stop?”, decide answers “which action is correct next?” — update or create, escalate or summarize, notify or stay quiet.
In the workbench, a decide step renders as a junction — the chosen branch’s steps run under its colored guide, the other stays untouched:
A decide fork after a run down its then branch: the inline step list nests under the blue then head; the single-call else was never rendered.

A decide fork after a run down its then branch: the inline step list nests under the blue then head; the single-call else was never rendered.

Gates

Exactly one of if or infer is required — unlike exit, where omitting both means “unconditionally”. The gate grammar is identical to exit gatesdecide spells the logical gate if where exit spells it when, because if … then … else reads as the fork it is. if is a logical comparison (eq ne gt lt gte lte empty not_empty contains, with typed splice keeping numbers numeric); infer is a yes/no question judged by the judge model role. An if gate costs zero inference; an infer gate costs one cheap judgment call. An inferred fork routes on judgment where no single comparison would do — here, whether a status update reads as blocked:
then runs only on a yes, and the model’s reasoning is carried in the step result — {{E2.reason}} — for downstream steps and the finish to use. The verdict mechanics — the forced {verdict, reason} schema, the judge role, and the per-gate model: override (which on decide may itself be a template) — are exactly those of inferred exit gates.

Branches

then is required; else is optional. Each branch is either:
  • A single tool call{tool_name, input}, no id. Any catalog tool works, including plan__* and plan_and_execute.
  • An inline step list — normal steps with ids. They run in authored order, and each may reference plan input, any earlier top-level step, and earlier steps in the same branch. Branch ids must not reuse top-level ids, and they are invisible outside the branch.
Branches may contain exit steps — a fired exit ends the whole plan from inside the branch (its step in the outcome carries the body path, e.g. E4/then/bail), and a passed gate lets the branch continue to its next step. Branches may also contain agent, ask, and filter steps, with the branch scope reaching the prompt, question, or gate. Branches must not contain decide, map, or reduce. For nested control flow, put it in a plan and call plan__* from the branch — cycle detection and the depth cap apply as usual, and an error-exit inside that sub-plan surfaces as a failure of the decide step.

The step result

The decide step’s result — stored under its own id — is the only thing later steps see:
  • branch — which side ran (null when the gate was false and there was no else).
  • verdict — the gate outcome; reason — the judge’s explanation, for infer gates.
  • result — the branch’s output: the single call’s result, or the last branch step’s result.
Downstream references go through the decide id ({{E1.result.…}}, {{E1.branch}}), so the rejoined plan looks the same regardless of which branch ran. With no else and a false gate, result is null and the plan simply continues — like a passed exit gate.

Only the chosen branch is rendered

The executor renders the gate first, evaluates it, and only then renders the chosen branch — per step, for inline lists. The non-taken branch’s templates are never evaluated. That’s what makes this pattern safe:
If both branches rendered eagerly, {{E0.issues.0.id}} would raise EmptyData on an empty list — the exact case else handles. Deferred rendering means each branch may assume it is the right branch. EmptyData raised inside the chosen branch still degrades normally (errors).

Failure and replanning

A failing branch call fails the decide step — attributed as step E1 (decide) with the branch and inner tool named in the message. Human-authored plans fail hard as always; planner-authored plans replan with that context.
Branch step results are scoped to the branch, so a replan past a decide failure re-runs the branch from its first step. Keep branch steps idempotent — see the idempotency caveat.
Branch steps count in steps_executed — a decide with a two-step branch reports three executed steps (the decide plus two).

Semantics summary

plan_and_execute’s planner also has the decide tool, so LLM-authored plans can fork on their own intermediate results.