engineering· May 7, 2026· 8 min read

Parallel agent worktrees need a context contract, not just isolation

Running coding agents in parallel worktrees fails at the merge boundary, not in the agents themselves. A short written contract — scope, interface, shared state, merge order — turns isolated work into coherent diffs.

Parallel agent worktrees need a context contract, not just isolation

You have three coding agents running in three separate worktrees. Agent one is finishing the upload endpoint. Agent two is wrapping the schema migration. Agent three is filling in the test file. Each one reports green. Each one hands you a clean diff. You merge them in order, run the suite locally, and watch a runtime crash neither agent ever saw.

This is not the agents failing. Each agent did the job it was asked to do, on the slice it could see. The failure happened in the seam between them — in the merge, where two correct local decisions added up to one broken global state. Worktrees gave you isolation. Nothing in your loop gave you coordination.

The fix is a context contract: a written, explicit agreement about what each parallel agent owns, what it cannot touch, and how its output gets merged back. Without one, parallel agent work is expensive serial work with extra steps and a worse outcome.

Three parallel agent worktrees with a context contract document defining scope, interface, shared state, and merge order

The unspoken assumption about parallel agents

The pitch for parallel agent workflows is straightforward: split the feature into independent slices, run an agent on each, finish in a fraction of the wall-clock time. In practice the slices are rarely as independent as the pitch implies.

Take a concrete case. Two agents are asked to improve the auth layer. Agent A hardens the middleware. Agent B refactors the user type so a new field can be added later. Each works in its own worktree, runs the test suite, and comes back green.

Agent A renamed requireAuth to requireAuthenticatedUser because the old name was ambiguous. Agent B changed the shape of the user type from { id, email } to { id, identity: { email } }. Both diffs pass their local tests because each agent regenerated only the tests inside its scope. When you merge them, the middleware now references a function name that exists, but the function expects a user shape that no longer exists. The runtime sees a Cannot read properties of undefined (reading 'email'). Neither agent ever ran a build that contained both diffs.

This is the canonical failure mode. It is not a model problem. It is a coordination problem, and you are the only thing in the loop that can solve it.

Worktrees give isolation, not coordination

git worktree add ../wt-uploads agents/uploads gives each agent a fully independent working tree backed by the same object database. They can edit, commit, and run tests without interfering on disk. That is the entire feature, and it is genuinely useful.

Isolation is not coordination. Isolation says the agents will not stomp on each other's working files in real time. Coordination says the agents produce diffs that combine into a coherent whole. Worktrees buy you the first. The contract buys you the second. If you do not write the contract, you are paying for parallelism without paying for the thing that makes parallelism work.

The four parts of a context contract

A context contract is a short markdown file that lives in the repo, gets handed to every agent at the start of its run, and contains four sections. Treat each one as load-bearing.

Scope

Scope names exactly which files, directories, or modules each agent owns. Anything outside the named scope is read-only. The agent may read it for context, but may not edit it.

The crisper this list is, the better the run goes. agent-1 owns src/api/uploads/ is enforceable. agent-1 handles the upload feature is not, because "the upload feature" eventually expands to include validation, error types, the storage adapter, and a config flag, and now agent-1 is editing files agent-2 also depends on.

Interface boundary

Inside its scope, an agent has freedom. At the boundary, it does not. The interface boundary is the public API surface each agent commits to: function signatures, exported types, route paths, schema shapes, env var names. Internal implementation can change freely. Anything that crosses the boundary requires a sync step.

This is the section that prevents the auth example above. If agent A had committed to "the exported requireAuth function name and (req, res, next) signature do not change," the rename would never have happened, or it would have surfaced as a contract violation before any code was written.

Shared state

Some files cannot be cleanly partitioned. package.json, tsconfig.json, .env.example, the OpenAPI spec, the schema migration directory, the root README. Two agents touching any of these will produce a merge conflict or, worse, a silent overwrite.

Pick one of two policies for every shared file: either ONE agent owns it outright (and the others must request edits through that agent), or NO agent edits it (only the human merge step does). Mixing policies file by file is fine. Leaving the policy unspecified is not.

Merge order

The merge order is a single line, written before any agent runs: agent-1 lands first, then agent-2, then agent-3. Pick it deliberately, based on which diffs unblock the others.

Without a merge order, the order ends up being whichever agent finished last, which is the worst possible heuristic — the last agent to finish has been working against the longest-stale view of main. With a merge order, every agent after the first one rebases against a known good state before its diff is reviewed.

A worked example

A feature splits cleanly into three concurrent slices: a new uploads endpoint, the schema delta it needs, and the integration tests for the endpoint. Three worktrees, three agents, one contract.

# Context contract — feature/uploads

