> ## Documentation Index
> Fetch the complete documentation index at: https://graph.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Branching

> Fork a plan into one of two branches with a decide step

A `decide` step forks the plan: it evaluates a gate — the same gate grammar as an [exit gate](/plans/exit-gates), 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.

```yaml theme={null}
steps:
  - id: E0
    tool_name: linear__list_issues
    input: { priority: 1, limit: 50 }

  - id: E1
    tool_name: decide
    input:
      if: { value: "{{E0.issues.length}}", op: gt, to: 5 }
      then:                                  # single tool call — any tool, incl. plan__*
        tool_name: plan__escalate_triage
        input: { issues: "{{E0.issues}}" }
      else:                                  # inline step list, run in order
        - id: E10
          tool_name: user__summarize
          input: { text: "{{E0.issues}}" }
        - id: E11
          tool_name: user__notify
          input: { message: "{{E10.summary}}" }   # same-branch dataflow

  - id: E2                                   # rejoin — same shape either way
    tool_name: user__log
    input: { entry: "took {{E1.branch}}: {{E1.result}}" }
```

In the [workbench](/workbench/plan-workbench), a decide step renders as a `⑂` junction — the chosen branch's steps run under its colored guide, the other stays untouched:

<Frame caption="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.">
  <img src="https://mintcdn.com/graph/VaTetSEpy4ieswMa/images/workbench/branching-decide.svg?fit=max&auto=format&n=VaTetSEpy4ieswMa&q=85&s=d5784988ed2a61684bb8bbabb30f807d" alt="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." width="624" height="270" data-path="images/workbench/branching-decide.svg" />
</Frame>

## Gates

Exactly one of `if` or `infer` is required — unlike `exit`, where omitting both means "unconditionally". The gate grammar is identical to [exit gates](/plans/exit-gates) — `decide` 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](/plans/template-language#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:

```yaml theme={null}
  - id: E2
    tool_name: decide
    input:
      infer: |
        Does this status update indicate the project is blocked?

        {{E1.statusUpdates.0.body}}
      then:
        tool_name: plan__escalate_blocker
        input: { project: "{{input.project}}", update: "{{E1.statusUpdates.0}}" }
      else:
        tool_name: user__log
        input: { entry: "on track" }
```

`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](/plans/exit-gates#inferred-gates-infer).

## 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`](/plans/exit-gates) 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`](/plans/agent-step), [`ask`](/plans/ask-step), and [`filter`](/plans/selection) steps, with the branch scope reaching the prompt, question, or gate. Branches must not contain `decide`, [`map`, or `reduce`](/plans/iteration). 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:

```json theme={null}
{ "branch": "then", "verdict": true, "reason": null, "result": { … } }
```

* `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:

```yaml theme={null}
input:
  if: { value: "{{E0.issues.length}}", op: gt, to: 0 }
  then:
    tool_name: linear__update_issue
    input: { id: "{{E0.issues.0.id}}" }     # only exists when then is taken
  else:
    tool_name: linear__create_issue
    input: { title: "{{input.title}}" }
```

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](/plans/errors-and-replanning)).

## 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.

<Warning>
  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](/plans/errors-and-replanning#plan_and_execute-replans).
</Warning>

Branch steps count in `steps_executed` — a decide with a two-step branch reports three executed steps (the decide plus two).

## Semantics summary

| Surface                      | Behavior                                                                       |
| ---------------------------- | ------------------------------------------------------------------------------ |
| gate false, no `else`        | result `{branch: null, verdict: false, result: null}`, plan continues          |
| trace events                 | one `decide` tool event for the gate, then normal tool events for branch calls |
| `steps_executed` / envelopes | decide counts as 1, plus 1 per branch call or branch step run                  |
| branch tool fails            | decide step fails: hard error (your plans) or replan (`plan_and_execute`)      |
| cost                         | `if`: 0 inference; `infer`: 1 judge call — plus whatever the branch calls      |

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


## Related topics

- [Template language](/plans/template-language.md)
- [Scripting contract](/reference/scripting-contract.md)
- [CI checks](/cookbook/ci-checks.md)
