# Workflows that Compound

A one-shot prompt solves one problem. A pipeline solves the same problem every
time, faster, and with each run making the next run cheaper. This guide is about
building those pipelines inside Claude Code: the plan-act-verify loop, passing
structured data between steps, fan-out with subagents, and deciding when to
script the glue deterministically versus let Claude drive.

You will not call the API in code here; Claude writes that code. You will chain
work in the CLI itself, through [custom commands](/guides/save-your-best-prompts), skills, and
plan mode, so the pipeline lives in your project and every teammate can run it.

## What does a compounding pipeline look like?

Every useful pipeline follows the same skeleton:

The loop from verifier back to planner is where compounding happens. A failed
run produces a richer context for the retry: the original plan, the worker
outputs, and the verifier diagnosis all feed the next attempt. Correctness rises
faster than cost.

## Step 1: extract a structured plan with plan mode

Never hand a raw user string straight to a worker step. Use
plan mode
to turn the request into a structured spec your later steps can rely on.

Press **Shift+Tab** before typing your prompt. Claude will lay out the goal,
subtasks, and success criteria as a numbered list and wait for your go-ahead.
You review, edit, or approve, and only then does work begin.

For a pipeline you run repeatedly, encode that planning step as a custom slash
command so no one has to remember the incantation.

```markdown
<!-- .claude/commands/plan.md -->
You are the planner for this pipeline. Given the user's request, produce a
structured work plan with these fields and nothing else:

- **goal**: one sentence describing the end state
- **subtasks**: numbered list, each with an id, instruction, and any
  dependencies noted in parentheses
- **success_criteria**: one sentence the verifier will use to judge the output

Do not begin any work. Emit the plan, then stop and wait for approval.
```

Run it with:

```bash
claude /plan "audit every component for missing aria labels"
```

Claude returns the plan. You approve it (or edit a subtask inline), then move
to the next command.

> Tip: if your planner tends to over-split, add a constraint to the command
> file: "Produce at most five subtasks. Merge anything that can share a single
> context window."

## Step 2: worker commands that emit typed results

Each worker receives one subtask and emits a structured result. Write a command
file per worker type, or a single parameterised worker command.

```markdown
<!-- .claude/commands/worker.md -->
You are a focused worker completing exactly one subtask. Do not speculate
beyond the task scope.

You will receive:
- **subtask_id** in $ARGUMENTS (first token)
- **instruction** in $ARGUMENTS (remainder)

When finished, emit a fenced JSON block with this shape and nothing else:

```json
{
  "subtask_id": "",
  "output": "",
  "confidence": 0.0
}
```

Confidence is a number from 0 to 1 reflecting how certain you are the output
is complete and correct.
```

Run a worker for one subtask:

```bash
claude /worker "aria-scan Scan src/components/ for missing aria-label props"
```

Claude reads only the files relevant to that subtask, emits the JSON block, and
stops. You collect these blocks for the synthesizer.

| Field | Purpose |
|-------|---------|
| `subtask_id` | Ties the result back to the plan |
| `output` | The finding, change, or artifact |
| `confidence` | Lets the synthesizer weigh contradictions |

## Step 3: fan-out then synthesize

Fan-out means [running independent workers at the same time](/guides/run-agents-in-parallel). In Claude Code you
can open multiple terminal panes and fire each worker command in parallel, or
use `claude --dangerously-skip-permissions` in CI with `&` to background jobs.A backgrounded worker that crashes or hits a rate limit does not stop the run: `wait` ignores the failed job's exit code even under `set -e`, so the synthesizer proceeds on a half-empty result file. Capture each job's PID, `wait` on it individually, and check `$?` per worker before merging.

```bash
claude /worker "aria-scan Scan src/components/ for missing aria-label props" \
  > results/aria-scan.json &

claude /worker "focus-order Verify keyboard tab order in forms" \
  > results/focus-order.json &

claude /worker "color-contrast Check contrast ratios in tailwind classes" \
  > results/color-contrast.json &

wait
```

After fan-out, pass all result files to the synthesizer:

```markdown
<!-- .claude/commands/synthesize.md -->
You are the synthesizer. You receive multiple worker result JSON blocks.
Collapse them into one coherent summary:

1. List every finding, grouped by subtask.
2. Where two workers contradict each other, cite their confidence scores and
   resolve in favour of the higher-confidence result.
3. Produce a final recommendations list ordered by severity.
4. End with a one-line verdict: PASS or FAIL and why.

The plan's success_criteria is provided in $ARGUMENTS.
```