## Worktrees
- wt-uploads     branch: agents/uploads     agent: agent-1
- wt-migrations  branch: agents/migrations  agent: agent-2
- wt-tests       branch: agents/tests       agent: agent-3

## Scope
- agent-1: src/api/uploads/**, src/api/router.ts (additions only)
- agent-2: src/db/migrations/**, src/db/schema.ts
- agent-3: tests/uploads/**, tests/fixtures/uploads/**

## Interface boundary
- agent-1 commits to: POST /api/uploads, body { filename, mimeType, sizeBytes },
  returns 201 { uploadId, url }.
- agent-2 commits to: uploads table with columns
  (id uuid pk, filename text, mime_type text, size_bytes bigint, created_at timestamptz).
- agent-3 reads the contract; does not define new contract surface.

## Shared state
- package.json: agent-1 owns. agent-2/3 must request additions.
- tsconfig.json: nobody edits.
- src/db/schema.ts: agent-2 owns.
- README.md: nobody edits.

## Merge order
1. agent-2 (migration must land first; agent-1 imports the new table)
2. agent-1 (endpoint depends on schema)
3. agent-3 (tests depend on endpoint)

Each worktree gets created with git worktree add ../wt-uploads agents/uploads and the contract is the first file the agent reads. The contract does not tell the agent how to do its work. It tells the agent where the edges are.

What breaks the contract

Four failure modes are worth naming, because they recur.

Silent scope expansion. The agent finishes its assigned work, notices a related issue in adjacent code, and fixes it as a courtesy. The diff now touches files outside scope, and you do not see it until the merge. Counter this in the prompt: "If you need to touch a file outside your declared scope, stop and report it. Do not edit it." Most modern coding agents respect this when it is stated as a hard constraint.

Cross-worktree dependencies. Agent-1 finishes a function that agent-3's tests need to import. Agent-3, working in parallel, cannot see that function yet because it has not been merged. The merge order document is what catches this — agent-3 is scheduled last for exactly this reason. Agents do not infer ordering on their own; you have to set it.

Schema migration drift. Two agents each generate a migration with timestamp 20260506120000_*.sql. Both work locally. Both fail on whichever order you try to merge them. Solve this in the contract by giving migration ownership to exactly one agent, or by assigning explicit numeric prefixes in advance.

The helpful refactor of shared config. The agent decides that tsconfig.json could use stricter noUncheckedIndexedAccess, or that package.json would be cleaner if a dev dependency were promoted. These edits never look harmful in a single diff. Stacked across three worktrees, they conflict instantly. The shared-state section exists to make these edits illegal by default.

The merge ritual

When the agents are done, the merge is a procedure, not a vibe.

  1. Review each worktree's diff in isolation, against main at the time of the contract. Each diff should be readable on its own — if it is not, the scope was too broad.
  2. Merge in the contracted order. Run the full test suite after each merge. Do not skip ahead just because the next diff looks small. The point of running tests after every merge is to localise any failure to the merge that introduced it.
  3. If a diff crossed a boundary it should not have, reject it. Re-prompt the agent with the boundary as a hard constraint and a specific list of the files it touched outside scope. Do not patch the violation by hand — the next run of that agent will reintroduce it.
  4. After all three merges, run the full suite one more time on the integrated branch. This is the test the individual agents could not run, because none of them ever had all three diffs in their tree.

This procedure is unglamorous. It is also the difference between parallel agent work that ships and parallel agent work that produces a pile of conflicting diffs you eventually rewrite by hand.

When not to parallelise

Parallel agents have a fixed coordination cost: writing the contract, running the merges, fixing boundary violations. That cost is roughly constant. The benefit scales with the size of the work. The math only works above a threshold.

Skip parallelism when the work is small enough that one agent finishes it in under two hours. Skip it when the modules are tightly coupled and the interface boundary would be redrawn every fifteen minutes. Skip it for anything touching the build, the CI config, or the deploy pipeline — those surfaces are too shared to partition.

Below the threshold, one agent doing the whole job is faster, cheaper, and produces a cleaner diff. Above it, three agents with a contract beat one agent every time. The skill is knowing which side of the line you are on before you start.

The contract is the part most teams skip, because it feels like overhead in front of the real work. It is not overhead. It is the work. Without it, you do not have parallel agents. You have three agents racing to produce the merge conflict that decides the project.


Related in the StoicSoft network

If you regularly stitch together PDF, image, video, or batch-file workflows like the ones above, 1FileTool is the StoicSoft network's purpose-built desktop app — 245+ local-first tools, pay-once, files never leave the device.

If you work in AI-assisted coding, shared terminal sessions, or agent-driven shell workflows like the ones above, 1devtool is the StoicSoft network's tool for safer AI-assisted terminal work — shared sessions with auditing, preflight policy, and tiered model routing built in.

multi-agentworktreesmerge-strategyai-workflowparallel-agents