Sequential, parallel, fan-out. A small set of operations that compose into work one agent can't do. Here's the real fan-out audit script, the referee that keeps it honest, and loops vs routines.
There’s a moment in the Boris Cherny interview with Claude Code’s creator where the framing clicks: you’re not running an agent anymore. You’re composing them. Sequential, parallel, fan-out. He calls it “an algebra for agents” — a small set of operations you combine to do work a single agent can’t.
That word choice matters. Algebra isn’t a pile of tricks. It’s a handful of operations that compose. Once you see agents that way, the question stops being “can Claude do this?” and becomes “what’s the shape of this problem?” Sometimes the shape is one agent, one file. Sometimes it’s forty agents sweeping a codebase in parallel while one referee decides who’s right.
Let me lay out the operations, then build the machine.

The three operations
Strip it down and there are three primitives.
Sequential (A then B). One agent’s output feeds the next. Explore the codebase, then plan, then implement. Each stage narrows the problem for the next. This is what you’re already doing when you tell Claude “first understand how auth works, then add the new provider” — you’ve just made the handoff explicit instead of hoping it happens inside one context window.
Parallel (A + B + C). Independent work at the same time. Three agents, three unrelated bugs, three branches. No agent waits on another because there’s nothing to wait for. The only thing you spend is tokens and the mental overhead of tracking three things at once.
Fan-out then summarize (N to 1). Spray N workers across a big surface — every route handler, every migration, every dependency — collect what they find, then one agent reduces the pile to a verdict. This is the operation people underuse, and it’s the one that pays off hardest on audits and sweeps.
That’s the whole algebra. Everything fancier is these three nested inside each other. A pipeline that fans out at stage two is just A → (B+B+B) → C. You don’t need new vocabulary. You need to see which shape your problem actually is.
And the honest first lesson: most problems aren’t any of these. They’re one agent, one file, done. Orchestration is a power tool. You don’t reach for the nail gun to hang one picture.

