Back to news

How to merge work from multiple AI coding agents without breaking main

Short answer: integrate serially, on an integration branch separate from main, with an automated gate (tests, lint, static analysis) deciding whether each merge exists, and never let an AI coding agent merge its own work. Parallelism happens while code is produced; integration is serial by nature, and accepting that fact is what keeps main unbroken.

Why integration is where parallelism sends the bill

Three AI agents produce three branches that pass tests separately and still break together: one renamed a method another one calls, two touched the same routes file, the third assumed a schema that changed. Git flags textual conflicts, but the semantic conflict (code that compiles and does the wrong thing) only shows up when the work meets. The more agents in parallel, the more expensive every unintegrated hour becomes.

The recipe: an integration branch and serial merges

The arrangement that has proven stable: main receives only what was already integrated and validated; an integration branch (call it stage, develop, the name does not matter) receives the merges; and each AI agent works on its own branch, cut from integration, preferably in a git worktree so they do not even share a directory. The site you are reading was built this way: 59 merges of agent branches in three weeks, measured in the repository itself in August 2026, none of them straight into main.

The gate: the merge only exists if verification passes

A gate is a single script that runs the test suite, the formatter, static analysis and the build before any merge, and blocks the merge if any step fails. The point is being binary and automatic: no "almost everything passed". With AI coding agents the gate matters more than with humans, because an agent is overconfident about its own diff and will declare done what is not.

What does the gate script actually look like?

Ours is longer, mostly because of readable error messages and flags to skip the slow steps, but the whole idea fits in ten lines of shell. This is the simplified version of the script that guards merges in this site's repository:

#!/usr/bin/env bash
set -euo pipefail

php artisan test --compact             # the whole suite
vendor/bin/pint --test                 # style, checks only, mutates nothing
vendor/bin/phpstan analyse             # static analysis
if ! git diff --quiet origin/stage... -- resources/ package.json; then
  npm run build                        # only when the diff touches the front end
fi
composer audit --no-scripts            # known CVEs in PHP dependencies
npm audit --audit-level=high           # known CVEs in JS dependencies

echo "gate passed"

Two details matter more than the list of steps. The gate only checks, it never fixes: a formatter that rewrites files during verification hides the problem instead of reporting it. And every step exits non zero on failure, which with set -e is what makes the result binary rather than a report someone has to interpret.

Who should do the merge, you or the AI agent?

The agent proposes, the human integrates. Letting an AI agent merge its own work puts the author and the judge in the same head, and the author's bias survives the author's review. It extends to code review too: reviewing with a fresh agent, one that did not produce the code, finds what the author cannot see. A merge is a decision, not a step in a script.

What about two AI agents finishing at the same time?

A queue, and the mechanism is more interesting than it sounds. The first one integrates; the second updates its branch with the first one's result, runs the gate again, and only then integrates. The queue serializes exactly the step that needs to be serial, and nothing else: code production stays parallel the whole time.

Where the queue lives is the part people get wrong. It cannot live in the repository, because a file committed on one branch does not exist on the other, and each agent is sitting in its own branch and its own worktree. So the state goes in a shared directory outside git: one file listing who is waiting, another naming the current conductor. Whoever enters first becomes the conductor and merges; everyone else reads that they are waiting and stops. When the conductor finishes, the same script promotes the next in line, which is what decides the order: arrival, not seniority, not who shouts.

The one step that needs a lock is entering the queue versus electing the next conductor, since two agents doing both at the same instant could each believe they are the conductor. Everything else can be a plain file, and plain files are why this survives a machine reboot in the middle of an integration.

How much does serial integration cost?

The cost is real: with a gate taking a few minutes per round, integrating five branches is half an hour of verification alone, and the second in line waits for the first. The alternative costs more: a broken main for every agent at once, and a whole afternoon of archaeology to find which combination of merges broke it. Half an hour of waiting is a price you can predict; the afternoon of archaeology is not.