```bash
claude /synthesize "zero missing aria labels and all contrast ratios >= 4.5" \
  < <(cat results/*.json)
```

## Step 4: verify against the plan

The verifier checks the synthesis against the original `success_criteria` and
returns a structured verdict. Write it as a command:

```markdown
<!-- .claude/commands/verify.md -->
You are the verifier. You receive the plan's success_criteria in $ARGUMENTS
and the synthesized output on stdin.

Emit only a fenced JSON block:

```json
{
  "passed": true,
  "diagnosis": "All criteria met." 
}
```

If passed is false, diagnosis must name the specific criterion that failed and
what evidence in the synthesis led to that conclusion.
```

```bash
claude /verify "zero missing aria labels and all contrast ratios >= 4.5" \
  < synthesis.md
```

If `passed` is `false`, append the `diagnosis` to your next `/plan` call:

```bash
claude /plan "audit every component for missing aria labels -- previous attempt
failed: color-contrast worker reported 3 components untested"
```

Cap retries at two or three in your shell script. A pipeline that retries
indefinitely has a bad planner prompt, not a transient error.

## How do I pass structured output between steps?

The contract between steps is what makes the pipeline reliable. Each command
produces a known shape; the next command expects that shape.

| Step | Receives | Emits |
|------|----------|-------|
| `/plan` | Raw user string | Plan (goal, subtasks, success_criteria) |
| `/worker` | One subtask id + instruction | Result JSON (subtask_id, output, confidence) |
| `/synthesize` | Plan criteria + all Result JSONs | Prose summary + PASS/FAIL line |
| `/verify` | Criteria + synthesis | Verdict JSON (passed, diagnosis) |

Because each hand-off is text you can inspect in the terminal, debugging is
straightforward. If something looks wrong, open the intermediate file and read
it. No debugger required.The trap is assuming Claude emits clean JSON when a downstream `jq` reads it. Claude often wraps the block in a ```json fence or adds a sentence before it, so `jq -r '.passed'` parses nothing and your loop treats a pass as a fail. Strip the fences first (pipe through `sed '/^```/d'` or extract the braces with a tiny script) before piping any worker or verifier output into `jq`.

## When should I script a step versus let Claude drive it?

This is the most consequential design decision in pipeline work. Get it wrong
and you end up with either a brittle hand-coded state machine or Claude burning
effort on decisions that need no judgment.

| Situation | Use |
|-----------|-----|
| Fixed number of steps with known shape | Shell script |
| Subtask count and type vary by input | `/plan` command |
| Branching on a boolean flag | `if` in shell |
| Deciding which of N strategies fits | Claude-driven planning |
| Retrying after a known error class | `for` loop in shell |
| Diagnosing an unexpected failure | `/verify` command |
| Parsing a well-defined JSON response | `jq` in shell |
| Extracting fields from free-form text | Claude-driven extraction |

The practical rule: script anything you can enumerate. Let Claude handle
anything that requires reading content to decide. A mixed pipeline is not a
compromise, it is the goal. Shell steps are fast, free, and predictable. Claude
steps are where judgment lives.

### Example: deterministic retry wrapper

```bash
#!/usr/bin/env bash
set -euo pipefail

CRITERIA="zero missing aria labels and all contrast ratios >= 4.5"
MAX_RETRIES=3
DIAGNOSIS=""

