Fanning out independent code review agents, at half the cost

Eleven reviewers over one diff, adversarial in both directions. Why fanning them out multiplied the reading instead of dividing it, and the change that halved what a review costs.

Every change I make to Kangentic goes through a board column called Code Review before it reaches Testing. Dragging a card there starts a Claude Code session, it runs /code-review against the branch, and that one command fans out to as many as eleven reviewers on the same diff.

The reviewers are all Sonnet at medium effort. The driver that spawns them and checks what they send back is Opus, so the expensive model does the judging and the cheaper ones do the reading. What made the review good was not the model. It was three changes to how the review is structured, and the last one cut what a review costs roughly in half.

Where it started

One agent, one pass, one prompt: read the diff and report problems. It ran in whatever session was already open, which usually meant the session that had just written the code. The skill is .claude/skills/code-review/SKILL.md, and every revision of it is public if you want to watch this happen in order.

That is the worst possible reviewer. It remembers deciding to do the thing it is now judging, so it grades its own intent instead of the diff. On a large branch it also went shallow, because one agent reading seventy files in a single pass gives the middle of the diff about a paragraph of attention.

Where it went

Two changes fixed that. Neither is surprising, but the first took two tries.

First, the reviewer got its own session. My first attempt was context: fork, which runs the skill in a subagent with no generation history. That does buy the independence, and I took it out two days later anyway. A fork runs inside a subagent, and only a subagent’s summary comes back to the main conversation. Put a fan-out under one and the finders vanish into it: you get a verdict when it finishes, not the review happening.

What replaced it is a board feature rather than a skill setting. The Code Review column runs on an isolated session, keyed to that column, so it never inherits the main conversation and spawns fresh on entry. It is the same blank slate the fork gave me, except that it is a real session rather than a subagent. I can open it and watch the finders come back and the driver throw out the ones it cannot confirm. Leaving the column resumes the main conversation, so the detour costs the task nothing.

Two parallel tracks. Along the top, the main session runs Planning, Executing, Testing, Merge and Done, with the stretch between Executing and Testing drawn as a faint dashed line marked suspended. Below it a second amber track carries a Code Review box labelled isolated session, which fans out to up to eleven reviewers inside it, and connectors branch down from Executing and rejoin the main track at Testing.

The skill says what the independence buys:

The reviewing agent therefore did not write the code under review and has no memory of intending anything by it.

“The author clearly meant X” stops being available as an argument, because there is no author in the room. The diff has to defend itself on what it does, not on what it was meant to do.

Second, the single pass became a fan-out. A run spawns four dimension finders, correctness, performance, maintainability, and a fused best-practices-and-conventions pass, plus a signatures-only integration pass and a red-green coverage pass. Up to five more auditors fire only when changed files match their globs. Eleven is the ceiling.

Splitting only pays if each finder gets criteria it can fail. Every correctness finding has to arrive with a falsifiable triple: the input that triggers the failure, the code path it takes, and why existing tests miss it. One that cannot be stated that way does not get raised.

Then the driver attacks the ones that are. It reads the cited lines itself, and anything it cannot confirm gets refuted rather than passed along. On one recent review it kept six findings and threw out five, after checking 2,682 cited line numbers with none wrong. So the review is adversarial in both directions, against the diff and against its own findings, which is the part that makes eleven cheap reviewers worth listening to.

The part I got wrong

A fan-out looks like it is dividing the work. On one diff it multiplies it.

A two column comparison of a code review fan-out. Before the pack, ten finders each gather for themselves: six re-derive the diff with git, all ten open changed files directly, four files are read between four and eight times each, 18 changed files are never opened at all, and 38.4% of every byte read back is a duplicate. After the pack, one script runs in a single call before any finder spawns and writes one line-numbered section per changed file to a single path, which the finders then read with sequential offset reads instead of running git. Each of them still reads the whole thing, so the bytes do not shrink. What falls is the round trips, from 15 to 30 per finder down to one.

Each of those agents went and read the diff and the changed files for itself, and nothing anywhere tells you it is happening. So I took one real review apart, turn by turn, from its transcripts. 38.4% of every byte returned by Read across the fan-out duplicated a file another finder had already read. One file was opened by seven of the ten finders. Eighteen changed files were opened by nobody. Six finders re-derived the diff through git, because I had handed them gather commands instead. That run cost $67.87 and left each finder holding between 139k and 334k tokens of context. The full decomposition is docs/code-review-fanout-audit.md in the app repo.

