DEV Community

Cover image for Running multiple Claude Code agents in parallel: what worktrees cannot see
Nick Woodhead
Nick Woodhead

Posted on Originally published at foremerge.com

Running multiple Claude Code agents in parallel: what worktrees cannot see

If you only ever run one coding agent, you do not need most of this. You're also probably working more inefficiently than you could be.

If you run two or more: give each Claude Code session its own Git worktree, give each one a task that changes different things, and run your real checks on the merged result rather than on each branch. Worktrees stop agents from overwriting each other's files. They do not stop two agents from making plans that cannot both be true, and Git will merge those plans cleanly, because Git compares text, not intent.

That second problem has a name. A semantic conflict is when two changes merge without a textual conflict but contradict each other in meaning: one agent removes something another agent is building on, two agents build the same thing twice, or one changes a contract that the other is still coding against. Git cannot report it, because no two edits touched the same lines.

This post covers the setup that works, what Git can and cannot see, how to split work so semantic conflicts are rarer, and the part no amount of task splitting fixes.

The setup: one agent, one worktree, one branch

Running two agents in one checkout fails fast. They overwrite each other's half-written files, one session's install step breaks the other's build, and each agent's picture of the code goes stale under it. The standard fix is a Git worktree per agent: a separate checkout of the same repository on its own branch, sharing one history.

Claude Code has this built in. Run it once per session, with a different name each time:

claude --worktree feature-auth
claude --worktree add-paypal
Enter fullscreen mode Exit fullscreen mode

Or use stock Git, which works the same way for any agent:

git worktree add ../myapp-paypal -b agent/paypal
cd ../myapp-paypal
Enter fullscreen mode Exit fullscreen mode

A few things worktrees do not isolate, and that will bite you the first week:

  • Gitignored dependencies. git worktree add checks out tracked files only. node_modules, virtualenvs and build directories do not come with it, so each worktree needs its own install before its tests mean anything.
  • Ports, databases and other shared services. Two dev servers on one port, or two agents migrating one local database, collide no matter how the files are laid out.
  • Branches. Git lets a branch be checked out in only one worktree at a time, so each agent needs its own branch.
  • Integration. Someone still merges the branches. Pick one integrator, a person or one designated session, and do not let several agents each resolve the same conflict their own way.

With that in place, file collisions are solved. What is left is everything that was never about files.

What Git can see, and what it cannot

Git merges by comparing changed lines against a common ancestor. That makes it very good at one kind of collision and blind to the rest.

What the two agents did What Git reports What actually happened
Edited the same lines of the same file Conflict Git stops you. This is the case it handles.
Edited different parts of the same file Usually a clean merge Often fine. Sometimes not.
One renamed a function, the other added a call to the old name Clean merge The build fails afterwards. Your CI catches it, if it runs on the merged result.
One replaced a class, the other extended it Clean merge It may compile. The design is broken.
Both built the same feature from different angles Clean merge, or an add/add conflict You paid twice and now reconcile two versions.
One changed a schema or config contract, the other coded against the old shape Clean merge It builds, runs, and disagrees at runtime.

Only the first row is visible to Git. The rename row is visible to a compiler. The last three are visible to nothing in a standard toolchain, because the collision is between two plans, and plans live in prompts.

The canonical failure: replace versus extend

This is the one that made us build tooling, and it is described in more detail in the first post on this blog.

Two sessions, two worktrees, one repository:

Agent A: Replace PaymentService with StripePaymentService
Agent B: Add PayPal support to PaymentService
Enter fullscreen mode Exit fullscreen mode

Agent A moves every caller onto the new Stripe class. Agent B adds PayPal to the old class. They touch different files. Git merges both branches without complaint. The PayPal work now hangs off a class nothing calls any more.

Neither agent did anything wrong. Each did exactly what it was asked, each branch passes its own tests, and each agent reports success. Two agents can each be correct and jointly wrong.

Same file is not the same as collision

The intuitive fix is to warn whenever two agents touch the same file. Our own data says that signal is weak in both directions.