When orchestration is overkill
Before the fun stuff, the discipline.
Orchestration has a fixed cost that’s easy to forget when you’re excited about spawning a swarm. Every subagent is a fresh context that has to re-learn where things are. Every parallel branch is tokens you’re spending concurrently instead of once. Every fan-out has a barrier at the end where you sit and wait for the slowest worker before the summary can even start.
So the rule is boring and correct: don’t orchestrate a one-file change. If the whole task fits in one context and one agent can hold the thread start to finish, running it as a single agent is faster, cheaper, and easier to follow. Splitting it just adds handoff seams where information leaks.
Orchestration earns its cost when one of these is true:
- The surface is bigger than one context. You can’t audit 300 files in one window without the model forgetting file 4 by the time it hits file 200. Fan-out keeps each worker’s job small.
- The work is genuinely independent. Three unrelated bugs don’t need to share a brain. Parallel wins.
- The stages have real handoffs. Research feeds a plan feeds an implementation, and each stage wants a clean context free of the previous stage’s clutter.
If none of those hold, you’re adding coordination overhead to a problem that didn’t ask for it. The swarm is a means, not a flex.
Subagents: the Task tool and why isolation matters
The mechanism underneath all of this is the Task tool. When Claude Code spawns a subagent, that subagent gets its own context window. It does its work, and only its result comes back to the main thread — not the forty file reads and dead ends it went through to get there.
That’s the actual point, and it’s worth saying slowly: subagents are context hygiene. Your main conversation is a scarce resource. Every file the main agent reads, every grep, every wrong turn — it all sits in the window taking up room, slowly pushing you toward the compaction cliff. Delegate the messy exploration to a subagent and the main thread only sees the two-paragraph conclusion. The mess stays in a context you’ll never look at again.
Parallelism is the bonus. Because subagents run in their own contexts, the main agent can kick off several and let them run concurrently. Isolation is what enables the parallelism — separate contexts can’t step on each other.
The trap: a subagent can’t ask you a follow-up question. You brief it, it runs, it reports. So the brief has to be complete. Vague task in, vague result out, and you burned a context to learn nothing. Treat the prompt like you’re handing work to a contractor who bills by the minute and won’t call you back.
Custom agents in .claude/agents/
You don’t have to describe the same kind of worker every time. Drop a definition in .claude/agents/<name>.md and Claude Code can pick it up as a reusable agent — with its own system prompt and its own tool allowlist.
Here’s a real one. An auditor whose entire job is finding missing auth checks, and nothing else:
---
name: auth-auditor
description: Audits a single route handler for missing or weak authentication and authorization checks. Use for security sweeps across many route files.
tools: Read, Grep, Glob
---
You are a focused security auditor. You look at ONE file and answer ONE question:
does every route/handler in this file enforce authentication and authorization
before doing anything sensitive?
For each handler you find, report:
- The handler name and line number
- Whether an auth check exists before the sensitive operation
- If missing: what the exploit looks like in one sentence
Rules:
- Do not fix anything. Report only.
- Do not speculate about files you were not given.
- If a handler is intentionally public, say so and why you think that.
- End with a one-line verdict: PASS (all guarded) or FAIL (list the gaps).
Three things carry the weight here.
The frontmatter is the contract. name is how you invoke it. description is how Claude decides when to reach for it on its own — write it like a job posting, not a mood. tools is the allowlist, and this auditor only gets Read, Grep, and Glob. It literally cannot edit a file or run a shell command. That’s not paranoia, it’s design: an agent that can only read can’t “helpfully” rewrite your auth layer at 2am while you’re asleep. Narrow tools, narrow blast radius.
The body is the system prompt. Notice how hard it clamps the scope — one file, one question, report only. Boring, single-purpose agents are the ones that compose cleanly. A vague “code quality” agent gives you vague results forty times over.
Custom agents pair naturally with .claude/commands/ (slash commands you trigger by hand) and a project CLAUDE.md (the shared context every agent inherits). Definitions live in the repo, so the whole setup is version-controlled and travels with the code. Your orchestration is part of the codebase, not a snowflake config on your laptop.
Worked code: a fan-out audit that actually runs
Now the machine. This is the piece the thin version skips, so this is the piece that gets real.
The goal: sweep every route handler in a codebase for missing auth, using many headless workers in parallel, then have one summarizer agent reduce all the findings to a single report. This is the N-to-1 operation as a shell script, built on claude -p — Claude Code’s headless mode, where you pass a prompt and get output back with no interactive session.
#!/usr/bin/env bash
# fan-out-audit.sh — sweep a file list with N headless workers, then summarize.
# Usage: ./fan-out-audit.sh "src/**/*.ts"
set -euo pipefail
GLOB="${1:?usage: fan-out-audit.sh <glob>}"
MAX_JOBS="${MAX_JOBS:-6}" # concurrency cap — don't fork-bomb yourself
OUTDIR="$(mktemp -d)" # per-worker findings land here
trap 'rm -rf "$OUTDIR"' EXIT
# The question each worker answers about ONE file.
read -r -d '' WORKER_PROMPT <<'EOF' || true
You are auditing a single file for missing authentication/authorization checks
on route handlers. For each handler, report the name, line number, and whether
an auth check runs before any sensitive operation. If a gap exists, describe the
exploit in one sentence. Output plain text only. End with PASS or FAIL.
EOF
echo "Collecting files matching: $GLOB"
mapfile -t FILES < <(eval "ls $GLOB" 2>/dev/null || true)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No files matched. Bail." >&2
exit 1
fi
echo "Found ${#FILES[@]} files. Fanning out (max $MAX_JOBS at a time)."
# --- FAN-OUT: one headless worker per file, capped concurrency ---
audit_one() {
local file="$1" out="$2"
{
echo "### FILE: $file"
claude -p "$WORKER_PROMPT
FILE UNDER AUDIT: $file
--- contents ---
$(cat "$file")" \
--allowedTools "Read" 2>/dev/null || echo "WORKER ERROR on $file"
echo
} > "$out"
}
export -f audit_one
export WORKER_PROMPT
i=0
for f in "${FILES[@]}"; do
audit_one "$f" "$OUTDIR/$(printf '%04d' "$i").txt" &
i=$((i + 1))
# throttle: wait whenever we hit the concurrency cap
while [ "$(jobs -r | wc -l)" -ge "$MAX_JOBS" ]; do wait -n; done
done
wait # <-- THE BARRIER: nothing proceeds until every worker is done
# --- COLLECT: concatenate every worker's findings in file order ---
FINDINGS="$OUTDIR/_all_findings.txt"
cat "$OUTDIR"/[0-9]*.txt > "$FINDINGS"
echo "All workers done. Collected $(wc -l < "$FINDINGS") lines of findings."
# --- SUMMARIZE (N -> 1): one pass over the whole pile ---
claude -p "Below are per-file security audit findings from many workers.
Produce ONE report: (1) every FAIL with file + line + one-line exploit,
ranked most severe first; (2) a count of files audited vs files with gaps;
(3) the single highest-priority fix. Ignore PASS files except in the count.
--- findings ---
$(cat "$FINDINGS")" > audit-report.md
echo "Done. Report written to audit-report.md"
Walk it stage by stage, because every block is doing a specific job.
The config up top. MAX_JOBS is the concurrency cap, and it’s the most important knob in the file. Without it, a 300-file repo forks 300 headless Claude processes at once — you’ll hit rate limits, melt your machine, or both. Six is a sane start. The mktemp -d plus the trap means every worker writes to its own scratch file and the whole directory gets cleaned up on exit, even if the script dies. Isolated writes are what make the parallelism safe; two workers appending to one file would interleave into garbage.
The worker prompt. One tightly-scoped question, defined once, reused for every file. Same discipline as the custom agent above: single file, single question, plain-text out. Note --allowedTools "Read" — each worker is locked to reading. It can’t edit, can’t run commands. A sweep that can only look is a sweep you can run without watching it.
The fan-out loop. audit_one runs one worker in the background with &. The throttle — while [ "$(jobs -r | wc -l)" -ge "$MAX_JOBS" ]; do wait -n; done — is the whole ballgame. It counts running jobs and blocks the loop whenever you’re at the cap, releasing as each worker finishes with wait -n. That’s how you get parallelism without a fork bomb. Each worker’s output goes to a zero-padded filename so the collect step reassembles them in order.
The barrier. That bare wait is the N-to-1 join. Nothing downstream runs until every worker has finished. This is the latency tax of fan-out made concrete: your total wall-clock time is bounded by the slowest worker, not the average. One file that’s 4,000 lines of tangled middleware will hold up the summary while thirty small files sit finished and waiting. Real cost, plan for it.
Collect and summarize. Concatenate every finding, then one final claude -p reduces the pile to a ranked report. This is the summarizer, and it’s doing real work — deduping, ranking by severity, pulling the signal out of forty separate PASS/FAIL verdicts. N noisy outputs in, one report out.
Swap the worker prompt and you’ve got a different sweep for free. “Find every direct DB query that isn’t parameterized.” “Flag every TODO older than the git blame says is decent.” “List every component that imports the deprecated design system.” The skeleton — cap, fan-out, barrier, summarize — doesn’t change. Only the question does.
The load-bearing point: no verifier, no swarm
Here’s the part that separates orchestration that works from orchestration that just feels productive.
A swarm without a verifier is parallelized guessing. Forty agents confidently reporting forty things is not forty times the confidence. It’s forty times the surface area for a plausible-looking wrong answer, and now you’ve got a tidy summarizer laundering all of it into one clean report you’re inclined to trust because it looks clean.
The example that makes this concrete is the Bun team’s Zig-to-Rust rewrite — roughly 100k lines, done in about 11 days with heavy agent orchestration. That didn’t work because the agents were geniuses. It worked because there was a comprehensive test suite acting as referee. Every agent’s output hit the same wall: does it pass? The tests were ground truth. Agents could be wrong all day long, and the suite caught it, and the loop kept going until green. The orchestration was fast; the tests were why fast didn’t mean broken.
So the actual recipe isn’t “spawn more agents.” It’s: build the referee first, then let the swarm run at it. In the audit script above, the referee is weak — it’s just PASS/FAIL text from workers, and a summarizer that trusts them. That’s fine for a read-only survey where a human reviews the report. The instant your agents start writing code, the referee has to get real: a test suite, a type checker, a linter, something deterministic that says yes or no without a vibe. If you can’t point at your referee, you don’t have orchestration. You have a very expensive random number generator with good manners.
Loops vs routines
Two more operations, and they’re often confused because they both mean “run it again.”
A loop is local and repeated. Run this prompt every N minutes, or run this command until the tests pass, right here on your machine while you watch. It lives and dies with your terminal. Close the laptop, the loop’s gone. Great for “keep hammering this flaky test” or “poll the deploy every five minutes until it’s green.” Tactical, ephemeral, tied to a session.
A routine is cloud-side and persistent. It runs on a schedule, survives you closing the laptop, and — the part that matters — it can carry memory of past runs. A routine that reaps stale experiments every night knows what it reaped last night. That history is the difference between a scheduled script and something that actually maintains a system over time.
The Claude Code team runs routines like this on their own repo. The examples that get mentioned: dead-code cleanup, an experiment reaper that clears out abandoned feature flags, test lifecycle management, and an “Abstraction Police” routine that flags premature or over-engineered abstractions before they metastasize. These aren’t one-shot tasks. They’re standing maintenance jobs that keep the codebase from rotting while everyone’s busy shipping — a slow immune system running in the background.
You can wire your own without anything exotic. A routine is a scheduler plus claude -p plus a place to keep state. On your own box that’s a cron entry:
# crontab -e — nightly dead-code sweep at 2am, appends to a running log
0 2 * * * cd /path/to/repo && \
claude -p "Find exported functions and components with zero references \
across the repo. List each with its file and a one-line safe-to-delete \
verdict. Do not delete anything. Append findings to maintenance/dead-code.md \
with today's date as a heading." \
--allowedTools "Read,Grep,Glob,Edit" >> maintenance/cron.log 2>&1
The shared state is that maintenance/dead-code.md file — dated headings, committed to the repo. Each run reads what past runs found. That’s the poor-man’s routine memory: a file that persists and accumulates. Same claude -p primitive as the fan-out workers, just triggered by a clock instead of a loop, writing to something durable instead of the void. Note the still-narrow tool list — even your unattended janitor doesn’t get a shell.
The interview goes further and suggests routines get much more capable as the models do — the forward-looking Opus-5 claims about routines running deeper autonomous maintenance are, to be clear, per the interview, not something settled. Treat it as direction, not a spec sheet. But the primitive you’d build on exists right now: schedule + headless + durable state.
Cost and latency, straight
The stuff nobody puts on the marketing slide.
Parallel agents cost real tokens, in parallel. Six workers isn’t one prompt — it’s six full contexts, each re-reading its file, each paying its own input and output. Fan-out over 300 files is 300 worker calls plus a summarizer call, and the summarizer’s input is everything the workers produced. That summary prompt can be enormous. Watch it.
Fan-out has a barrier, and the barrier is your slowest worker. You don’t get the average-case latency, you get the worst case. One pathological file stalls the whole join. If your workers have wildly uneven sizes, either chunk the big ones down or accept that the tail dominates your wall clock.
Summarize to fight context bloat, not just to be tidy. The reason N-to-1 exists isn’t neatness — it’s that you physically can’t stuff 300 raw findings back into the main thread and keep working. The summarizer is the compression step. Reduce early, reduce often. If your findings are huge, summarize in tiers — batches of 20 into partial summaries, then a summary of summaries. A pyramid, not one heroic prompt trying to swallow the ocean.
The math that decides it: orchestration is worth it when the surface is too big for one context OR the parallel speedup beats the token overhead. One-file change? Single agent, every time. Three hundred files? The swarm pays for itself. The gray zone in the middle is where judgment lives, and judgment mostly means being honest about whether you actually have a referee.
Your first fan-out audit
Don’t build a distributed system today. Do this instead.
- Pick a read-only sweep. Something with a crisp yes/no question and zero risk if a worker is wrong. “Every route handler has an auth check.” “No component imports the old design system.” Read-only means you can run it unattended and the worst case is a wrong line in a report.
- Write one custom agent in
.claude/agents/, tools locked toRead, Grep, Glob. One file, one question, report only. Boring on purpose. - Grab the fan-out script above. Set
MAX_JOBS=4the first time — you want to watch it, not drown in it. Point it at your glob. - Read the summarizer’s report against your own eyes on three or four files. This is you being the referee for the referee. If the summary matches reality, your worker prompt is good. If it hallucinated a gap, tighten the prompt and rerun. Cheap, because read-only.
- Only then let it write. Once the read-only sweep is trustworthy, graduate to a version that proposes fixes — and the day it starts editing is the day a test suite stops being optional and becomes the whole point.
That’s the algebra. Sequential, parallel, fan-out, and the referee that keeps all three honest. The operations are simple. The discipline is knowing which shape your problem is — and refusing to spawn the swarm until you can name the thing that tells it when it’s wrong.
Start with one audit. Let it earn the second.