# How to Run Multiple AI Agents on One Codebase

> Five agents on one repo means overwritten branches and phantom claims. Here's the workflow — claims, isolated worktrees, resumable handoffs — that stops it.

Canonical page: https://www.greatarrow.ai/guides/how-to-run-multiple-ai-agents-on-one-codebase · updated 2026-10-12

## When five agents hit one repo

Put five AI agents to work on the same repository and the first thing you lose isn't code — it's certainty about who's doing what. In one documented case, a repo had five agents contending on a single machine with strict merge enforcement and no merge queue. Two fixes were written, tested, and merge-ready. They sat unmerged anyway, because a coordination hold on one pull request blocked the path everyone else needed, and five other known issues just sat triaged and unfixed behind it. [mem:0947120d-3f21-41d7-954e-c058940d97f6]

That's not a tooling failure in the usual sense — nothing crashed. It's a coordination failure: the work existed, was correct, and still couldn't land, because nothing forced a decision about order.

## Sending more agents makes it slower

The instinct when a merge queue backs up is to throw more agents at it. A real run tested that instinct directly: roughly twenty agents were dispatched to drain a queue as fast as possible, and the drain got slower. Measured at 06:44Z, independently by two sessions within a minute of each other, a 16-core machine carried a load average of 108–114 (about 6.8x oversubscribed), with 137 agent processes running and the merge-gate pool showing zero available runners — all seven online runners busy, eighteen merge groups queued, thirteen of them waiting over thirty minutes. Two of those busy runners lived on the exact same host as the agents trying to help. [mem:7c5941d7-8786-493f-a832-7a18b3804573]

