Transitions & Branching
Task chains are state machines. When a task finishes running its handler, the chain evaluates its transition rules to determine which task to execute next.
"transition": {
"branches": [
{ "operator": "equals", "when": "tool-call", "goto": "run_tools" },
{ "operator": "default", "when": "", "goto": "end" }
]
}
How transitions work
- The current task returns a result string (the "eval").
- The engine checks the
transition.branchesarray from top to bottom. - It evaluates the
whencondition against the eval string using theoperator. - The first branch that evaluates to
truedetermines the next step (goto).
If the branch specifies "goto": "end", the chain terminates successfully.
on_failure
A task ID to jump to when the current task raises an error — evaluated before any branch conditions. If on_failure is absent and the task errors, the chain terminates.
"transition": {
"on_failure": "error_handler",
"branches": [
{ "operator": "default", "when": "", "goto": "next_step" }
]
}
Operators
| Operator | How it matches | Example |
|---|---|---|
equals | Exact string match | "when": "tool-call" matches "tool-call" |
contains | Substring match | "when": "fail" matches "api_failure" |
starts_with | Prefix match | "when": "err" matches "error_timeout" |
ends_with | Suffix match | "when": "_ok" matches "write_ok" |
> | Numeric greater-than | "when": "3" matches "5" |
< | Numeric less-than | "when": "10" matches "7" |
in_range | Inclusive numeric range (min,max) | "when": "200,299" matches "201" |
default | Always matches | Used as the fallback at the end of the array |
What do tasks return?
Each handler returns a different eval string that you can branch on:
chat_completion: Returns"stop","tool-call", or"length".execute_tool_calls: Returns"ok"or"error".hook: Usually returns"ok"or"failed".prompt_to_string: Returns the rendered template string.prompt_to_int: Returns the decimal string of the parsed integer (e.g."42").noop: Passes the input through; eval string mirrors the input value.raise_error: Terminates the chain with an error — no branch is evaluated.
Branch Composition
When a branch is taken, you can merge or store the task's output into a chain variable using the compose object.
{
"operator": "default",
"when": "",
"goto": "next_step",
"compose": {
"with_var": "summary",
"strategy": "append_string_to_chat_history"
}
}
| Field | Description |
|---|---|
with_var | Name of the chain variable to write to |
strategy | How to combine the task output with the existing variable (see below) |
Compose strategies:
| Strategy | Behaviour |
|---|---|
override | Replace the target variable with the task output. If both are maps, keys are merged (task output wins on conflict). |
merge_chat_histories | Concatenate two chat histories — task output first, then the existing variable. |
append_string_to_chat_history | Append the task's string output as an assistant message to an existing chat history variable. |
If strategy is absent, the engine auto-selects: merge_chat_histories when both values are chat histories, override otherwise. Later tasks can reference the variable with {{.summary}} in their templates.