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, 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.
<!-- .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:
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.
<!-- .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": "<id>",
"output": "<your finding or change summary>",
"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. 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.Common gotchaA 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.
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 &
waitAfter fan-out, pass all result files to the synthesizer:
<!-- .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.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:
<!-- .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:
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.Common gotchaThe 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 intojq`.
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
#!/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 1The 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 guide covers which of each are worth building.
<!-- .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.
# 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.mdChecklist 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
&andwait, not sequentially. - The
/verifycommand receives the exactsuccess_criteriastring 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 and 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/.