← Our FailuresOne Agent Only: Why Parallel Writer Fan-Outs Spray Duplicate Work and Leave Mid-Edit Breakage
intermediate6 min read · updated 2026-06-20
Market & numbers — every figure sourced
two_thread_lock_cost_ms118,000 msMartin Thompson, The Single Writer Principle — benchmark: two threads contending on a lock for a 500M-op increment loop
one_thread_lock_cost_ms10,000 msMartin Thompson, The Single Writer Principle — same benchmark, single thread
pairwise_collision_paths3,741 interaction-pairsest: N(N-1)/2 distinct writer pairs for an N=87 agent fan-out, the count of independent collision surfaces a single-writer design removes
One Agent Only: Why Parallel Writer Fan-Outs Spray Duplicate Work and Leave Mid-Edit Breakage
The fastest way to feel productive and ship nothing is to launch a swarm of agents at the same codebase. It looks like leverage. It is actually contention. This entry is the post-mortem on why fan-outs of parallel writers fail, and the one rule that fixes it: for any artifact you can mutate, exactly one writer touches it at a time.
What we tried
The pitch is seductive. You have a big job — finish five apps, sweep a hundred files, knock out a backlog — so you spin up many agents in parallel and let them all work. In our own runs this took two recurring shapes:
- The fan-out fleet. Dozens of agents (in one case 87) dispatched at once, each with edit access to overlapping files and the same shared stores. The mental model was "more agents, more throughput."
- The second-agent shortcut. A single extra agent quietly opened in a second worktree to "help" with the same branch the main loop was already editing.
Both assume that agents writing in parallel compose cleanly. They do not. The moment two writers can touch the same artifact, you have not bought parallelism — you have bought a race.
What broke
- Duplicate, conflicting output. Five sessions each generated their own copy of "the app," producing roughly two dozen duplicate bundles that shared the same identifiers, so the OS misrouted which one actually launched. Each writer was locally correct and collectively wrong.
- Mid-edit breakage. Agents revert and overwrite. A second worktree ran `git checkout .` mid-stream and wiped another agent's uncommitted work. Files were left half-edited because two writers interleaved on the same region — the classic read-modify-write loss where two readers see state 0, both write back 1, and the increment to 2 simply vanishes both write back 1.
- Silent corruption, not loud errors. The worst failures threw no exception. Stale data quietly overwrote fresh changes, so the build went "green" while the truth on disk regressed. Race conditions in multi-agent orchestration manifest silently — the result just depends on who got there last depends on which one gets there first.
- Coordination cost dwarfing the work. This is not new. Thompson's benchmark shows two threads contending on a lock take 118000 ms for the same job a single thread finishes in 10000 ms — managing the contention costs more than the actual work. Agents are vastly slower writers than threads, so the ratio is worse, not better.
- Quadratic blast radius. With N parallel writers there are N(N-1)/2 distinct ways for two of them to collide. For an 87-agent fan-out that is 3741 independent collision surfaces. A single observed collision reliably predicts systematic collisions at scale.
The root cause
Write contention, not "bad prompts," is the bottleneck. As the single-writer principle puts it, the biggest limit on scalability is having multiple writers contend for any item of data or resource any item of data or resource. An LLM agent makes this worse than ordinary threads: a single agent "transaction" spans minutes, its read set is broad and opaque (it touches far more than it declares), and the live filesystem it writes to admits neither fork nor buffer — every write lands the instant it executes. There is no transaction to roll back.
The fix
Run single-writer in the main loop. One agent owns the work; mutation is serialized. Concretely:
- Default to one writer. Do the work directly in the main loop, in one workspace, on one branch. No fan-out of editors over shared files. This is the binding rule we adopted after the fleet runs: one agent only, no multi-agent write fan-outs.
- Parallelize reads, never writes. Read-only work (research, search, analysis, gathering context) fans out safely because it has no side effects. Keep writes on the single owner.
- If you must split work, partition by ownership. Give each agent a disjoint slice of the filesystem so no two writers can touch the same artifact — the single-writer principle says each item of data should be owned by exactly one execution context for all mutations owned by a single execution context for all mutations. Non-overlapping owners, then merge at a single integration point.
- Serialize through a queue or a lock, not shared memory. When concurrency is genuinely required, route work through a task queue or claim a lock per artifact (PostgreSQL advisory locks, Redis streams, optimistic version checks) so consumption is serialized and a stale writer's commit is rejected and retried writes fail if versions have changed. This mirrors the LMAX Disruptor's single-writer-per-slot ring buffer, which gives lock-free throughput precisely by never letting two cores write the same slot single-writer-per-slot.
- Make commits atomic and frequent. Commit fast so uncommitted work can't be wiped by another process, and so any interleaving collapses to a clean, ordered history instead of a half-edited tree.
- Prefer a relay, not a swarm. If long-running autonomy is the goal, run agents one-at-a-time in a ring/relay where a single driver advances one worker per turn. You keep the breadth of many roles without ever having two writers live at once — and a dead agent can't dead-end the loop.
Apply it
Before you spin up a second writer, ask one question: can these two agents ever touch the same file, branch, store, or record? If yes, you do not have parallelism — you have a race, and the coordination tax will exceed the work. Split by ownership or serialize through a queue. When in doubt, one agent, one loop, one writer. It is slower in theory and dramatically faster in practice, because you spend cycles on the task instead of on cleaning up the collision.