how to prevent merge conflicts between AI agents.
published
to prevent merge conflicts between AI coding agents like Claude Code, Codex and Cursor, stack defenses that avoid, flag or catch them: split work by file, give each agent a worktree, claim files, warn before each edit, predict conflicts with git merge-tree, merge through a queue, and test the merged result. teammates’ agents need a shared record of edits.
how parallel agents overwrite each other’s changes
two agents working at once collide in three ways, and each defense below covers some of them.
- one checkout, one file. an agent reads
src/models/user.ts, a second agent writes to it, and the first agent’s next edit is built on what it read. that edit either fails to match or quietly puts the old lines back, whether as an edit built on stale text or as a whole-file rewrite from the copy it read. - two branches, one file. each agent has its own checkout and changes the same lines. git reports the conflict when someone merges, after both agents have finished.
- two files, one contract. alice’s agent changes the signature of
createUserinsrc/models/user.ts. bob’s agent, insrc/api/handlers.ts, keeps calling the old one. no line overlaps, git merges cleanly, and the build breaks. that third kind is a semantic merge conflict.
a few studies have measured how often this happens, each counting something different; how often AI coding agents cause merge conflicts puts them side by side.
how to avoid merge conflicts with multiple coding agents
the list runs from planning to after the merge. the early defenses act while an agent can still pick different work. the late ones look at real code, so they’re more certain, and what they find is rework. every tool named here is described from its own documentation or source, checked on 2026-10-16.
| defense | acts | catches | misses | setup |
|---|---|---|---|---|
| 1. split by file or module | before work starts | two agents in one file, while the split holds | shared files, drift, breaks across files | a CODEOWNERS file and tasks written around it |
| 2. a worktree per agent | when an agent starts | two agents writing one file on disk | one file on two branches, teammates’ machines | a flag per session, setup per checkout |
| 3. claim before editing | before the first write | two plans for the same paths | agents that don’t claim | an MCP server and an instruction |
| 4. warn before each edit | at each edit | agentmash: a teammate’s change to the file, or (JS/TS, from an agent that reports the names it changed) to a symbol it imports; Clash: your other local worktrees | agentmash: your own agents; Clash: teammates’ machines; both: shell commands | a hook in each agent |
5. git merge-tree | while branches are in flight | textual conflicts between committed branches | uncommitted work, semantic breaks | git 2.38 and a script, or Clash |
| 6. merge through a queue | at the merge | pull requests that pass alone and fail together | what the checks don’t test | a protection rule and merge_group, where GitHub or GitLab offers you a queue |
| 7. build and test the result | in the queue and after the merge | semantic breaks the tests reach | seams no test covers | CI time |
1. split the work along file and module boundaries
before any agent starts, give each agent, and each developer, a part of the repository for the task: a module, a directory, a list of files. the Claude Code docs on agent teams say it directly: “Two teammates editing the same file leads to overwrites.” their fix is this one, a different set of files for each teammate.
a CODEOWNERS file is the usual map. GitHub reads it from .github/, the repository root or docs/, and requests a review from the owners of the code a pull request changes, though not while the pull request is a draft (about code owners). that routes review and doesn’t stop an agent from editing a path someone else owns, so write the split into each task’s prompt too, since the prompt is what the agent reads.
# .github/CODEOWNERS
/src/models/ @alice
/src/api/ @bobkeep each agent’s task small, and merge its pull request soon. a small change has fewer lines to collide with, and a branch that merges soon has less time to drift from main while other work lands.
- catches: two agents in the same file, for as long as the split holds.
- misses: files every task touches (a route table, a lockfile, a shared types file), work that grows past its boundary, and breaks across files: bob’s handlers still import alice’s models.
- tools:
CODEOWNERSon GitHub, the task prompt,CLAUDE.mdorAGENTS.md. - setup: one text file and the habit of writing tasks around it. requiring a code owner’s approval before a merge is a branch protection setting. in a private repository,
CODEOWNERSand branch protection need GitHub Pro, Team or Enterprise.
2. give each agent a worktree
a git worktree is a second working directory of the same repository, on its own branch, and each agent gets its own when it starts. by default git refuses to check out one branch in two worktrees, so two agents can’t end up on the same branch by accident. the agent tools make one for you:
- Claude Code:
claude --worktree feature-authstarts a session in.claude/worktrees/feature-auth/, on a new branch namedworktree-feature-auth(Claude Code worktrees). - Cursor:
/worktreeruns a chat in a separate checkout, and/apply-worktreebrings its changes back to your main one (Cursor worktrees). - the GitHub Copilot app: a session can start in a new working tree, in your local repository, or in a cloud sandbox, which is in public preview (Copilot agent sessions).
for any other agent, make one by hand and start the agent inside it:
git worktree add ../app-alice -b alice/soft-deletes- catches: two agents writing the same file on disk, and the stale read inside one checkout.
- misses: the same file changed on two branches, which still conflicts at the merge; breaks across files; and every worktree on a teammate’s laptop. the guide to git worktrees and agentmash, linked below, walks through each case.
- setup: a flag per session, and a fresh checkout to prepare each time. dependencies have to be installed again, and gitignored files such as
.envcopied: Claude Code copies the ones listed in.worktreeinclude, and Cursor runs the setup commands in.cursor/worktrees.json.
3. claim files before editing
a claim is the one fact a shared record can hold before any edit exists: “my agent is taking src/routes/ for the next hour.” the agent makes it at the start of a task, before its first write. a second agent that asks for overlapping paths learns who holds them, and can take other work.
tools differ on what happens next. a lock refuses the second agent. an advisory claim tells it and lets it decide.
a lock held by an agent that crashed stalls everyone waiting on it, so each tool below keeps claims advisory, gives them an expiry, or both. Befall and agentmash each keep a room per repository, a record on a shared server that every teammate’s agents report to.
| tool | what an agent claims | when two claims overlap | who shares the claims |
|---|---|---|---|
| Foremerge | an intent with semantic scopes, such as symbol:PaymentService | both succeed; colliding plans raise an advisory finding | the agents on one machine, through a store under .git |
| Befall | path globs, over MCP (README) | refused at claim time; the agent is told who holds it and a file both claims cover | teammates, in a hosted room per repository |
| Shepherd | the work it’s starting, with its work tool, until done or a TTL (60 minutes by default) | both claims succeed; work reports the overlap as an advisory conflict (source) | teammates, through a hub the team runs or Korso hosts (README) |
| agentmash | paths and a one-line task, with the claim_work MCP tool | both stand; the second agent is told who holds what, and to split the work | teammates, in one room per repository |
Claude Code’s agent teams, an experimental feature that is off by default (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 turns it on), have claiming too, for the tasks on their shared list, with file locking so two teammates can’t take the same task. that lock covers the task list; two teammates can still edit the same source file. how Claude Code agent teams compare with agentmash goes through the rest.
in agentmash, an agent calls claim_work with the paths it expects to change. this is what dana’s agent is told when alisher’s agent already has part of them:
Claimed src/routes/login.ts, src/routes for "add a login endpoint" as dana@example.com
(id claim_x9…, 120 min).
OVERLAP — someone else has already claimed part of this:
- alisher@example.com has src/routes for "rename every route handler" (claimed 4 min ago)
Your claim stands, but split the work before you write anything: take the files and symbols
they have not claimed, or build on top of what they land rather than changing it. …an agentmash claim is advisory, never a lock. on the agent’s own branch it stands until the branch merges, a week at most. on the default branch it expires on its own, after two hours by default and eight at most, and ends with the session that made it.
an agent about to edit a file a teammate claimed gets an [AgentMash: CLAIMED] note, delivered the way that agent hears the warning in defense 4. how claims work has the rest.
- catches: two agents planning the same files, before either writes. Foremerge’s semantic scopes also catch two plans that meet in different files, when both agents declare the same scope, such as
symbol:PaymentService. - misses: an agent that doesn’t claim, because claiming is a protocol the agent follows when its instructions say so; work that drifts past what was claimed; and which lines inside a file each agent will change.
- setup: an MCP server registered in each agent, and an instruction to claim at the start of a task (agentmash’s MCP server carries that instruction itself). in Codex, Cursor and GitHub Copilot,
npx agentmash loginregisters agentmash’s MCP server too: Cursor asks you to approve the server when you restart Cursor, and for GitHub Copilot in VS Code, login adds it whencodeis on your PATH and otherwise prints thecode --add-mcpline to run. in Claude Code the login adds hooks only, so claims and the MCP tools come where a repository registers agentmash’s MCP server:npx agentmash initcommits that entry in.mcp.json, and each teammate approves the server once (in a repository connected on the website, init stops and says how to commit one anyway). the login alone gives the warning before each edit and reports each edit to the room.
4. warn the agent before each edit
a hook runs before the agent’s edit tool, at the last moment the agent can still change course. it looks up what else is changing and puts what it finds into the agent’s context. it doesn’t depend on the agent remembering to ask, which is the gap claims leave.
Clash ships a Claude Code plugin that checks a file against your other local worktrees before Claude writes it. agentmash’s hook checks the repository’s room instead: if a teammate’s agent changed the same file, or changed an exported symbol the file depends on and reported the names it changed (Claude Code’s edits do; a Codex patch and Cursor’s edits don’t), the warning (agentmash calls it an advisory) goes into the agent’s context, graded by severity. this is what bob’s agent is told when alice’s agent changed the file two minutes earlier in the checkout bob is working in, while she is still at it, word for word:
[AgentMash: ACTIVE COLLISION] alice's agent is working in src/models/user.ts — last write 2 min ago on your branch (main), while working on 'add soft deletes to the user model' (+12/-3 lines, touching deletedAt). Their write is already in your working tree, so whatever you read from this file earlier is out of date. Re-read src/models/user.ts now and rebuild this edit against what is actually there: a stale old_string either fails to match or quietly reverts their change. Leave deletedAt named and shaped as alice left it — that is what they just changed. If your task genuinely needs those same lines, change them and say so in your reply, so the overlap is visible.the agent is told to re-read the file and rebuild its edit. a change that isn’t in your working tree, on another branch or in a teammate’s own clone, is worded as a PARALLEL EDIT instead: there’s nothing to re-read, so the agent is asked to keep its edit additive, not to reformat or reorder, and to name any divergence in its reply.
how the warning arrives depends on the agent. in Claude Code, Codex and GitHub Copilot it’s a note the agent reads, and the edit goes ahead. Cursor, Amp, Crush, Factory Droid, Gemini CLI, goose, Kilo Code, Kiro, OpenCode, OpenHands and Qwen Code get it as a refusal of the first edit, with the advisory as the reason, and the same edit tried again goes through. the refusal comes at most once per session and file every ten minutes (in Cursor, once per conversation and file).
- catches: a teammate’s change to the same file in the last 30 minutes (the default window), or on a branch of theirs that isn’t merged yet, for up to a week; in JavaScript and TypeScript, when the teammate’s agent reports the names it changed (Claude Code’s edits do; a Codex patch and Cursor’s edits don’t), a teammate’s change in the last 30 minutes to an exported declaration your file imports; and teammates who never claimed anything.
- misses: your own agents, since agentmash compares your edits with your teammates’ and never with yours; a shell command such as
sedor a heredoc, which names no file, so nothing warns before it; a symbol reached only through anexport * frombarrel, a name shorter than three characters, and a match across files that rests only on names every project has, such asconfigorhandler; a symbol change by a teammate whom an advisory for the same file named in the last ten minutes, which waits out that cooldown; and a check that runs past its budget, 1500 ms by default, which lets the edit go ahead unwarned. - setup: for agentmash, on a GitHub repository, someone who can push to it connects it once by signing in with GitHub on agentmash.dev; then each developer who can push to it runs
npx agentmash loginonce per machine and restarts their agent (Codex runs no hook until you approve it once: open Codex, type/hooksand approve agentmash).
5. predict textual conflicts with git merge-tree
while branches are still in flight, before anyone opens a pull request, git merge-tree --write-tree can tell whether two of them conflict. it performs a merge without reading or writing the index or the working tree, and exits with status 1 when the merge has conflicts (git merge-tree). that mode arrived in git 2.38.
git merge-tree --write-tree --name-only alice/soft-deletes bob/user-emailon a small test repository where both branches changed createUser, git 2.39.3 printed the tree the merge would have written, the conflicted file and the reason, and exited 1:
03a19f56d98fd1753aae2c0a9bc151d775cd9a70
src/models/user.ts
Auto-merging src/models/user.ts
CONFLICT (content): Merge conflict in src/models/user.tsto check against a teammate’s branch, run git fetch first and name it as origin/<branch>. Clash runs the same three-way merge between every pair of worktrees on your machine, read-only: clash status shows the matrix, clash watch keeps it current, and clash check <file> looks at one file.
researchers have replayed agents’ merges at scale, though against the base branch rather than a second branch in flight. in one study of AI coding agents’ pull requests that were never merged (still open, or closed unmerged), researchers at UNLV replayed 107,026 merges against each pull request’s base branch and found a textual merge conflict in 27.67% of them (AgenticFlict, Ogenrwot and Businge, AIware 2026). the other side of each conflict could be anyone’s change, a person’s or an agent’s.
- catches: textual conflicts between any two committed branches, before a pull request exists.
- misses: work that isn’t committed and fetched, since a merge-tree reads commits (Clash’s
checkalso flags a file with uncommitted changes in another worktree on your machine, according to its source); two changes that merge cleanly and break together; and a base that moves after the check. - setup: git 2.38 or newer and a few lines of script, or Clash on each machine (
brew tap clash-sh/tap && brew install clash, orcargo install clash-sh). a teammate’s branch has to be pushed before you can check against it.
6. merge through a merge queue
a pull request’s checks test it against the base branch as it was when they ran, so two pull requests can each pass against main and still conflict or fail once both are in. a merge queue tests them in the order they’ll land.
on GitHub, a pull request added to the queue is grouped on a temporary branch with the latest version of the base branch and the pull requests ahead of it. the required checks run on that group, and a pull request whose checks fail, or that conflicts with the base, is removed from the queue (managing a merge queue). GitLab’s merge trains test each merge request against the combined changes of the ones ahead of it.
- catches: pull requests that pass alone and conflict or fail together, before the combination reaches
main. - misses: whatever the required checks don’t test. it also acts last, when every agent has finished.
- setup: on github.com, a merge queue is available in public repositories owned by an organization and in private repositories of organizations on GitHub Enterprise Cloud, and on GitHub Enterprise Server in any organization-owned repository; GitLab’s merge trains need Premium or Ultimate. where you have one, turn on “Require merge queue” in the branch’s protection rules, and add the
merge_groupevent to every GitHub Actions workflow behind a required check. without it, the checks never start and the merge fails. the required checks run again on each queued group.
7. build and test the merged result
this is the one defense that checks a semantic conflict in the code itself: two changes that merge without a textual conflict and break together. it runs in the queue, and again on main after each merge. defenses 3 and 4 can flag the risk earlier, by scope or by symbol; only a build or a test shows the break.
on GitHub Actions, a pull_request workflow runs on the pull request’s merge branch, refs/pull/<number>/merge, so it already tests your branch merged with the base, as long as the pull request has no merge conflict; with one, the workflow doesn’t run (events that trigger workflows). it doesn’t test your branch with a teammate’s pull request that hasn’t landed. the merge queue’s group does, once their pull request is ahead of yours in the queue, and the build on main after each merge is the last check.
the tests have to reach the seam. in the createUser example, if the new signature makes the old call invalid, TypeScript’s type check fails the build in src/api/handlers.ts. in a codebase without a type checker, it takes a test that calls that handler, or a static check that knows the signature.
on tasks built to trigger the problem, researchers at UIUC and Columbia had GPT-5.5 write code against real Django helpers while a scripted parallel change altered those helpers: in 97% of runs (105 of 108), the two changes each passed alone and merged without a textual conflict, then failed together (Passes Alone, Fails Together). the authors say these constructed rates don’t estimate how often it happens in practice. on 834 runs over 417 real, merged Django pull request pairs, run with a GPT-5-class model and graded after the authors corrected their procedure, one showed interference.
- catches: semantic breaks, when the build or a test exercises them.
- misses: anything the tests don’t reach.
- setup: nothing new beyond CI time, and a test suite that covers the calls between modules.
avoiding conflicts with teammates’ agents on other machines
most of the stack runs on one machine or at the very end. worktrees and Clash see the checkouts on your laptop. Foremerge keeps its store under the repository’s .git on one machine, and its README puts coordination between machines outside its scope.
Claude Code’s agent teams keep a team’s config under ~/.claude on the lead’s machine, one team per session. git merge-tree and a merge queue do see teammates’ work, once it’s committed and pushed, which is usually after their agent has finished.
alice’s Claude Code agent, on her laptop, is halfway through src/models/user.ts on alice/soft-deletes, and has just changed the signature of createUser. bob’s agent, on his, is about to edit the same file on bob/user-email. nothing on bob’s machine knows. the conflict shows up when the second pull request merges, or as a broken build if the two changes met in different files.
covering it takes a shared record that both agents report to while they work. Befall, Shepherd and agentmash are three tools that keep one for a team, each described here from its own docs, checked on 2026-10-16:
| tool | where the shared record lives | how an agent hears about a teammate |
|---|---|---|
| Befall | a hosted room per repository | through MCP tools it calls, such as a refused claim or the room’s status; a local daemon also heartbeats each checkout’s uncommitted files, so overlaps with live claims surface for a tool that never claimed |
| Shepherd | a hub the team runs itself, or Korso hosts | through MCP tools it calls (work, sync), with claims that last until done or a TTL |
| agentmash | a hosted room per repository | from a hook before each edit, with nothing for the agent to call; claims and MCP tools on top |
agentmash’s hooks report each edit to the room as it happens, before it’s committed or pushed. so bob’s agent gets the warning from defense 4 before it edits src/models/user.ts, as a PARALLEL EDIT. while her edit is inside the 30-minute window, bob’s agent gets one again before it edits src/api/handlers.ts, since that file imports the createUser whose signature her agent changed.
past the window, the room remembers her unmerged work by person, branch and file for up to a week, until the branch merges, and matches it by file only: bob’s agent still hears of it before editing src/models/user.ts, but not before src/api/handlers.ts. while her changes are uncommitted, the advisory says they’re only on her machine, so nothing bob can fetch shows them yet. once she has committed, it says how to read her side after she pushes: git fetch origin, then git diff HEAD...origin/<branch> -- <file>. that holds for bob in Claude Code, Codex or GitHub Copilot. in Cursor he’d hear less of it: Cursor’s adapter is told only of work from the last 48 hours by default, on a branch he hasn’t worked on, in a file he hasn’t changed since, and without those two notes.
the agents don’t have to come from one vendor. npx agentmash login connects the coding agents it knows on a machine (Antigravity when asked for), so alice in Claude Code and bob in Codex report to the same room. if a teammate’s agent and yours change the same file anyway, in Claude Code, Codex and GitHub Copilot agentmash tells you once, as the agent finishes its turn, whose change each file meets, so a person can look at both before either lands and decide how the two come together.
what agentmash doesn’t do
- it tells an agent before it edits, and the edit goes ahead (in Cursor and the agents like it, when the same edit is tried again); nothing is locked.
AGENTMASH_STRICT=1is the setting that blocks: an edit into a teammate’s change is canceled until the agent acknowledges it with a reason, and that acknowledge step needs a POSIX shell. strict mode has the details. - it never warns your own agents about each other. two of your agents, in two worktrees or one checkout, both report as you, so neither is warned about the other. for them, the rest of the stack still applies: a split, worktrees, Foremerge’s intents, Clash or
git merge-tree, and CI on the merged result. - symbols are read from the JavaScript and TypeScript family only. Python, Go and Rust files are matched by path, so a break across files in them waits for defense 7.
- not every agent hears the warning before the edit. Cline hears it just after the edit, and Antigravity at the start of each turn. the setup page says how each agent hears it, Devin’s and Junie’s included.
- not every agent’s edits reach the room. Continue and Zed get only the MCP tools, so their edits don’t show in the room, and aider, which has no hooks, is seen only through its commits, in a repository set up with
npx agentmash initandnpx agentmash git-hook installin each checkout. - it doesn’t merge, push or open pull requests, and it doesn’t run a textual merge the way
git merge-treedoes. - the room gets who made each edit, file paths, branch names, the shape of each change (its line counts and the names it touches), the first 500 characters of a prompt and an opaque id of the checkout. your code stays on your machine unless the room turns diffs on, or you share a session live, end to end encrypted to the teammates you share it with. what leaves your machine lists each field.
where to start
which defenses fit depends on where the agents run:
- one developer, several agents on one machine: split the work, give each agent a worktree, check branches with
git merge-treeor Clash before opening pull requests, and let CI build the merged result. Foremerge adds intents scoped to symbols. - a team on one repository: add a merge queue on
mainwhere GitHub or GitLab offers you one (see defense 6), and a shared record so each agent hears about teammates’ changes. for agents that claim work over MCP, Befall keeps that record, or Shepherd for a team that wants to run the hub itself. - a team on GitHub that wants a warning before each edit: with nothing for the agent to call, across Claude Code, Codex, Cursor and GitHub Copilot, that’s agentmash, set up as defense 4 describes. set up agentmash for your team.