for attempt in $(seq 1 $MAX_RETRIES); do
  PROMPT="audit every component for missing aria labels"
  if [[ -n "$DIAGNOSIS" ]]; then
    PROMPT="$PROMPT -- previous attempt failed: $DIAGNOSIS"
  fi

  claude /plan "$PROMPT" > plan.md

  # fan-out workers (adjust ids from plan.md as needed)
  claude /worker "aria-scan Scan src/components/" > results/aria-scan.json &
  claude /worker "focus-order Verify keyboard tab order" > results/focus-order.json &
  claude /worker "color-contrast Check contrast ratios" > results/color-contrast.json &
  wait

  claude /synthesize "$CRITERIA" < <(cat results/*.json) > synthesis.md
  VERDICT=$(claude /verify "$CRITERIA" < synthesis.md)

  PASSED=$(echo "$VERDICT" | jq -r '.passed')
  DIAGNOSIS=$(echo "$VERDICT" | jq -r '.diagnosis')

  if [[ "$PASSED" == "true" ]]; then
    echo "Pipeline passed on attempt $attempt."
    cat synthesis.md
    exit 0
  fi

  echo "Attempt $attempt failed: $DIAGNOSIS"
done

echo "Pipeline failed after $MAX_RETRIES attempts: $DIAGNOSIS" >&2
exit 1
```

The retry logic is deterministic shell. Only the diagnosis that drives the next
planning call is Claude-generated.

## When should I use subagents?

Everything above assumes workers are focused, single-step Claude invocations.
When a worker needs to execute code, run tests, browse the web, or operate the
file system across many files, you reach for
subagents.

In Claude Code, subagents are ordinary `claude` processes with their own context.
The orchestrator never sees the raw execution noise, only the typed result the
subagent emits at the end.

Reach for subagents when:

- A worker needs more than one tool call to finish its task.
- You want the worker's context capped separately from the orchestrator's.
- You need the worker's execution to be auditable independently (each subagent
  session has its own transcript).

## What are skills in a pipeline?

A
skill
is a markdown file that encodes a repeatable capability. Where a command runs a
specific task, a skill teaches Claude how to perform a class of tasks. The
[commands and skills](/guides/claude-code-commands-and-skills) guide covers which of each are worth building.

```markdown
<!-- .claude/skills/structured-audit.md -->
# Structured audit

When asked to audit code, always:
1. Scope the audit to the directory or file pattern named in the request.
2. Emit findings as a JSON array: [{ "file", "line", "severity", "description" }].
3. After the array, write a one-line summary: N findings, highest severity X.
4. Do not fix anything unless explicitly told to fix.
```

Load the skill in your command file with `@skills/structured-audit` at the top,
or reference it in your CLAUDE.md so it is always active.

Skills compose. A pipeline can load an audit skill for the worker stage and a
synthesis skill for the collapse stage, keeping each command file short and
focused.

## Effort and cost checkpoints

Before running a full fan-out pipeline, use `/cost` to see the current session
spend and `/effort` (or the effort slider in the UI) to set how deep Claude
searches within each command.

| Stage | Recommended effort |
|-------|--------------------|
| Planning | High: a good plan saves retries |
| Workers (well-scoped subtasks) | Low to medium |
| Synthesis | Medium |
| Verification | Low: the criteria are explicit |

Higher effort means more tool calls and a larger context scan per step. On a
ten-subtask pipeline, the difference between low and high effort on workers can
be 3x the token spend with marginal accuracy gain. Tune workers first.

## Prompt templates as pipeline currency

Prompt templates
are what let a pipeline scale. Keep them as command files in `.claude/commands/`
rather than composing strings inline. Versioning templates separately from shell
glue means you can tune behaviour through prompt iteration without touching the
orchestration logic, and you can A/B test a planner command against a production
baseline by swapping one file.

```bash
# test a new planner against the current one
cp .claude/commands/plan.md .claude/commands/plan-v2.md
# edit plan-v2.md, then compare outputs
claude /plan-v2 "audit every component" > plan-v2.md
diff plan.md plan-v2.md
```

Set up a three-stage pipeline in .claude/commands/ for a code-quality audit: a /plan command that scopes the audit, a /worker command that scans one directory and emits findings as JSON, and a /verify command that checks the findings list against a severity threshold. Show the shell script that fans out workers and retries once on failure.

## Checklist before shipping a pipeline

- Every Claude-to-Claude hand-off passes a typed JSON block, not raw prose.
- Worker commands are scoped: each touches one directory or one concern.
- Fan-out runs concurrently with `&` and `wait`, not sequentially.
- The `/verify` command receives the exact `success_criteria` string from the plan.
- Retry count is capped and the cap is enforced in shell, not by Claude.
- Effort is tuned per stage: high for planning, low-to-medium for well-scoped workers.
- Each command file is under 60 lines. Long commands are a sign the stage is doing too much.

The guides on [Multi-Agent Architectures](/guides/multi-agent-architectures) and [Claude Code Workflows](/guides/claude-code-workflows) cover the
broader ecosystem these pipelines live in. The pattern here is the building
block: one typed spec, parallel workers, one synthesis, one verdict, and a
clean retry path, all wired together in plain shell and `.claude/commands/`.