When we replayed 76 intents from our own parallel work on GPTree through Foremerge 0.4.0, one pair of branches produced a real Git conflict, and Foremerge had flagged that pair. Five other pairs had changed the same file while both pieces of work were open, and merged cleanly when we checked them with git merge-tree thanks to Foremerge.

What it does show is the shape of the problem. Same-file overlap produced mostly clean merges. The replace-versus-extend failure above produces no file overlap at all. File paths are the wrong unit for the collisions that cost the most.

The replay also showed a real gap: 0.4.0 could not match a scope on a whole class against a scope on one of its methods. Scope hierarchy is a candidate for a later release and is not built yet. More on that in the limitations section below.

How to split work between parallel agents

This part is useful whether or not you ever install anything. Most semantic conflicts are avoidable at assignment time, if you assign on the right axis.

  1. Split by what a task changes, not by what it is called. "Payments refactor" and "PayPal support" sound like different features and change the same class. Before starting a task, ask which things it will change: which classes, endpoints, tables, config keys. Two tasks with no overlap there can run in parallel. Two with overlap need an order.
  2. Name the operation, not just the target. Adding to something, extending it or modifying it preserves what other work depends on. Replacing, removing, renaming or migrating it does not. Two additive tasks on one target usually just need a shared contract. A destructive task on a target anyone else is touching should run alone, or first.
  3. Specify the contract before you split the work. If two tasks meet at a boundary, make one small change that defines the boundary (the interface, the schema, the API shape), merge it, and then start both agents from that commit. In the payments example, that is a PaymentProvider interface first, then Stripe and PayPal implementations behind it.
  4. List the shared registries. Route files, .env.example, dependency manifests, migration numbering, feature flag lists. Almost every task touches one of them, and almost no plan mentions them. In our replay, two of the same-file pairs were exactly this: a routes file and an example env file that neither plan had named.
  5. Write the plans down where every agent can read them, before any code. A shared markdown file listing each agent's task and targets catches duplicate work, as long as every agent reads it every time. It does not catch replace versus extend unless someone reads both entries and notices.
  6. Verify the merged result, not each branch. Every branch can be green while the merge is red. Build, typecheck and test after integration, and treat a branch passing its own tests as a statement about that branch only.
  7. Run only as many agents as you can follow. We do not have a number to give you. The honest limit is how many plans you can read and compare before the agents finish.

Steps 1 to 5 are all versions of one idea: get each plan into a form that can be compared with the others before code exists. That is exactly the step that falls apart past two or three agents, because a person has to do the comparing.

The part Git cannot see: compare plans before code

Foremerge is the tool we built for that comparison. It is an open-source coordination protocol for coding agents that sits above Git. Each agent declares what it is about to change, and how, before it changes anything. Foremerge compares declarations from every agent in the repository and reports a finding when two plans collide, while both worktrees are still clean.

A declaration is an intent with one or more semantic scopes, each carrying an operation:

foremerge intent publish \
  --agent "$AGENT" \
  --task "modernize-payments" \
  --summary "Replace PaymentService with StripePaymentService" \
  --scope symbol:PaymentService=replace
Enter fullscreen mode Exit fullscreen mode

Scopes are not file paths. The kinds are symbol, api, schema, config, infra, test, migration, env, file, component, contract and domain. The operation is declared by the agent rather than guessed from the summary, so "consolidate payments onto Stripe" and "replace PaymentService with Stripe" reach the same verdict.

When Agent B declares symbol:PaymentService=extend, Foremerge raises a destructive_vs_additive finding at HIGH severity, explains that one intent replaces the scope the other extends, and suggests coordinating on a stable PaymentProvider contract first. The suggestion is labelled as a heuristic, not an architecture decision. The response also lists the other agent's intent and every overlapping scope with both declared operations, so the agent can decide what the overlap means and record that decision.

