Back to writing

12 June 2026

What a ten-agent build actually looks like

We ran a build with a fleet of coding agents to see where the "ten agents while you sleep" pitch was real. Some of it genuinely 10x'd us. Some of it was an expensive way to produce garbage we then had to redo. The whole skill turned out to be knowing which was which before spending the tokens.

The setup: worktrees, review chain, contracts

The mechanics first, because the mechanics are what kept it from being chaos.

Each agent got its own git worktree — isolated working copy, isolated branch, no two agents editing the same file. We learned that one the hard way in an earlier attempt where two agents shared a directory and stomped each other's edits, and we spent an afternoon untangling the merge damage:

git worktree add ../svc-auth   -b agent/auth
git worktree add ../svc-orders -b agent/orders
git worktree add ../svc-search -b agent/search

Between agents, contracts instead of coordination. When the orders agent needed a field the auth agent owned, it didn't reach into auth's code. It filed a contract proposal — a written change to the shared interface — reviewed before either side built against it:

proposal: auth.User needs `tenant_id` in the token claims
from: orders-agent
reason: orders queries filter by tenant, need it without a second call

And every agent's output went through a review chain before it landed. An independent reviewer agent read the diff and tried to break it. Nothing merged on the author's say-so.

The permission boundaries were as load-bearing as the worktrees. Each agent got a settings file scoping it to its own service directory — the auth agent could write services/auth/** and read the shared contracts, but was denied write access to services/orders/**. Without that, a helpful agent "fixing" a type error in a sibling service is a merge conflict you find at integration time. The scope wasn't advisory, it was enforced: an edit outside the boundary failed rather than landing. We added this after an early run where an agent, trying to make its own tests pass, edited the shared model in a way that broke two other agents' assumptions silently.

Where it genuinely 10x'd us

Two situations, and they were the ones worth the setup cost.

Parallel independent work. We had three services with no shared code — auth, orders, search — and three agents in three worktrees built them at once. This is real parallelism: the services don't touch, so the agents didn't block each other. We got roughly three days of work in one, and the isolation meant no merge pain at the end.

Adversarial review. One agent wrote, a second with a different prompt tried to refute it. On this build the reviewer caught a broken pagination cursor in the search service and an off-by-one in the orders filter that the authoring agent had written confidently and couldn't see. Same reason you don't let an author review their own PR — a fresh set of eyes with a mandate to break things finds what the author's mandate to build things hid. Running it as a second agent made it cheap and consistent.

Those two — independent fan-out and skeptical review — are where the fleet earned its keep.

The pagination bug the reviewer caught is worth spelling out, because it's the kind a human reviewer skims past. The search service's cursor encoded the last-seen ID and page size, and at a page boundary where two records shared a sort key, the cursor advanced past both instead of resuming between them — so one record fell in the gap between pages and never appeared in any result set. The authoring agent had written a test, but the test used records with distinct sort keys, so it passed. The reviewer agent's mandate was to break the code, so it constructed the tie case, and the bug fell out immediately. That's a bug you find by trying to break the thing, not by reading it, and a second agent with a break-it prompt does that consistently where a tired human reviewer does it sometimes.

Where it just burned tokens

The failure was running producer/consumer work in parallel. We did it once on this build and paid for it.

We had one agent designing the payment schema and another building refund logic on top of it, and we started them together to save time:

A: designs the payment schema
B: builds the refund logic on top of it   <- needs A's schema to exist

B built against a guess of what A would produce. A produced something else — different field names, a status enum B hadn't assumed. B's work was wrong the moment A landed, and we redid it against the real schema. We paid for B twice and got one usable result. That's the same trap as parallelizing code review and test-writing: the reviewer critiques code that's about to change, the tester writes tests for code still in review, and both outputs are trash the moment the upstream work moves.

We ran the numbers on that pair afterward, because "burned tokens" should be a figure, not a feeling. B's first pass cost roughly the same token spend as the schema work itself, and about 80% of it was thrown away — the refund logic's structure was salvageable but every field reference, every enum check, every test had to be rewritten against A's actual output. Running them serially would have cost A's time plus B's time once. Running them in parallel cost A's time plus B's time roughly 1.8 times, for the same result and a day of reconciliation on top. The parallelism didn't just fail to help, it cost more than the serial version in both tokens and wall-clock, because the reconciliation was slower than just waiting for A would have been.

The contract-proposal mechanism was our attempt to make some producer/consumer work safe to parallelize, and it half-worked. If A and B agree on the interface up front — the contract proposal is reviewed and frozen before either builds — then B can build against the frozen contract while A implements behind it. That works when the contract is genuinely stable. It fails when the act of implementing A reveals the contract was wrong, which on a new schema is often, because you don't know the right shape until you've tried to build it. So the honest rule stayed: freeze the contract first only when you're confident it's right, and on genuinely novel work you usually aren't, so serialize.

Producer/consumer work is serial. A finishes, A's output is real, then B starts. No agent count fixes a dependency — it just multiplies the wasted work.

There's a middle option we use now instead of full parallel or full serial: stub-first. A writes a thin stub of its interface — the schema with field names and types but no logic — lands that in the shared contracts directory, and only then does B start building against the stub while A fills in the implementation behind it. B is building against something real and frozen, not a guess, and A can change the internals freely as long as the stubbed interface holds. This recovers some parallelism on producer/consumer edges where the interface is knowable before the implementation, which is a smaller set than you'd hope but larger than zero. The payment schema wasn't in that set — we didn't know the right field names until we'd built it — which is exactly why stub-first wouldn't have saved that pair either.

The honest version

A ten-agent build isn't ten times faster. It was faster on the independent parts, sharper on the reviewed parts, and exactly as slow as before on the parts with real dependencies, because those can't be parallelized no matter how many agents you spawn.

The skill is in the dependency graph, not the agent count. Draw it first: which edges are independent — fan those out — and which are producer/consumer — keep those serial. We got it wrong on the payment/refund pair and right on the three services, and the difference was a day of reconciliation versus three days of work in one. That's narrower than the pitch, and the narrowness is the part worth being honest about.