Prompt caching does not rescue this. A cache hit needs a completely identical prefix, and each finder’s prompt diverges at its own criteria before any file it reads, so sibling subagents can never share each other’s tool results. Ten finders reading the same file pay for it ten times no matter how you schedule them.

The vendor docs answer a different question. They frame subagents around keeping the main context clean and running independent lanes in parallel, and Claude Code’s own example is researching the authentication, database, and API modules at once. Code review is the opposite shape. It is N reviewers over one diff, not N reviewers over N modules.

Anthropic’s write-up of their multi-agent research system names this exact failure, “subagents misinterpreted the task or performed the exact same searches as other agents”, and answers it with prompt engineering: better task descriptions, a clearer division of labor. The structural answer is cheaper. Gather the shared context once, write it to one file, and hand every finder that same file.

Spotify’s engineering team reached the same diagnosis from another direction. Most of what a coding agent does is not thinking, it is I/O, so their fix routes the reading to a cheaper model, Gemini 2.5 Flash, behind a hook that intercepts large file reads and hands back a summary. They measured about 90% off the bulk-read portion. That works anywhere, including when nobody knows what the agent will need to open next. A code review is the easier case, because the diff already names every file that matters. So the gather does not need a model at all. A script does it once, and the finders spend their tokens on judgment instead of retrieval.

What it is now

A script, scripts/build-review-pack.mjs, runs once before anything spawns. It writes one line-numbered section per changed file, every line marked as added, removed or unchanged: files under the body cap in full, the rest as just their changed hunks. Every finder except the signatures-only integration pass starts with one read of that file, and none of them runs git. The same change pinned every finder to effort: medium on a Read, Glob, Grep roster, which drops the tool and MCP manifest from each spawn’s fixed floor, 56.5k tokens down to 34k.

The pack does not stop the finders reading the same bytes. Every finder that gets it reads all of it. What it removes is round trips, and round trips are the bill: every turn re-reads the entire accumulated context at the cache-read rate, so cost scales as turns times context size. Collapsing fifteen to thirty gather-and-read round trips into one read makes each finder’s context bigger earlier and still wins, because turns dominate.

Matched A/B on the same diff, Sonnet on both arms: turns fell from 69 to 33, fresh input by 37%, cache reads by 68%, cost from $2.61 to $1.07, and the slowest finder from 8.4 minutes to 3.8. The findings held up too: the cheaper arm reproduced the primary race condition with its full triple, and surfaced three legitimate documentation findings the expensive one missed.

A later pass took another 23.4% off the pack itself. The first version carried a union diff and then repeated those same added lines inside each file body, so every changed line was paid for twice. Packing each file once as marked hunks removed that by construction, and replayed across eight merged pull requests it cut 2.95MB of pack down to 2.26MB with identical file coverage.

What it did on real reviews

The board has 122 real reviews inside a matched band of 8 to 60 changed files, 94 before the pack and 28 after, which keeps the two eras within 12% on diff size.

  • 51%Lower cost per review$49.96 to $24.60
  • 44%Lower cost per changed file$2.14 to $1.20
  • 40%Fewer tool calls392 to 234 per review

The driver’s own token traffic barely moved across the same band, 13.72M before and 13.90M after. So the saving is not the driver getting cheaper. It came out of the fan-out, which is the only part the pack touched. The tool calls fell for the same reason: a finder that reads one file makes fewer calls than one running git and opening changed files one at a time.

None of it came from the model. The finders are the same Sonnet they were before the pack, on a smaller budget than they had then, because the same change dropped them from xhigh effort to medium. They did not get smarter. They stopped re-deriving what the agent next to them already had.

Getting this into your own project

Two halves, and only one of them needs Kangentic.

The pack is portable. build-review-pack.mjs is one Node script with no dependencies. Gather once, write the result to a file, then tell every reviewer to read that path first and never run git itself. That works with any agent that can open a file.

The column is the Kangentic half. Open the Column Manager on a Code Review column and set two things:

Session
Isolated (starts clean on every entry)
On enter automation
Send message to agent: /code-review {{baseBranch}}

Kangentic drives plenty of other coding agents, and Codex documents subagents of its own, so the shape of this transfers even though the skill itself is Claude Code’s. Board Configuration covers the rest of the column options, and Agent Orchestration covers what happens once the card moves.