We build GPTree with several coding agents working the same repository at once: Claude Code, Codex, and Cursor, each in its own git worktree. The failure that finally made us build tooling for it was small and completely silent.
One session was told to replace PaymentService with a Stripe-specific implementation. Another was told to add PayPal support to PaymentService. Different worktrees. Different files. Zero textual conflict. Git merged both branches cleanly, and the second change now depended on an extension point the first had deleted. Nothing in the toolchain had an opinion about it at any moment.
Git compares diffs. It cannot compare plans.
Worktrees isolate files, not plans
Worktrees became the standard answer to parallel agents for a good reason: two sessions editing one checkout will overwrite each other's files and poison each other's context. Isolated checkouts fix that completely.
But three failure modes survive file isolation, because they were never about files:
- Destructive versus additive. One agent removes or replaces a thing another agent is building on. The example above. Merges clean, breaks the design.
- Duplicate work. Two agents solve the same problem from different angles because nothing assigned ownership. You pay twice and then pay again to reconcile.
- Contract drift. One agent changes an API, a schema, or a config contract while another codes against the old shape. Compiles, runs, disagrees at runtime.
A shared task list helps with the second one, if every agent reads it, every time. Nothing in that setup catches the first or third, because the collision is between intentions, and intentions live in prompts, not in any file a tool can watch.
Declare the work before doing it
Foremerge is the internal tool we built for this, open-sourced this week. It is a coordination protocol that sits above Git: agents declare what they are about to do, before they do it, in a form precise enough to check.
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
Scopes are not file paths. The vocabulary covers symbol, api, schema, config, migration, contract, and more, because file paths miss API, schema, configuration, and cross-language collisions entirely. The operation (replace, extend, and so on) is declared rather than parsed out of the summary, so it does not matter how the agent phrased its plan.
When a second agent declares work on the same scope, deterministic rules compare the declarations and raise a finding while both pieces of work are still plans. This is real output from 0.4.0, captured while writing this post:
{
"kind": "destructive_vs_additive",
"severity": "HIGH",
"scope": { "kind": "symbol", "key": "PaymentService" },
"explanation": "One intent will replace `PaymentService` while the other will extend it; both declare the same semantic scope.",
"suggestion": "Coordinate on a stable `PaymentProvider` contract first, then implement StripePaymentProvider and PayPalPaymentProvider behind it and migrate callers deliberately. This is a heuristic suggestion, not an automatic design decision."
}
The finding names the rule that fired, explains itself, and suggests a resolution. It does not block anyone. Claims in Foremerge are leased and advisory: overlap produces a warning and shared context, never a lock, because two agents can often work the same region compatibly and a lock would serialize work that did not need serializing.
The other half of the protocol is evidence. When an agent finishes, it publishes a ChangeSet, and acceptance is gated on verification that Foremerge runs itself: your named check (a build, a typecheck, a test target you registered) executed against the exact candidate fingerprint. An agent saying "tests pass" is recorded as provenance; it does not satisfy the gate. If the tree changed after validation, the attempt is non-authoritative and the gate says so.
Mechanically it is one Rust binary. The CLI, a local JSON API, and an MCP server are adapters over the same SQLite store, which lives inside your repository's git common directory, which is exactly why worktrees work well with it: linked worktrees share that directory, so every agent in the repo sees the same declarations while keeping isolated files. Local-first, no cloud, Apache-2.0.
The five-minute version
The fastest path is to let your agent set it up.
Paste this into Claude Code, Codex, or Cursor from inside the repository you want to coordinate:
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.
The MCP server gives the agent the full lifecycle as tools: publish intent, claim scope, check conflicts, publish ChangeSets, run verification, record the commit that landed. Set it up once per repository and every agent connected to that repository shares the same awareness.
Doing it by hand instead is the install line,
foremerge init
, and the commands in the README, which walks two throwaway agents into the exact conflict above in under five minutes.
What it deliberately does not claim
This is a pre-1.0, local-first MVP, and the parts it does not do are documented as carefully as the parts it does:
Detection is deterministic and explainable, but heuristic. It can miss synonymous concepts and warn on work that was always compatible.
Claims warn. They never lock files, symbols, or agents.
Passing validation proves that the recorded command passed for the recorded fingerprint. Nothing more.
The store is per-machine SQLite. Shared multi-machine mode does not exist yet.
There is a reproducible benchmark harness in the repo, but no published coordinated-versus-uncoordinated results yet. Performance claims wait for numbers.
The full list is in docs/limitations.md. We think a coordination tool that overclaims is worse than no coordination tool, because severity is the signal an agent uses to decide what to stop for, and a false HIGH is worse than silence.
Where this goes
The protocol is the part we most want feedback on: is the scope vocabulary the right shape, where would the conflict rules warn on compatible work in your codebase, and which collisions would they miss? Issues and Discussions are open.
Repo: github.com/naw103/foremerge
Site and install: foremerge.com
Crate: cargo install foremerge
If you run parallel agents and have hit collisions worktrees could not see, we would genuinely like to hear what they looked like.
Top comments (9)
This is the failure mode worktrees made easy to miss. File isolation fixes the stomp, then two agents can still delete and extend the same contract in clean branches.I like that Foremerge treats the scope claim as a warning with evidence instead of a lock. Locks turn into a queue the minute the repo gets busy.
Thanks Reid. The queue point is exactly why claims stayed advisory so the moment a lock exists, every compatible overlap pays the serialization cost, and busy repos are mostly compatible overlaps. Two details that made warnings workable in practice: claims are leases, so a session that dies or wanders off cannot squat on a scope forever, and an overlap warning carries the other side's intent with it, so the common outcome is both agents proceeding with awareness rather than stopping to coordinate. The gate at the end stays hard (verification against the exact candidate fingerprint), which is what lets everything before it stay soft.
The silent merge is the worst outcome because everything in your toolchain is actively telling you it worked. We hit a version of this last year on an ER pipeline when two jobs started rearranging the same matching schema from opposite ends, and the actual problem only showed up when records started dropping in production about three days later. The scope-based intent declaration is exactly the missing layer I'd been wishing existed after the second or third time git said everything was fine and clearly wasn't. Does the conflict check gate the second agent from proceeding, or is it purely advisory right now?
Your production story is exactly the failure class we are building for. The protocols "schema: " and "contract: " exist precisely because file paths never catch two jobs rearranging the same schema from opposite ends.
To answer your question: both, at different points. At declaration time it is purely advisory. The second agent gets the finding in the same call (the rule that fired, an explanation, a suggested resolution), claims never lock, and nothing stops it proceeding. That is deliberate since plenty of overlaps are compatible, and a lock would serialize work that did not need serializing.
The hard stop is at acceptance: a ChangeSet cannot be accepted while an unresolved HIGH finding stands, unless a human deliberately overrides with --allow-high-conflicts and a stated reason, which lands in the audit trail.
The shorthand we have started using is soft claims, hard gate. Warnings while you work, evidence to finish. The second agent can proceed into the wall with full knowledge, but the wall is real.
The PaymentService example is the cleanest statement of the problem I have seen: zero textual conflict, both branches merge, design broken. Git compares diffs, not plans, and worktrees only ever isolated files.
Declaring operations (replace vs extend) instead of parsing them out of prose is the right call, since agents phrase the same plan ten different ways. The question that decides whether this holds up in practice: what happens when an agent drifts from its declaration mid-task? Sessions regularly start with "add PayPal support" and end up refactoring the base class on the way. Is foremerge advisory at declaration time only, or do you re-check the actual diff against the declared scopes before merge? The second is where contract drift actually gets caught.
You have landed on exactly the question this week's threads converged on, and the honest answer is: today, the second check does not exist. Declaration time is advisory (findings arrive in the same call, claims never lock), and the acceptance gate checks three things: a clean tree, no unresolved HIGH finding, and a named verification command executed against the exact candidate fingerprint. The ChangeSet's affected files and symbols are agent-supplied provenance, nothing currently derives touched symbols from the diff and compares them to the declaration.
One nuance worth noting though is that mid-task drift is inside the validated fingerprint, so the executed check ran against the real tree, refactored base class included. The evidence covers the actual code. What is missing is the conformance comparison between declared and touched.
That comparison is now the top of the roadmap as a scope_drift finding: derive touched paths and symbols from the candidate diff at ChangeSet publish, compare against declared scopes, and raise a finding that can gate acceptance. A Reddit commenter effectively specified it this week, and your "start with add PayPal support, end up refactoring the base class" case is precisely its acceptance test. Until it ships, the honest position is the documented one: declarations are the map, verification is the territory check, and the map is not yet audited against the territory.
The PaymentService collision is the exact failure that makes worktrees feel finished when they are not. Two agent sessions, two worktrees, a clean merge, and one change still depending on an extension point the other deleted. Git compared the diffs. It never saw the plans. Declaring symbol scopes before the edit is the first check I have seen that catches destructive versus additive work while both are still intentions. How noisy do the advisory findings get once three agents share one busy repo for a whole day?
Findings fire only on declared scope collisions, never on proximity guesses, so quiet agents on disjoint scopes generate nothing. The severity ladder does the triage: HIGH is reserved for two declarations that cannot both be true, MEDIUM for likely rework, LOW for shared-context awareness, so a busy day accumulates mostly LOWs you can ignore in bulk. v0.4.0 exists specifically because the noisy version taught us that inferring operations from prose produced confident false HIGHs, so operations became declared syntax, and the repo now carries a regression fixture where compatible work raising a HIGH fails the build.
recently started using zuse.sh the best so far.zu