State & Variables
The state is how steps communicate. Every step writes its outputs and metrics to a shared key-value store. Other steps read from it via template variables.Basics
After a step executes, its results are available to all subsequent steps:{decompose.output} resolves to the JSON plan produced by the decompose step. No steps. prefix needed — variables use short names.
Fully-qualified keys
The state is a flatmap[string]string with fully-qualified keys:
decompose.output— the JSON planbuild/task-1/converge.output— the diff from task 1build/task-1/converge.diff— the git diffbuild/task-1/converge.agent— the agent that executed (e.g.,claude-opusif escalated)build/task-1/converge.cost— estimated cost USDbuild/task-1/converge.tokens_in— input tokensbuild/task-1/converge.status— pass/fail/fatalbuild/task-1/converge.gate.compile— boolbuild/task-1/converge.gate.review.comments— string
What each step writes
Every completed step writes these keys:| Key | Description |
|---|---|
{<step>.output} | Main output: diff (code), tasks JSON (split), bool (validate) |
{<step>.diff} | Git diff (code steps) |
{<step>.agent} | Agent that actually ran (reflects escalation) |
{<step>.session_id} | Agent session ID |
{<step>.status} | pass, fail, or fatal |
{<step>.attempt} | Final attempt number |
{<step>.duration} | Duration in milliseconds |
{<step>.cost} | Estimated cost in USD |
{<step>.turns} | Number of cognitive turns |
{<step>.tokens_in} | Input tokens consumed |
{<step>.tokens_out} | Output tokens produced |
{<step>.gate.<name>} | Bool result of a specific gate |
{<step>.gate.<name>.comments} | Validator comments |
Task variables (each block)
Inside aneach block, task-level variables are available:
| Variable | Description |
|---|---|
{task.name} | Current task name (from the split JSON) |
{task.description} | Current task description |
{task.files} | Current task blast radius (file list) |
Retry variables
On retry (attempt > 1), the failed attempt’s context is available:| Variable | Description |
|---|---|
{error} | Error output from the failed gate (truncated to 2000 chars) |
{diff} | Diff from the failed attempt (truncated to 3000 chars) |
{attempt} | Current attempt number |
{gate.<name>} | Bool result of a specific gate |
{gate.<name>.comments} | Comments from a workflow validator |
{gate.<name>.error} | Stderr from a deterministic gate |
{prev.output} | Output from the previous retry iteration |
{prev.gate.<name>.comments} | Validator comments from previous iteration |
Dynamic agent reference
{<step>.agent} returns the agent that actually executed a previous step. If the step was escalated (agent overridden in retry), the variable returns the final agent, not the declared one. This enables the idiom:
Scope resolution
Variables resolve by strict scope: step local → each scope → workflow scope. If a variable name is ambiguous (same step name in different scopes),--dry-run will reject the workflow with an error listing available paths.
There is no implicit resolution by proximity. For cross-scope references, use the fully-qualified path: {build/task-1/converge.output}.
Run-level metrics
The{run.*} variables from v0.0.3 have been removed. Run-level metrics (total cost, duration, tokens) are in the ledger and accessible via gump report. The global bounds (max_budget, max_timeout, max_tokens) replace {run.*} for control purposes.
If a prompt needs the cost of a specific step, use {<step>.cost}.
State persistence
The state is serialized to.gump/runs/<uuid>/state.json as a flat JSON dictionary. It’s reloaded on --resume and --replay.
At retry, the previous iteration’s keys are copied under the prev namespace. The original keys are overwritten by the current iteration.