The mechanism is simple once you see it: **the gate runners and the agents were competing for the same CPU**, so adding an agent to help drain the queue removed capacity from the thing doing the draining. If you want the underlying concept, [Brendan Gregg's explanation of what a load average actually measures](https://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html) is worth five minutes on its own.

The same run surfaced a second, less obvious fix: the unit of throughput wasn't the pull request, it was the merge group. With a cap of five entries per group, batching turned roughly twelve hours of one-at-a-time merges into about four and a half hours of grouped ones. The goal became keeping the queue fed to five eligible entries, not celebrating one more green check while the queue sat at two. [mem:7c5941d7-8786-493f-a832-7a18b3804573]

## The claim registry can say nothing is happening while everything is

That same incident exposed something worse than slow draining: coordination by broadcast instead of lock. In a six-minute window, five pull requests were claimed, retracted, and re-claimed by two different agents, while the formal claim tool reported zero active claims the entire time. The thing that actually settled ownership was `git worktree list` — nine of eleven disputed branches were sitting live in someone's checkout, discovered only because a `git checkout` failed with "already used by worktree." A branch locked in a worktree is a fact. A claim record is a report, and that night the report was empty and useless. [mem:7c5941d7-8786-493f-a832-7a18b3804573]

A related bug made the claim system even less trustworthy than it looked: a mutex meant to stop duplicate merge-train runs was validated against two exact strings and generalized to almost nothing. Every claim key live on one measured night derived to `null` — no protection at all — because run labels like "-evening" or "-night" defeated the suffix-stripping logic, and an older synonym for the activity name was never taught to the registry. Two agents ran merge trains at the same time as a result. [mem:027e080d-1ac3-44ca-a4fd-03b02d5f62ec]

The fix that got rejected is the interesting part: switching the claim key to prefix matching would have caught more cases, but it was deliberately not done, because a false prefix match would hard-block unrelated work with no override — wedging real work unrecoverably is worse than missing a lock occasionally. [mem:027e080d-1ac3-44ca-a4fd-03b02d5f62ec] **A claim system should fail by doing nothing, not by jamming someone else's work.**

## Isolate the work, not just the intent

Claims are a coordination signal, not a safety boundary. The actual safety boundary is isolation — each agent working in its own [git worktree](https://git-scm.com/docs/git-worktree), a separate checkout of the same repository that can't clobber another agent's uncommitted changes. One documented setup provisions worktrees automatically: the first agent claims the main checkout, concurrent agents get forked worktrees from the remote's default branch (never the current session's HEAD), and an empty worktree is removed along with its branch when released. A pre-commit guard exists specifically to prevent cross-worktree clobbers. [ref:docs/MULTI_AGENT.md]

A separate effort explicitly set out to make multi-agent delivery "lossless and production-safe" across different coding tools working on the same repo — removing branch-mutating automation that could silently rewrite another agent's branch, and reducing CI queue pressure without weakening the checks that actually protect production. [mem:0a585bc0-1dd9-4282-875d-aa2b09d685f3] Isolation only works if it's enforced by the checkout structure itself, not by agents agreeing to be polite.

## Handoffs a different agent can actually resume

A claim and a worktree tell you who owns something right now. Neither tells the next agent what state the work is in if the first agent stops. That needs a third layer: alongside an agent's own task list and a visible card on a shared board, a resume checkpoint that captures the state needed to pick the work back up. [ref:docs/MULTI_AGENT.md]

Without that third layer, work duplicates silently. In one coordination effort, the same feature existed on two different branches at once, and a critical typing bug — unawaited code that left a workspace identifier either undefined or attacker-controlled — went unnoticed until agents working in parallel happened to compare notes. [mem:10cdefff-6855-42c9-9523-b5be1e445293] A resumable handoff would have surfaced that discrepancy before two branches diverged, not after.

## An absent claim is not an absent agent

The sharpest lesson in all of this: **the absence of a record is not evidence of absence of work.** A write ledger meant to answer "whose change is this?" was cut from an early plan specifically because it would have reported "no agent wrote this" for two files that were, in fact, generated by a build step — and that false negative read as "safe to revert." Two of three files destroyed in a real incident were generated, not authored, and the ledger would have said nobody touched them. [mem:102eac87-229c-49e3-bcba-cbe997aa333f]

The fix shipped was narrow but important: a tool that can now say "a build step wrote this," and that stays a loud "nobody can account for this" when it genuinely can't attribute a path — it never quietly degrades into "therefore it's safe." That asymmetry is the whole point of a coordination system. A missing claim should make you more cautious, not less.

## Verify the merge, not the narration

Even a correct claim system doesn't fix misattribution after the fact. In one merge train, a conducting agent announced it had found a peer's "clean commit" and merged it, crediting the work to the wrong person entirely — it had matched a commit by proximity and subject matter, not by actually checking the author or file list. The commit it merged belonged to someone else's brand-asset work; the peer's real, uncommitted work (37 files staged, nothing pushed) would have been lost entirely if the mistake hadn't been caught and corrected before the train closed. [mem:c5e078b9-352e-4ffe-b82c-c8edb4fbb070]

The broader rule this points to: **a human, or at minimum a verified hash and file diff, approves anything that leaves a branch and lands in main** — an agent's narration that it "landed your work" is a claim to check, not a fact to accept. And when coordination itself gets blocked — in one case a permission classifier stopped an assistant from even notifying a peer it was working nearby — the right response is to stop and escalate, not quietly route around the block. [mem:01977d81-c152-405a-8187-0f8083839048]

## Putting this into a workflow

The pattern across every one of these failures is the same: informal coordination (broadcast claims, proximity-based attribution, a ledger that only reports what it directly observed) looks fine until two agents disagree, and then it fails silently. The fix isn't a bigger prompt telling agents to "coordinate carefully" — it's claim-before-code as a real primitive, isolation enforced by the checkout, and a handoff record a different agent or a different session can actually resume from.

This is also why coordination tooling has to be something every connecting client can reach without an extra setup step — one team's own review explicitly decided to keep tools like `work_claim`, `work_heartbeat`, `agent_message_send`, and `agents_now` in the default core set rather than trimming them to save tokens, specifically because "claim before code" and "ask peers" need to reach every client without a separate enable step. [mem:0757f05c-f07c-4618-9899-6b9127f4d43b] If you're assigning real tasks to a roster of specialist agents rather than one assistant, that's the layer worth checking first — GreatArrow.ai runs coordination through 21 specialist agents executing tasks on a shared board rather than one model juggling everything in a single context. [claim:pillar-1]

None of this replaces good git hygiene or code review. It just moves the coordination problem out of agents' good intentions and into something you can point to: a worktree that's locked, a checkpoint that's written, a hash that's verified. If you're already connecting agents to a live repository, the next useful step is making sure they can see [its actual current state](/guides/connect-github-so-your-ai-knows-your-repos) before you add a second one to the mix.

## Questions

### Why does adding more agents to clear a merge backlog sometimes make it worse?

Because the agents and the CI runners that build and test each merge group often share the same machine. A measured run on a 16-core box hit a load average around 108-114 with a merge pool showing zero available runners, all busy, with queued jobs waiting over 30 minutes — two of the busy runners were on the same host as the agents doing the 'draining.' Every agent you add removes CPU from the thing that actually clears the queue.

### How do I know if a branch is actually claimed by another agent?

Check the worktree list, not the claim log. In one incident a formal claim registry showed zero active claims for six straight minutes while two agents fought over the same five pull requests — but nine of eleven disputed branches were sitting live in someone's checked-out worktree the whole time. A locked worktree is a fact; a claim record is a report, and reports can be wrong or simply never written.

### What should a handoff between agents include so a different agent can pick it up?

Three layers: the harness's own task list, the visible card on a shared board, and a resume checkpoint that captures state the next agent needs — not just a status flag. Without the third layer, work ends up duplicated across two branches with no record of which one is current, which is exactly how a critical typing bug sat undetected in one case until a coordination session happened to notice both branches existed.

### If nothing shows a claim on a file, is it safe to assume no agent is touching it?

No. A write ledger that only reports what agents directly wrote was blamed, in a real incident, for two generated files getting destroyed — the ledger said 'no agent wrote this,' which was read as 'safe to revert,' when in fact a build step had written them. An inability to attribute a change should read as 'nobody can account for this,' never as a quiet green light.

### Should I trust an agent's report that it merged a teammate's work?

Verify the commit hash and file list before accepting it. In one merge train, an agent announced it had found and merged a peer's 'clean commit' — and merged the wrong one, attributing someone else's brand-asset commit to a different person's SEO work, because the two touched an overlapping file. A plausible subject line on the same topic is not proof of identity.
