Shared Git state is the fleet coordination bug
Git worktrees isolate working files, but they do not make every part of Git private. An agent can write a rule that changes how every task in the repository behaves, even when the change looks local to its own lane.
I saw this in a busy agent fleet. Tasks were appending per-task paths to .git/info/exclude, assuming the file belonged to the current worktree. In linked worktrees, that exclude file is shared repository state. One task’s ignore rule therefore changed what other tasks could see and add. A separate runner snapshot failure was eventually traced to a tracked ignore rule; the shared exclude behavior was not that root cause. Treating every symptom as one bug would have sent us to the wrong fix.
The same development period exposed other shared Git resources. Autocommit and queue pushes raced over refs and index state. A non-atomic push could update one mirror ref while leaving another behind. A merge autocommit carried conflict markers into the shared branch and needed a follow-up repair. These were related by shared mutable coordination state, but they had distinct failure mechanisms: ignore scope, snapshot classification, writer contention, multi-ref publication and conflict validation.
Worktrees isolate files, not the whole control plane
A Git worktree gives each task its own checked-out directory and HEAD. Git still has common metadata for the repository, including refs, object storage and parts of its config. Repository-level excludes are an easy example because they look like a local convenience file. A task can add a path to its own view of “untracked clutter,” while another task suddenly stops seeing a file it needs to stage. The resulting failure can look like a build problem, a missing artifact or a stuck lane.
I now ask which state is actually private before giving agents parallel write access. The filesystem checkout may be isolated while the index, refs, exclude rules, locks or remote branch are shared. If the task requires shared Git mutation, I treat that surface like a queue or database: define who can write, serialize incompatible operations and make publication atomic where possible.
Separate the failure modes before fixing them
The snapshot failure had a tracked .gitignore as its root cause. The runner needed to distinguish ignored paths from missing output and avoid treating one task’s local result as a fleet-wide failure. The repository-level exclude entries were a fleet-wide hazard, but removing those lines did not resolve the tracked ignore rule. They were separate layers.
A different failure involved autocommit publishing conflict markers. A merge can produce a syntactically valid commit object while leaving conflict text in source. A green command or successful push does not prove the tree is semantically clean. The repair removed the bad changes and added checks around the merge path. The useful lesson is to inspect the resulting tree after integration, not only the merge command’s exit status.
A third problem was publishing two refs to a mirror without atomicity. If the remote accepts one ref update and rejects another, the mirror no longer represents one consistent release. Switching to an atomic push makes the operation all-or-nothing for those refs. A separate queue/autocommit race needed lock coordination and backoff, not atomic push alone.
Give every shared writer an explicit protocol
For paths that change shared refs or indexes, I want a single writer lock or a protocol that prevents incompatible writers from overlapping. The queue should fetch the current remote state, attempt a fast-forward, and stop on real divergence rather than improvising a merge in the live checkout. Autocommit should yield while the queue holds its lock. Conflict resolution and merges belong in isolated worktrees, where the resulting tree can be reviewed before publication.
For multi-ref publication, an atomic push prevents partial mirror updates. For tree validity, a separate post-merge check should reject conflict markers and verify the expected files. For snapshots, ignored and untracked are different states and need explicit handling. Each guard addresses one failure mode; a catch-all “Git is isolated because we use worktrees” assumption addresses none of them.
Make the blast radius visible
An agent task should own its branch and worktree, but the orchestrator should own the shared control plane. That includes when a task may publish, whether another publisher is active, what remote state it is based on, and which paths may be globally ignored. Keep per-task scratch under task-owned directories rather than writing task-specific policy into repository-wide configuration. If global policy must change, make it a reviewed change with a clear owner and an explanation of how every active task is affected.
I also separate “one task failed” from “the fleet is unhealthy.” A failed snapshot should pause or retry the task whose evidence is missing, while a broader stop requires evidence that a shared invariant is broken. Otherwise one missing result can halt unrelated work, and the retry merely repeats the same failure.
The architecture lesson is that Git is not a distributed lock manager. Worktrees help isolate edits, but refs, indexes, excludes and pushes still need coordination. Map the actual shared state, assign writer ownership, and test each race with the other writer active. Which Git resource in your agent setup is shared even though each task has its own directory?
Give shared Git resources explicit owners
A worktree is one isolation primitive, not a complete concurrency model. Before launching parallel agents, list the resources they can still mutate together: common config, repository-level excludes, index locks, refs, remotes, credential helpers and branch publication. For each, decide whether writes are isolated, serialized or made atomic. Avoid letting a task add its private scratch paths to a shared exclude file; use a worktree-local mechanism or an explicit ignore contract instead.
For publication, treat a group of related refs as one transaction when the remote supports it, and read back the resulting refs after the push. For merges, validate the resulting tree and scan the exact changed files for conflict markers; a successful command exit only proves the command returned success. For autocommit, give one writer ownership of the index and branch at a time, or have writers create separate commits that a single integration lane applies.
Make shared resources visible in the handoff
The resource map should be concrete enough that a reviewer can identify which operations are safe to parallelize. Two workers editing different files may still share a repository-level exclude file. Two isolated worktrees can contend on the common index lock or race to update the same branch. A push can publish a stale tree even when each local checkout looks clean. These are different shared objects, so a single “use worktrees” instruction does not resolve them.
I describe each writer operation in terms of ownership: who may mutate the resource, what lock or atomic compare protects it, and what readback proves the update landed. For example, an atomic ref update prevents replacing a newer value, but it does not serialize a non-atomic edit to another file. A post-merge tree comparison detects unexpected content, but it does not stop a competing process from changing the target during the check. The protection has to match the shared state.
This turns fleet coordination into a distributed-systems problem. Processes need a clear owner, a versioned input, a bounded lock or compare-and-swap, and a durable result that the next worker can observe. If an operation cannot acquire ownership, it should wait or fail with the current state; it should not “help” by forcing a reset. The handoff packet should carry base commit, intended paths, validation result and next owner so another agent can continue without guessing.
These controls address different hazards. A lock does not make a partial multi-ref push atomic. An atomic push does not stop two processes from racing over an index. A clean tree comparison does not prove that the intended tests ran. Map each failure to its own guard and evidence. What shared Git metadata can one of your worktrees change for every other agent?
How this was verified
I checked the atomic-push change and the conflict-marker repair at pinned Git commits. The snapshot-root-cause and worktree event details come from the source dossier. Counts and outage durations are omitted because the available evidence was assistant-reported rather than independently pinned here.
