I’ve been experimenting with running around four coding agents against the same production codebase at once. Working on
@StarkscanCo for
@starknet
The surprising part is that parallel coding is easy. Safely merging everyone’s work is the hard part.
Each agent works in its own Git worktree and is tied to one exact commit. Focused tests,
@QodoAI and
@coderabbitai reviews, and a resettable five-minute review quiet window must all apply to that same commit.
Before merging, we combine the proposed change with the latest approved version of main. We record exactly what produced that result:
main commit + PR commit + combined commit + resulting Git tree
A dedicated bare-metal runner at
@Hetzner_Online tests that exact combined tree. Every run gets isolated systemd resources (see
wiki.archlinux.org/title/Cgr…), its own worktree, Rust build directory,
@PostgreSQL databases, logs, and machine-readable evidence. The runner deliberately has no deployment credentials.
Testing remains parallel. Only after reviews and validation pass does an agent briefly acquire permission to merge
At that moment, the landing controller checks that:
1> main has not changed since validation
2> the PR still points to the reviewed commit
3> the final Git tree is exactly the one that passed
4> the agent still owns the landing lease
This is effectively a compare-and-swap operation built around Git’s atomic push and lease semantics (check
atlassian.com/blog/it-teams/…)
If another agent merges first, the outdated merge is rejected cleanly. We do not throw away every result and start again
When the PR’s own commit has not changed, its focused tests and AI reviews remain valid. We only combine it with the new main and rerun the checks that depend on that combination. If the PR itself changes, all commit-specific evidence is discarded and rebuilt
Deployment is a separate trust boundary. The shared preview environment can deploy only the exact current main, never code directly from a PR. Every deployment publishes the source revision and an attestation describing what actually started.
This may sound like additional process, but the objective is the opposite:
> make collisions cheap
> reject stale evidence automatically
> preserve valid work
> serialize only the few seconds where serialization is unavoidable
The bottleneck has now moved from agents interfering with each other to validation throughput
We are improving that with automatic lifecycle cleanup, sealed pre-migrated PostgreSQL template databases, separate database leases for tests, cargo-nextest inventory checks, and bounded test sharding (see
github.com/nextest-rs/reuse-…).
The next layer is better queue scheduling and visibility, potentially using
@buildkite. But Buildkite would schedule the work, not define correctness. The checked-in Git, shell, Python, PostgreSQL, and systemd contracts remain the source of truth
The safety rules stay simple:
> no retries that hide a real failure
>no shared mutable test database
>no PR code on the shared preview
>no merge unless the exact code being landed was reviewed and tested
My main takeaway: multi-agent software engineering looks less like hiring more developers and more like designing a distributed system
You need identities, leases, immutable inputs, stale-work rejection, narrow critical sections, isolation, and durable evidence.
Once those contracts exist, adding agents starts producing throughput instead of contention