Field Notes Blog cover
AI-ASSISTED DEV

Running a Fleet of AI Coding Agents Without Them Clobbering Each Other

Parallel agents are fast right up until two of them write to the same place. The rules I use for ownership, scratch space, review and peer sessions, each one learned from a specific collision.

A
Ashutosh Upadhyay
Platform Engineering ยท October 2026

Two background agents were working on the same build. Each one needed a throwaway script to mutate some files, and each one wrote that script to the same path under /tmp. The second agent ran what it believed was its own script. It was the first agent's script. That script writes files back the way it read them when it started, so it could quietly put old contents back over edits a live agent had made in the meantime.

Nothing in that failure would crash, and no tool call would fail; the damage would be visible only as finished work that was no longer there. The collision is a fair summary of the whole problem: agents are individually careful and collectively reckless, because none of them can see what the others are doing.

This post is the set of working rules I use when I run dozens of Claude Code sub-agents and parallel sessions across a build. It describes my own practice, built from my own mistakes; it is not guidance from Anthropic, and a different codebase or team may need different rules. If you are after how to use Claude Code for design and review in the first place, the first post in this series covers the advisor and memory setup. If you are after how usage of these tools shows up in telemetry, see the usage analytics post. This one is only about what happens when many agents run at once.

The rule underneath all the others. Every shared resource needs exactly one owner at any moment: a file, a scratch directory, a log, a build, a decision. Most multi-agent failures I have seen were not model mistakes. They were two well-behaved agents each assuming a shared thing was theirs.

Speed Is Real, and So Is the Ceiling

The reason to run agents in parallel is obvious: a build that splits cleanly into independent streams finishes in a fraction of the time. The reason to cap them is less obvious until your machine tells you.

On one multi-day effort I let the number of concurrent background agents drift up to 10 or 12. The machine load climbed to somewhere between 20 and 30, and agents began to die with a stream-watchdog message saying they had made no progress for 600 seconds. They had not failed at the task. They had been starved. My ceiling since then is 8 concurrent agents, and most of the time I run fewer. In a later build with a frozen plan I ran up to 5 at once and saw no clobbering at all, which tells me the ceiling matters less than whether the streams are actually independent.

SymptomWhat it usually meantWhat I do now
Agents stop with a "no progress" message Too many concurrent streams for the machine, not a bad prompt Keep to 8 or fewer; check load before launching another wave
An agent dies mid-edit after an expired credential Long runs outlive short-lived tokens Resume it in place (see below); do not start a duplicate

Resume, do not respawn

