Skip to main content

The State Bag

The state bag 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:
steps:
  - name: decompose
    agent: claude-opus
    output: plan
    prompt: "Decompose {spec} into items."

  - name: review
    agent: claude-opus
    output: review
    prompt: |
      Review the plan:
      {steps.decompose.output}
{steps.decompose.output} resolves to the JSON plan produced by the decompose step. The review step sees the full plan as context.

What each step writes

Every completed step writes these keys:
KeyDescription
{steps.<n>.output}The step’s main output (patch for diff, JSON for plan, text for artifact, JSON for review)
{steps.<n>.files}Files changed (for diff steps)
{steps.<n>.status}pass, fail, or fatal
{steps.<n>.duration}Duration in milliseconds
{steps.<n>.cost}Estimated cost in USD
{steps.<n>.turns}Number of cognitive turns
{steps.<n>.retries}Number of retries executed
{steps.<n>.tokens_in}Input tokens
{steps.<n>.tokens_out}Output tokens
{steps.<n>.cache_read}Cache read tokens
{steps.<n>.cache_write}Cache write tokens
{steps.<n>.session_id}Agent session ID
{steps.<n>.check_result}Gate result: pass, fail, none

Run-level variables

Aggregate metrics for the entire run:
KeyDescription
{run.cost}Total cost so far
{run.duration}Elapsed time
{run.tokens_in}Total input tokens
{run.tokens_out}Total output tokens
{run.retries}Total retries
{run.status}running, pass, fatal, aborted

Foreach variables

Inside a foreach block, item-level variables are available:
KeyDescription
{item.name}Current item name
{item.description}Current item description
{item.files}Current item blast radius

Retry variables

On retry, the failed attempt’s context is injected:
KeyDescription
{error}Error output from the failed gate (truncated to 2000 chars)
{diff}Diff from the failed attempt (truncated to 3000 chars)

Resolution by proximity

When you write {steps.impl.output}, Gump resolves it by searching the closest scope first — same group, then parent, then root. This means you can use short names when there’s no ambiguity:
- name: build
  foreach: decompose
  steps:
    - name: impl
      agent: claude-sonnet
      prompt: "Implement: {item.description}"

    - name: review
      agent: claude-opus
      output: review
      prompt: |
        Review: {steps.impl.output}
Inside the build group, {steps.impl.output} refers to the impl step in the same group. If the name is ambiguous (same step name in different groups), --dry-run will reject the workflow.

Hierarchical state bag

When a workflow: step completes, its entire state bag is grafted into the parent under the step’s namespace:
- name: validate
  workflow: security-check
  with:
    diff: "{steps.impl.output}"
After execution, the parent can access {steps.validate.steps.review.output} — the review step’s output from inside the security-check sub-workflow. There’s no explicit “return” — the entire state bag is the interface.

Persistence

The state bag is saved to .gump/runs/<uuid>/state-bag.json at the end of the run. It’s reloaded on --resume and --replay.