A few properties matter for parallel Claude Code work specifically:

  • It works across worktrees. The store is a SQLite file inside your repository's Git common directory, which every linked worktree shares. Agents keep isolated files and see the same declarations.
  • It works across tools. Claude Code, Codex and Cursor each connect through the same MCP server and read the same store, so a Codex session and a Claude Code session on one repository see each other's plans.
  • You can check before you commit to a plan. check_conflicts runs the same comparison as publishing and stores nothing, so an agent that is still planning can ask without leaving a trace.
  • Claims never lock. Two agents can claim the same scope. The second gets a warning and the other agent's context, not a refusal, because a lock serializes work that was often compatible.
  • Acceptance is gated on evidence, not on the agent's word. When an agent finishes, it publishes a ChangeSet, and Foremerge runs a check you registered (a build, a typecheck, a fast test target) against the exact fingerprint of that candidate. An agent saying "tests pass" is recorded as provenance and does not satisfy the gate.

None of this replaces worktrees. Worktrees isolate files. Foremerge shares plans. You want both.

Setting it up

The fastest path is to paste this into Claude Code, Codex or Cursor from inside the repository:

Set up Foremerge in this repository so we can coordinate parallel agents.

1. Install it:      curl -fsSL https://foremerge.com/install.sh | sh
2. Initialize:      foremerge init
3. Wire this client and any others in use: foremerge setup all
4. Register the check I should be validated against, for example:
                    foremerge checks set test -- cargo test --all-targets
5. Confirm:         foremerge doctor --client all

Then read the Foremerge skill that step 3 installed for this client and follow
it from now on: publish your intent with semantic scopes before editing, claim
the scope, and check for conflicts before you start.
Enter fullscreen mode Exit fullscreen mode

Change step 4 to your repository's real test command. Step 3 asks the client to enable an MCP server, so it will prompt you first.

To install by hand instead: the install script above fetches a checksum-verified release binary for macOS or Linux. npm install -g foremerge works on macOS and on Linux with glibc. cargo install foremerge builds it with Rust 1.85 or later. Windows binaries are on the releases page. Then run foremerge init and foremerge setup claude (or codex, cursor, all) inside the repository.

What it does not catch

A coordination tool that overclaims is worse than none, because severity is what an agent uses to decide what to stop for. So, plainly:

  • Under-declaration. An agent that declares extend and then rewrites the thing is not detected. Not caught, but auditable: the declaration and the diff are both on record, so the miss can be reconstructed afterwards.
  • Scope hierarchy. A scope on a whole class does not match a scope on one of its methods, and domain:payments does not imply every payment symbol. Narrow, accurate scopes matter.
  • Files nobody declared. File overlap is not a conflict signal in this release. The shared registries from step 4 above only show up if an agent declares them.
  • Synonyms. Two agents using different names for the same concept can slip past the detector, and it can warn on work that was always compatible. Detection is deterministic and explainable, but heuristic.
  • Late arrivals. A conflict is detected when the later intent publishes. There are no push notifications, so the earlier agent learns of it at its next boundary or by checking again.
  • Other machines. The store is per machine. Coordination between machines is outside the project's scope.
  • Measured results. There is a benchmark plan and a harness in the repository, but no published coordinated-versus-uncoordinated results. We make no claim about time saved right now.
  • Everything else in your pipeline. It does not replace code review, CI, or architecture ownership. A missing finding never proves two changes are compatible.

The full list is in docs/limitations.md.

If you are deciding whether you need this at all, the companion piece on orchestrators and coordinators has a one-question test, and the case for a coordination layer has a post of its own.

Frequently asked questions

Can I run multiple Claude Code sessions in the same directory?

You can, but they share one set of files, so one session's edits and installs land underneath the other's work. Give each session its own Git worktree with claude --worktree <name> or git worktree add. Each gets a separate checkout and branch while sharing the repository's history.

Do Git worktrees prevent merge conflicts between agents?

No. Worktrees stop agents from overwriting each other's files while they work. Textual conflicts still appear at merge time when two branches change the same lines, and semantic conflicts, where two changes contradict each other without touching the same lines, merge cleanly and are not reported at all.

Why did my agents' branches merge cleanly but the app broke?