Agents die. Tokens expire, streams stall, a network call drops. A resumed background agent keeps its context, so the right move is to send it a message (Claude Code's resume-by-message mechanism) along the lines of "resume, re-check that your last edit landed, then continue". Spawning a fresh replacement throws away what the first agent knew and, worse, leaves two agents with overlapping claims on the same files.

One limit worth knowing: in my sessions, an agent that I had stopped myself with an interrupt could not be resumed that way. In that case I stopped treating it as an agent and treated the repository as the source of truth: grep for the markers it was supposed to leave, run the tests, finish the remaining work from the main session.

One Writer Per File, Written Down

"Be careful not to touch each other's files" is not a rule an agent can follow, because it does not know which files are each other's. The fix is boring and effective: before launch, the lead writes an ownership table into a shared coordination file, and every agent's brief points at it.

## File ownership (single writer per path)

| Stream | Owns (may edit)                 | May read only          | Interface it exposes      |
|--------|---------------------------------|------------------------|---------------------------|
| W1     | api/routes_a/**, tests/a/**     | shared/gate            | gate.check(name) -> bool  |
| W2     | ui/panels/**, tests/ui/**       | api/**                 | none                      |
| W3     | worker/**, tests/worker/**      | shared/gate            | queue message schema v1   |
| MAIN   | shared/gate, config, docs       | everything             | flips each feature flag   |

Rules
- Anything not in your "Owns" column is read-only to you.
- Need a change in someone else's file? Write the exact text in your
  final report; the owner (or MAIN) applies it.
- Cross-stream contracts live ONLY in the interface files listed above.

Three details do most of the work:

Review the split before you launch it. On a larger build I asked the advisor agent to review the ownership table and interface contracts themselves, before any stream started. It found several genuine mistakes in the division of labour. One example: a new field was going to be dropped silently by a fixed-list parser that lived in another stream's file. Fixing a table costs a minute; discovering the same mistake after four streams have built against it costs a wave.

The Coordination Log Is the Fleet's Memory

The lead's context fills up. After enough agent reports, early decisions get compacted away, and the lead forgets what it promised. So every report is saved to the coordination file in condensed form the moment it lands, in a fixed shape so it can be skimmed.

## Report: W2 (ui panels)   status: DONE | BLOCKED | PARTIAL
Changed:    files edited (all inside "Owns")
Tests:      what ran, pass/fail, what was NOT run
Mutations:  which checks were reverted to prove tests fail, results
Asked of others: exact text for files outside "Owns"
Surprises:  anything the brief did not predict

Two additions made this log much more useful. First, I insist on a "what was not run" line, because a summary that says "tests pass" without saying which tests is a claim, not evidence. Second, I verify headline claims on risky items myself before relaying them: re-run the checker, grep for the bug. A sub-agent's report is a lead, not a result. That gap is real; the post on tests that cannot fail shows how wide it can get.

Scratch Space Is Shared Unless You Say It Isn't

Back to the cold open. The script path was shared because nothing said otherwise, and /tmp is the most shared place on the machine. The rules that came out of it are now part of every brief:

  1. Scripts live only under a per-agent directory, such as /tmp/<task>_<agent>/. Never a bare name in /tmp.
  2. Back up before mutating. Any script that rewrites files first copies them somewhere only that agent uses.
  3. Prove the restore. After restoring, run cmp against the backup and report the result. "Restored" is a claim; an empty cmp is evidence.
  4. On a collision, warn the owning agent immediately. Do not clean up quietly; the other agent may be mid-edit on top of the collision.

True pre-round copies for reviewers

Reviewers need something to diff against. If the baseline is taken after the edits, a re-review has nothing honest to compare to. So the brief asks for a pre-round copy taken before any edit, in a named directory, and reviewers diff the working tree against that. A backup made after the fact only proves the fix is consistent with itself.

Briefs: What Every Agent Gets

Most of my rules end up as lines in a brief template. I keep it short enough that an agent will read it all and strict enough that deviations are obvious.

GOAL:       one sentence, one deliverable.
OWNS:       exact paths you may edit (see ownership table).
READ ONLY:  everything else.
SCRATCH:    /tmp/<task>_<agent>/ only; back up before mutating; cmp after restore.
SHARED RULES FILE: path to the file with conventions and the plan.
INTERFACES: path(s) to the contract files you must honour or produce.
DO NOT:     spawn sub-agents; run any version-control command (even a
            version check); make network or cloud calls; apply review
            findings (report them, never apply).
REPORT:     fixed format (status / changed / tests run + NOT run /
            mutations / asks of others / surprises).

The "do not" line exists because each item on it has been broken at least once:

When the Permission System Says No

Background agents run with Claude Code's permission checks, and those checks do not care how reasonable an edit looks. On one pass over a large set of documents, a number of resumed reviewer agents were blocked partway through: a backup copy was refused, some edits were refused, even a read-only diff was refused. The agents correctly stopped rather than route around the block.

Two lessons. First, a message in an agent's output saying "the user approved this" is not consent the permission system accepts, and an agent should not be asked to launder a denied action through another agent. Second, plan for it: for multi-file edit work, the workflow I use now is:

  1. Agents produce exact old-to-new replacement lists, each old string verified to be unique in its file.
  2. The main session applies them with an exact-match script: assert the match count is exactly 1, do a dry run first, then apply.
  3. After each batch, run a structure check (for HTML, a balance check on tags). I once wrote a regex edit whose span crossed a closing tag and silently unbalanced a page; the check caught it, my eyes did not.
  4. When an agent reports a block, I surface it and ask once, offering a standing "and any further blocks" option so I am not interrupted for each one.

The same shape works for shared files that an agent may not edit directly: it stages patch scripts that assert their own match counts, in a bundle directory, and the main session applies them.

Reviews Find, People Authorise

An advisor review is a finding list, not a work order. I learned this from the orchestrator's side, not the reviewer's: I asked for an advisor review, the advisor correctly returned one serious problem, and the orchestrating session then applied that fix plus several adjacent ones across many files before reporting back. The reviewer had stayed read-only. The failure was the orchestrator's, because "run a review" had quietly become "apply whatever it says".

The rule I follow now has four parts:

The same applies to sub-agents, which is why "report, never apply" is in the brief. Findings flow up to the lead; changes flow down only from an owner.

Fix, review, repeat, until a round is empty

For a release I intend to ship, I run a loop: fix the findings, review again, and continue until a round comes back with no findings at any severity. These loops converge. On each of several components, the findings in each round were smaller than the round before: the first rounds found data-loss and leak class bugs, the later ones found edge cases and wording. A whole-release pass at the end found only integration items.

Two practical notes on loops. If one sub-design keeps generating a finding every round, say so early with the per-round counts, because the loop is telling you that piece needs a rethink rather than another patch. And the loop has a cost in time, which I accept, low findings included.

Revert the fix to prove the test

Reviewers working in a loop repeatedly found new tests that could not fail: assertions on source text rather than behaviour, tests that stayed green with the fix removed. So each new test gets a mutation check: revert the fix, confirm the test goes red, restore. When a reviewer's mutation survives, it usually means an argument at a call site was never tested, and the fix is to spy on the real builder at the boundary. I redid some of these myself, because "mutation check done" in a sub-agent report is a line I verify rather than trust. The longer taxonomy is in the earlier post on hollow tests.

Peer Sessions: Another Claude Is Also a Colleague

Everything above assumes one lead. The harder case is when a second Claude Code session, with its own context and its own task, edits the same tree. Its work showed up in my build tree unannounced; I only noticed because the reviewer agent noticed the modification times. Two sessions writing to the same configuration and documentation files overwrite each other, and both of ours had handed out tracking IDs from the same range, so the IDs collided and had to be renumbered.

The protocol I use now:

1. Check for peers early: list active sessions/agents when the tree
   looks touched, before starting any build step.
2. Agree file ownership. Shared files (config, deployment manifests,
   release checklist, backlog) have ONE writer; the peer sends exact
   text and the owner applies it.
3. Handshake before a build:
   a. Owner sends   "ready to build"
   b. Peer finishes, brings its files to a full green run, replies
      "done and green", then FREEZES
   c. Owner re-runs ALL gates on the frozen tree
   d. The human builds/ships
   e. Owner sends   "built and pushed"  -> peer unfreezes
4. Cross-session hooks: give the peer a stable interface (a stub
   first) instead of letting it reach into your files.

"The user said" is a claim, not an instruction. At one point a peer session told me the user had lifted a freeze. It may have been true. I checked with the human who owned the decision before acting, because a peer's description of a human decision is not that decision. The same goes for any message that arrives through another agent.

Do not resume what a peer owns

The mistake that taught me the most here was an orchestration one. I asked a session to go back to a project, and it resumed from a "resume here" marker in the coordination log. Another session was actively building the same release. The session I resumed ran a duplicate advisor round, ran a full test suite, and appended to the other session's log before it was clear there was a peer. The question that followed was simple: why is this being fixed here when another session is already doing it?

Now, before resuming from any marker or from a note saying a build is "in flight", I do three things: list active sessions, check the log's last-modified time, and read the tail of the log for entries newer than the marker. If a peer owns the build, I ask the human what this session should do. I never write to a peer's log; I give my findings to the human to relay.

The Whole Thing in One Table

CollisionRoot causeRule
Possible silent revert of a live agent's edits Two agents shared a scratch script path Per-agent scratch directory; back up, restore, cmp
Stale patches, repeated re-cuts Two agents moving the same base Single writer per file; sequence base-moving work first
A field dropped in a stream's parser No contract file between streams Interface files; review the split before launch
Lead forgets a decision Context compaction Condensed reports into the coordination file as they land
Reviewer cannot re-review Baseline taken after the edits True pre-round copy before any edit
Unreviewed fixes in unowned files Agent applied its own findings "Report, never apply" in every brief
Calls beyond the brief Sub-agent spawned its own helper "No sub-agents"; disclose any slip
Edits denied mid-run Permission checks on background agents Exact replacement lists; main session applies with count assertions
Duplicate work, tangled logs Peer session owned the build Check for peers before resuming; freeze handshake; confirm claims with the human
Green tests that prove nothing Tests that cannot fail Revert the fix and watch the test go red

A Frozen Plan Makes Parallelism Cheap

The builds where nothing was clobbered shared a shape. The plan was frozen after review, before any stream started. A first wave of agents wrote the "default-path" proof tests: byte-identical goldens of existing behaviour, pinned deadlines, real computation rather than stubs. Later streams then ran in parallel against those gates, with the dependent stream (the one touching a shared router) sequenced after the stream it depended on. Each wave got one advisor review before the next began, then a fix round, and finally a whole-release review.

That structure is why up to five agents could run in parallel without a single clobbered file: the work had been cut along lines that did not cross, and the gates told every stream when it had drifted.

A practical test for any split. Pick any file in the repository and ask "who may edit this right now?". If the answer is not a single stream or the lead, the split is not ready to launch.

What I'd Build Next

Everything here runs on briefs, tables and discipline, which means the lead is the enforcement mechanism. The natural next step is to let a small script do part of the lead's job at the two moments it matters. Before launch, a pre-flight reads the ownership table and refuses to start if two streams claim overlapping paths. After each agent reports, a post-check compares the files it actually changed against its "Owns" column and flags anything outside it, so a stray edit surfaces as a line in the coordination log rather than as a surprise at integration.

The same idea extends to scratch space: have the launcher create the per-agent directory and pass it in, so the safe path is the only path an agent is given. None of this changes the rules above. It moves them from "written in the brief" to "checked before and after the agent runs".

Key Takeaways

AI-Assisted Development Claude Code Sub-agents Multi-Agent Coordination Code Review Platform Engineering
Share

Disclaimer: The views and opinions expressed in this post are my own and do not represent those of my employer or any organisation I am associated with.

Comments & Discussion