> For the index of every page in Orbital's docs, read https://docs.runorbital.dev/llms.txt.

# Add branches

> Send a run to different steps with routes that test what it has learned

Send a run to different steps depending on what it has just learned. Give the step a list of `routes`, each with a condition and a step to go to, and the run follows the first one whose condition holds.

```toml title="conditional.toml"
[steps.classify]
prompt = "If the goal asks for a greeting, choose greet. Otherwise choose explain."
outputs.route = { kind = "choice", options = ["greet", "explain"] }
routes = [
  { if = "route == 'greet'",   to = "steps.greet" },
  { if = "route == 'explain'", to = "steps.explain" },
]
```

## How routes choose the next step

Routing lives on the step that decides, not in a separate decision step. `routes` is an ordered list of tables. Each route has an `if`, the condition, and a `to`, the step to go to. `next` is the fallback the run takes when no route holds. A route without `if` is refused, because that is what `next` is for.

When a step succeeds, Orbital tries its routes in the order you wrote them and takes the first whose condition holds. Only then does it take `next`. Put the most specific route first.

A condition compares one context value with a literal, such as `verdict == 'pass'`, `verdict != 'pass'` or `attempts == 3`. It can also test a value on its own, such as `git.worktree.clean`, or its negation, `!git.worktree.clean`. The `context.` prefix is optional, and an input reads by its name, such as `mode == 'quick'`. You cannot join conditions with `and` or `or` or compare two context values, so every route stays readable in the file. [Conditions](/reference/workflow-file/#conditions) lists every form a condition takes.

![The workflow editor canvas shows arrows leaving an agent step, each labelled with its condition, such as verdict == 'reject', and a dashed arrow labelled else.](/screenshots/workflow-editor.webp?v=85a9c6275a)

*Each route shows its condition; the dashed else arrow is the fallback.*

## Choose what to branch on

Decide first what produces the value you branch on. The answer changes the cost and the reliability of the branch.

- Use a [probe step](/reference/step-kinds/#probe) when Orbital already observes the fact. The [ticket workflow](/examples/workflows/ticket/) routes on `github.pr.state` without asking an agent whether a merge happened.
- For another exact observation, use a [command step](/reference/step-kinds/#command) with declared `bool`, `number` or `text` facts, such as `facts.tests.pass = "bool"`. Its last line of output must be the JSON object exactly as declared. Keep the script read-only when it only inspects something.
- For judgement, give an agent a `choice` output. Orbital refuses any answer outside its options before it reaches context. So `outputs.verdict = { kind = "choice", options = ["pass", "reject"] }` accepts nothing else.

A probe or a script gives an exact answer at no agent cost. The example below branches on judgement, so an agent makes the choice.

A choice can describe each option, which helps the agent pick the right one. Give `options` as a table of values and descriptions in the output's own table; the options keep the order you write them in:

```toml
[steps.pick.outputs.next]
kind = "choice"
options.ticket = "A ticket is ready to ship"
options.none = "Every ticket is shipped"
options.blocked = "The remaining tickets wait on people"
```

A choice without `options` offers the values its routes test.

To let a small decision model make the choice instead, with a default route when it cannot, see [Decide with a model](/workflows/decide-with-a-model/).

## Cover every case

A branching step needs `next`, unless its routes answer every option of the choice output they test. A run where nothing matches stops with an error, so validation asks for `next` before the run starts.

:::note
`next` handles successful results outside your named cases. It never handles a failed step. A failed step takes the first route whose condition holds, usually one that tests `outcome == 'failed'`, and stops for you when none does. [Add a loop](/workflows/add-a-loop/) and [Error handling](/reference/error-handling/) explain.
:::

## Before you start

You need a harness you have signed in to, and a project in Orbital. Any project works. It does not need to be a Git repository, unless you run the example with Codex, which needs one. See [Projects and folders](/projects/).

## 1. Download the example

[Download the example](/conditional.zip) and unpack it into `~/.orbital/workflows/`. Keep every relative path.

![classify selects greet or explain; both reach done.](/conditional.svg?v=56bd802163)

```toml title="conditional.toml"
version = 3
entry = "steps.classify"

[steps.classify]
prompt = "If the goal asks for a greeting, choose greet. Otherwise choose explain."
outputs.route = { kind = "choice", options = ["greet", "explain"] }
routes = [
  { if = "route == 'greet'",   to = "steps.greet" },
  { if = "route == 'explain'", to = "steps.explain" },
]

[steps.greet]
prompt = "Write a friendly greeting."
next = "steps.done"

[steps.explain]
prompt = "Explain the goal in one sentence."
next = "steps.done"

[steps.done]
kind = "terminal"
```

`classify` declares `route` as a choice of `greet` and `explain`, and Orbital refuses any other answer before it reaches context. Its routes are tried in order, and between them they answer both options, so this step needs no `next`. `greet` and `explain` each name `done` as their `next`.

[Download conditional.toml](/examples/conditional/conditional.toml)

## 2. Validate it

```sh
orbital validate ~/.orbital/workflows/conditional.toml
```

## 3. Run it

Choose **New task**, pick any project, and choose the `conditional` workflow and your harness. One project serves every example. Use a greeting as the goal to select `greet`, or any other goal to select `explain`.

Exactly one response step runs before `done`. The run's **Context** tab shows each value the run holds and the step that set it.

![The run page's Context tab lists the values held by the run, each with the step that set it on the right.](/screenshots/run-context.webp?v=3e79bd8607)

*The Context tab shows which step set each value.*

:::caution
A later step that reads a value produced on only one branch fails validation, unless it gives the value a default with `needs`. Check [Pass context between steps](/workflows/pass-context/#what-a-step-can-read) and [Error handling](/reference/error-handling/) before you join branches back together.
:::

## Related

- [Routes](/reference/workflow-file/#routes) and [conditions](/reference/workflow-file/#conditions): every form a route and a condition take, and the fallback rules.
- [Add a loop](/workflows/add-a-loop/): send rejected work back for repair, with a bound.
- [Pass context between steps](/workflows/pass-context/): the values a condition can read.
- [Error handling](/reference/error-handling/): what happens when a step fails.