Most likely a semantic conflict: one agent renamed, replaced or removed something another agent was still using, or changed a schema or config contract the other coded against. Git only compares changed lines, so contradictory plans in different files merge without complaint. Building and testing the merged result catches some of these. Comparing plans before code catches others earlier.

How many Claude Code agents should I run in parallel?

There is no number we can defend. The practical limit is how many plans you can read and compare before the agents finish, plus how many installs, ports and databases your machine can run at once. If tasks change different things, more agents are fine. If they overlap, more agents mostly means more reconciliation.

Does Foremerge work with Codex and Cursor, or only Claude Code?

All three. foremerge setup all installs the skill and MCP entry for Claude Code, Codex and Cursor, and every client reads the same store in the repository's Git common directory. Any other MCP client can launch foremerge mcp over stdio. Agents from different vendors see each other's declared plans.

Does Foremerge lock files or stop agents?

No. Claims are leased and advisory: overlap produces a warning and the other agent's context, never a lock. Findings are evidence for the agent or person to act on. The one hard gate is acceptance, which requires a passing registered check on the exact candidate and no unresolved HIGH finding unless someone overrides it with a recorded reason.

If you run more than one agent

If you only ever run one coding agent, you do not need this. If you run two or more on one repository, worktrees handle the files and Foremerge handles the plans: one Rust binary, local-first, Apache-2.0, with its SQLite store in the Git directory your worktrees already share.

It will miss what agents do not declare. If you hit a collision it should have caught, an issue describing it is the most useful thing you can send us.

Originally published at foremerge.com.

Top comments (4)

Collapse
 
naw103 profile image
Nick Woodhead •

The question I’d most like answers to: if you run two or more agents on one repo, what are the collisions that cost you the most time? Was it two agents in the same file, or two that never touched the same file and still contradicted each other, like the PaymentService one above?

Also let me know whether scope hierarchy would matter for how you work: a change to a whole class not matching a change to one of its methods. It’s the biggest gap our own replay showed, and I’d rather build it from real cases.

Collapse
 
naw103 profile image
Nick Woodhead •

If you’ve hit the replace-versus-extend failure (two agents, both correct, jointly wrong), I’d love to hear how you caught it. In our case it was the merged tests, a day later.

Collapse
 
autenai profile image
Auten •

To your question: in our setup the expensive collisions were never in the repo. We run three agent sessions in parallel on one machine, and the two that cost the most time were:

  1. Duplicate work. Two sessions picked up the same task from a shared notes file, because "read the notes first" was advice, not a gate. What fixed it was an explicit claim step: a task is claimed (who, when) before any work starts, and a stale claim has to be taken over visibly, not silently redone. Your intent publish is the same idea one level up, with scopes instead of task ids.

  2. Shared resources that are not files at all: one screen and mouse, one logged-in browser profile, one test phone. Worktrees give each agent its own checkout, but if two agents drive the same GUI, one types into the other's window. We ended up with a lease (acquire before touching the GUI, release after, a holder that goes quiet for ~30 s counts as dead) rather than a merge-time check, because by merge time the damage is a sent message, not a diff.

So I'd add "anything with side effects outside the repo" to your list of things worktrees don't isolate. On scope hierarchy: a class-level replace matching every method-level scope under it sounds like exactly the pair your PaymentService example needs.

Collapse
 
reidmarlow profile image
Reid Marlow •

The collision that costs me the most time across parallel sessions is contract drift on shared test fixtures and mock generators.Two agents get tasks on separate modules that both depend on a common factory or schema definition. One agent refactors a mock helper to satisfy a new nullable field, while the other updates an integration test assuming the old fixture signature. Neither touches the other's source files, Git merges the branches cleanly, and the failure only surfaces when the full test suite runs on the merged branch.The second messy failure mode is lockfile churn. Even with separate worktrees, if two agents add different packages around the same time, merging their pnpm or poetry lockfiles creates merge conflicts where automatic resolution silently drops dependencies or scrambles transitive pins. Enforcing that only the primary integrating branch runs package additions stopped the lockfile corruption.