Concurrent Claude Code sessions on one machine: a shared-clone collision problem
Written 2026-08-13, updated 2026-08-21 for sharing with Anthropic. A field report from a team running
3–6 concurrent Claude Code sessions on one workstation against one git working directory. It documents a
reproducible failure, how often the conditions for it arise, three mitigations we built, and what each
one measurably does and does not fix. All three are working code you are welcome to take, improve, or
replace.
The headline is not "concurrent sessions collide." It is that two of our three incidents happened
to sessions that had read the mitigation and followed it correctly, and one of those had deliberately
excluded the contended file from its own commit and still lost its work to another session. That is what
makes this a tooling gap rather than a discipline problem.
And the 2026-08-21 update sharpens it. Our third mitigation removes the collision structurally —
sessions never edit the shared file at all — and over seven days it drove file-bundling on our
highest-contention file from 55% of commits to zero, across 23 fragments from 20 sessions. What remained
was not a design gap. It was that three long-running sessions forgot the convention and edited the file
anyway, despite the rule being in the document every session reads at startup, a session-start check,
and a tool that refuses bad input. A tool can only refuse the calls it receives. That residue is the part
we cannot reach from userland, and it is what we would most like to see addressed at the harness level.
TL;DR — We run 3–6 concurrent Claude Code sessions on one workstation against one git clone.
Sessions commit their own work. Three times, one session's uncommitted edits were swept into a
different session's commit; content always survived, attribution never did, and two were pushed
before anyone noticed. We measured the exposure (37% of all commits touched a single shared file,
~8/day, 55% of those bundled with unrelated work), then built three mitigations and measured each.
The third one works: sessions stop editing the shared file entirely and queue fragments instead.
Over 7 days it drove file-bundling on that file from 55% of commits to 0, across 23 fragments
from 20 sessions, with zero losses.
The residue is the reason we're filing this. Three sessions edited the file directly anyway —
about one every two days — despite the rule living in the document every session reads at startup,
a session-start check, and a tool that refuses bad input. A tool can only refuse the calls it
receives. The harness is the only layer that sees the write itself, and a soft prompt there
("3 sessions have this file queued in an inbox — edit it directly?") would have caught all three.
Two related asks, both cheap: a warning at git add/git commit time when another session has
uncommitted edits to files in your index (this alone would have prevented all three incidents), and
some way for concurrent sessions on one machine to know about each other at all. Working code for
both userland tools is offered below.
Environment
- One developer workstation, Windows.
- One git clone of a shared documentation repo at
~/.claude/team-docs, plus several ServiceNow
workspace directories.
- 3–6 Claude Code sessions open at once, each working a different project, all against those same
directories. This is normal for us, not an edge case — different sessions own different products and
releases.
- Sessions commit to git themselves (with developer approval); the developer pushes.
The essential property: every session shares one working tree and one git index. There are no
per-session checkouts. A file written by one session is visible to all of them the instant it is saved.
The failure, three times
Content was never lost in any incident. Attribution was, and two of the three were pushed before
anyone noticed, so the history is public and unrewritable.
1. 4def7c1 — 2026-05-12
A session ran git add / git commit while a parallel session had staged files it had not yet
committed. git commit commits the entire index, not the paths you added, so the other session's
work rode along. The commit is titled "2.53 Phase 2 SHIPPED" and its diff also contains an unrelated
1.24 audit.
Our response: a written protocol. Run git status --short before staging; read both columns (index
vs working tree); un-stage foreign entries; bundle add + commit + verify into one shell invocation to
narrow the race; verify HEAD afterwards; never rewrite history to clean it up.
2. fbf126e — 2026-08-08. The protocol had a hole.
A session finished editing three files, ran git status --short (clean — nothing foreign staged),
presented its scope to the developer, and waited for approval. In that window a parallel session ran
git add and committed. All three files were swept into "docs(ADR-039): 3B-1c-iv SHIPPED". When
approval came back, git commit answered "nothing to commit, working tree clean."
What the protocol missed: steps 1–4 protect the staging window — they assume the danger is a
foreign file entering your commit. This is the mirror case: your own unstaged edits being taken by
someone else's git add. A clean git status proves nothing about a file's fate, because the working
tree is shared and any session can stage anything in it at any moment.
The corollary we drew: the dangerous gap is edit → commit, not add → commit. Every minute a
finished edit sits uncommitted is a minute another session can adopt it — and a "present the diff, await
approval" ceremony deliberately widens exactly that window.
3. cca8700 — 2026-08-11. And this one had read the protocol.
A session adding a new section to master-work-items.md found a parallel session holding 42 lines of
uncommitted edits in that same file. It did everything the protocol asks: ran git status --short,
read both columns, identified the foreign edits, and deliberately excluded the file from its own
commit so as not to sweep the other session's work.
That restraint worked perfectly and was beside the point. The parallel session's next git add carried
the new section into "docs(PM-9): the 08-11 nightly confirms the flake classification…", pushed before
the first session's own commit landed.
The structural finding, which is the useful part of this report:
When a shared file already contains another session's uncommitted edits, there is no clean way to
commit your own edit to that same file. Staging it sweeps theirs. Not staging it leaves yours
adoptable. Both branches lose something, and "be careful" does not resolve it.
How often the exposure occurs — measured, because three anecdotes have no denominator
Added 2026-08-14. The three incidents above are what we noticed. The question that decides whether
this is worth tooling is how often the conditions for one arise, so we measured the 14 days before we
changed anything (2026-07-31 → 2026-08-13) against our highest-contention file,
master-work-items.md — a work-item tracker every project session updates at close-out.
|
14 days before |
| Repo commits, all files |
286 |
Commits touching master-work-items.md |
106 |
| …as a share of all commits |
37% |
| …that also carried other files |
58 (55%) |
| …that carried 4 or more files |
23 (22%) |
| Active days |
13 → ~8 commits/day against one file |
Be careful what that 55% does and does not say. Most of those bundles are legitimate: a session
updating its work-item row, its ADR and its handoff prompt in one coherent commit. We are not
claiming 58 incidents. We are claiming that in 58 commits this file's authorship was shared with other
work — and that is the population the three confirmed incidents were drawn from. A single file being
touched by ~8 commits a day, across six sessions that cannot see each other, is the exposure. The
incidents are just the times it landed badly enough to notice.
The other number worth stating: 37% of everything committed touched this one file. Whatever
coordination mechanism exists has to be right about that file specifically, far more than about the
repo in general.
Mitigation 1: a written protocol — insufficient, and we can say why
It is documented, sessions read it, and it prevented the first failure mode. It cannot prevent the
second or third, because both turn on a fact no amount of care can change: git status reports that a
file is dirty; it never reports who is editing it or why. You cannot avoid a collision you cannot see.
Two of three incidents happened to sessions that had read this protocol. That is the measurement that
told us to stop writing rules and start building tools.
Mitigation 2: session-claims.js — working code, and its measured limit
An advisory claims registry. Before editing a shared file, a session claims it; at commit, it
releases.
node tools/session-claims.js claim --session project-a-stories master-work-items.md
node tools/session-claims.js who master-work-items.md
node tools/session-claims.js list # everything claimed; flags CONTESTED
node tools/session-claims.js release --session project-a-stories
node tools/session-claims.js prune # drop claims from sessions that died (default 12h)
The design decision worth copying: one file per session, at .session-claims/<label>.json. A
single shared registry file would race exactly like the file it protects — two sessions appending to one
index collide the same way two sessions editing one document do. Because no two sessions ever write the
same path, the registry can never itself become the contention point, and "who holds X" is a lock-free
directory scan. The claims are git-ignored: they are ephemeral facts about one machine's live sessions
and are useless to anyone else.
A CONFLICT result exits 3, so it can gate a script.
What it fixed: collisions became visible. Sessions can now see that a file is held, by whom, and
for how long, and can choose to work elsewhere.
What it did not fix, measured on 2026-08-13: it is advisory. On that day master-work-items.md went
CONTESTED — two sessions held it simultaneously — and nothing stopped either. A third session
(ours) needed to correct two stale rows in that file and simply could not, so the corrections are still
outstanding. Separately, one session's git push carried an unrelated session's commit with it.
Visibility is a real improvement over a rule. It is not a solution.
Mitigation 3: the inbox pattern — built 2026-08-13, and measured over 7 days
The insight: every approach above tries to make it safe for two sessions to edit one file. The
alternative is that they never do.
- Each session writes only to a path unique to it —
master-work-items.d/<session-label>.md —
containing the rows or fragments it wants added. Zero contention, structurally, because no other
session will ever write that filename.
- A short merge step folds in every pending fragment, commits, and deletes them.
Why this is stronger: it converts a long edit window — minutes or hours during which a session
holds a contended file, including the time it spends waiting for a human to approve a diff — into a
short append window measured in seconds. The failure mode requires two sessions to be in the same
file at the same moment; this makes that window almost nonexistent rather than merely visible.
We got our own specification wrong, and found out on day one
The paragraph this section replaces said the pattern suits "row-shaped, appendable content", and we
nearly built it append-only on that basis. That was wrong, and it would have failed the exact case
that commissioned the work.
Of the first four operations real sessions queued, three were corrections to existing rows, not
appends — a story number replacing a needs a story placeholder, a status moving from planned to
shipped. Close-outs mostly revise something a session wrote earlier. And the two updates that were
blocked on 2026-08-13, the ones that motivated building this at all, were both stale flags needing
correction. An append-only inbox could not have expressed any of them.
So the fragment format carries four directives:
| Directive |
Does |
@@ note: <line> |
goes into the merge commit body |
@@ section: <exact heading> |
append at the end of that section |
@@ row: <exact heading> |
append rows to the last table in that section |
@@ replace: <exact existing line> |
replace that line (empty block deletes it) |
Everything anchors on a heading or an exact line, never a line number — and that mattered within
hours. One fragment sat queued while other sessions added content above its target; its anchor resolved
to line 2092, then 2149, then 2151, and landed correctly each time. A line-based format would have
written into the wrong section twice.
The tool refuses rather than guesses. A missing or ambiguous heading, a table row whose column count
differs from its header, a @@ replace: target that is not unique, or content with no anchor skips that
fragment whole and leaves it on disk. One session's typo cannot block another's queued work, and
nothing ever half-lands.
The refusal that matters most: if the shared file already carries uncommitted changes, the merge
refuses the entire run and exits non-zero. The fragment stays queued, costing nothing. That is the case
this report's structural finding said had no clean answer — and the answer turns out to be that not
landing yet is safe once your work is no longer sitting inside the contended file.
Measured: 2026-08-13 to 2026-08-20
|
14 days before |
7 days after |
Commits touching master-work-items.md |
106 |
21 |
| …that carried other files |
58 (55%) |
3 (14%) |
| Merge commits through the inbox |
— |
18 |
| Fragments folded |
— |
23, by 20 distinct sessions |
| Merges folding more than one session |
— |
4 |
| Merge commits that carried a foreign file |
— |
0 of 18 |
| Fragments lost or abandoned |
— |
0 |
Every one of the 18 merge commits contains exactly one file. The bundling that produced all three
incidents went to zero through the pattern — by construction, not by care. Nine of the 23 fragments
landed inside a shared merge commit, which under the old convention would have been nine sequential
edits to one contended file.
It is not airtight, and the way it leaks is the most useful thing we learned
Three sessions edited the file directly anyway — 08-15, 08-18 and 08-20 — despite the rule being in
the standards document every session reads at startup, and despite a session-start check. Roughly one
every two days. The 08-18 one is the worst shape: master-work-items.md bundled with an ADR, a prompt,
the standards document and two tool files in a single six-file commit. Exactly the signature the pattern
exists to eliminate.
Compliance is therefore ~88% (18 inbox merges against 3 direct edits), not 100%, and the residue is
not carelessness in any interesting sense. It is instruction decay: a rule that a session read hours
earlier competes with everything else in its context, and a long session drifts back to the obvious act
of editing the file in front of it. We can write the rule, check it at startup, and build a tool that
refuses bad input — and none of that reaches a session that simply never invokes the tool.
And it cannot cross a machine boundary at all. A second developer's clone has the tool via git pull but their sessions were never told the convention; their commits still edit the file directly, and
one of them was a git merge resolving conflicts inside it. Every mitigation in this report is
per-machine userland scaffolding, and the second developer is invisible to all three.
Honest limits
- It is not a lock. Between the tool reading the file and writing it, another session could write it
too. That window is milliseconds instead of hours, which is the whole of the claim.
- It does not generalise to prose. Deliberately scoped to the work-item tracker, with no
--target
flag. A fragment saying "insert under §X" in a standards document is more fragile than a contested
manual edit. Prose stays on the claims registry — so this is a second mechanism, not a replacement.
- A queued fragment is invisible until someone runs the merge. The inbox directory is git-ignored,
so a session that dies leaves rows git status will never show. We made the read-only dry run a
session-start check to cover it, but it is a real failure mode and it was ours to find.
- The tool needed two safety fixes in its first week, both found by other sessions using it: it
committed the whole index rather than by pathspec (so it could sweep what it existed to prevent), and
it deleted fragments even when the commit failed. Neither lost data. Both are the kind of mistake a
userland reimplementation of a coordination primitive invites.
What we think belongs at the tool level
Offered as observations from the field, not as a specification. Items 6–8 were added 2026-08-21 after
seven days of running mitigation 3, and they are the ones we could not have written before building it.
- Concurrent sessions on one machine do not know about each other. Everything above is us building,
in userland, an answer to "who else is working in this directory right now?" — a question the harness
is much better placed to answer, since it already knows which sessions are open and which files each
has edited.
- The risky moment is
git add / git commit, not the edit. A warning at commit time — "session B
has uncommitted edits to 2 of the files in your index" — would have prevented all three of our
incidents, and requires no locking or coordination protocol.
git commit commits the index, not your paths. This surprises people, and it is the direct cause
of incident 1. A selective git add offers no protection against changes another actor pre-staged.
Our own merge tool shipped with this bug in its first week, in the tool written to prevent it.
- Attribution is the damage, and it is silent. In all three incidents the content survived and every
test passed. Nothing failed loudly. The cost lands later, in git blame and in code archaeology, and
by then two of ours were pushed and unrewritable.
- A "present the diff and await approval" interaction pattern widens the dangerous window. Anything
that encourages holding finished edits uncommitted while waiting on a human increases exposure. We
now prefer committing promptly and presenting the SHA.
- A convention a session must remember is a convention it will eventually forget — this is the big
one. Mitigation 3 removes the collision structurally, and still leaked at about one direct edit
every two days: 18 inbox merges against 3 sessions that simply edited the file. The rule was in the
standards document every session reads at startup, plus a session-start check, plus a tool that
refuses bad input. None of that reaches a session that never invokes the tool. A tool can only
refuse the calls it receives. The harness sees the write itself, which is the only place a
convention about writes can actually be enforced — even a soft "3 sessions have this file in an
inbox; are you sure you want to edit it directly?" at write time would have caught all three.
- Userland coordination cannot cross a machine boundary, and ours silently didn't. A second
developer's clone received all three mitigations via git pull and adopted none of them, because the
conventions live in that developer's session context, not in the repo. Their commits kept editing the
contended file directly and one was a git merge resolving conflicts inside it. Any coordination
built per-machine has this ceiling; a repo-level or account-level signal does not.
- Sessions found real bugs in each other's tools by using them, which is worth designing for. Two
safety defects in our merge tool were caught within a week by other sessions that had only the tool
and its documentation — including the index-vs-pathspec bug in point 3, and consuming queued fragments
even when the commit failed. Concurrency made the tooling better as well as harder; the same sessions
that collide also cross-check.
What you can take
Two pieces of working code, both in daily use, both dependency-free, both about 200–600 lines:
tools/session-claims.js — the advisory claims registry. The one-file-per-session design has held
up completely; it is the visibility layer and still the right answer for prose documents.
tools/merge-work-items.js — the inbox merge tool. Anchors fragments by heading rather than line
number, refuses ambiguous anchors rather than guessing, and refuses the whole run when the target is
already dirty.
Take them, improve them, or treat them as a sketch of the requirement — our interest is in the problem
being solved, not in our solution surviving. If we had to keep one finding, it is point 6: we removed
the structural collision and the residue was purely that a long-running session forgets a rule. That part
is not solvable in userland.
The incident SHAs are ours and the repository is private, but the diffs and commit messages can be shared
on request if the detail is useful.
Concurrent Claude Code sessions on one machine: a shared-clone collision problem
Written 2026-08-13, updated 2026-08-21 for sharing with Anthropic. A field report from a team running
3–6 concurrent Claude Code sessions on one workstation against one git working directory. It documents a
reproducible failure, how often the conditions for it arise, three mitigations we built, and what each
one measurably does and does not fix. All three are working code you are welcome to take, improve, or
replace.
The headline is not "concurrent sessions collide." It is that two of our three incidents happened
to sessions that had read the mitigation and followed it correctly, and one of those had deliberately
excluded the contended file from its own commit and still lost its work to another session. That is what
makes this a tooling gap rather than a discipline problem.
And the 2026-08-21 update sharpens it. Our third mitigation removes the collision structurally —
sessions never edit the shared file at all — and over seven days it drove file-bundling on our
highest-contention file from 55% of commits to zero, across 23 fragments from 20 sessions. What remained
was not a design gap. It was that three long-running sessions forgot the convention and edited the file
anyway, despite the rule being in the document every session reads at startup, a session-start check,
and a tool that refuses bad input. A tool can only refuse the calls it receives. That residue is the part
we cannot reach from userland, and it is what we would most like to see addressed at the harness level.
Environment
~/.claude/team-docs, plus several ServiceNowworkspace directories.
directories. This is normal for us, not an edge case — different sessions own different products and
releases.
The essential property: every session shares one working tree and one git index. There are no
per-session checkouts. A file written by one session is visible to all of them the instant it is saved.
The failure, three times
Content was never lost in any incident. Attribution was, and two of the three were pushed before
anyone noticed, so the history is public and unrewritable.
1.
4def7c1— 2026-05-12A session ran
git add/git commitwhile a parallel session had staged files it had not yetcommitted.
git commitcommits the entire index, not the paths you added, so the other session'swork rode along. The commit is titled "2.53 Phase 2 SHIPPED" and its diff also contains an unrelated
1.24 audit.
Our response: a written protocol. Run
git status --shortbefore staging; read both columns (indexvs working tree); un-stage foreign entries; bundle add + commit + verify into one shell invocation to
narrow the race; verify
HEADafterwards; never rewrite history to clean it up.2.
fbf126e— 2026-08-08. The protocol had a hole.A session finished editing three files, ran
git status --short(clean — nothing foreign staged),presented its scope to the developer, and waited for approval. In that window a parallel session ran
git addand committed. All three files were swept into "docs(ADR-039): 3B-1c-iv SHIPPED". Whenapproval came back,
git commitanswered "nothing to commit, working tree clean."What the protocol missed: steps 1–4 protect the staging window — they assume the danger is a
foreign file entering your commit. This is the mirror case: your own unstaged edits being taken by
someone else's
git add. A cleangit statusproves nothing about a file's fate, because the workingtree is shared and any session can stage anything in it at any moment.
The corollary we drew: the dangerous gap is edit → commit, not add → commit. Every minute a
finished edit sits uncommitted is a minute another session can adopt it — and a "present the diff, await
approval" ceremony deliberately widens exactly that window.
3.
cca8700— 2026-08-11. And this one had read the protocol.A session adding a new section to
master-work-items.mdfound a parallel session holding 42 lines ofuncommitted edits in that same file. It did everything the protocol asks: ran
git status --short,read both columns, identified the foreign edits, and deliberately excluded the file from its own
commit so as not to sweep the other session's work.
That restraint worked perfectly and was beside the point. The parallel session's next
git addcarriedthe new section into "docs(PM-9): the 08-11 nightly confirms the flake classification…", pushed before
the first session's own commit landed.
The structural finding, which is the useful part of this report:
How often the exposure occurs — measured, because three anecdotes have no denominator
Added 2026-08-14. The three incidents above are what we noticed. The question that decides whether
this is worth tooling is how often the conditions for one arise, so we measured the 14 days before we
changed anything (2026-07-31 → 2026-08-13) against our highest-contention file,
master-work-items.md— a work-item tracker every project session updates at close-out.master-work-items.mdBe careful what that 55% does and does not say. Most of those bundles are legitimate: a session
updating its work-item row, its ADR and its handoff prompt in one coherent commit. We are not
claiming 58 incidents. We are claiming that in 58 commits this file's authorship was shared with other
work — and that is the population the three confirmed incidents were drawn from. A single file being
touched by ~8 commits a day, across six sessions that cannot see each other, is the exposure. The
incidents are just the times it landed badly enough to notice.
The other number worth stating: 37% of everything committed touched this one file. Whatever
coordination mechanism exists has to be right about that file specifically, far more than about the
repo in general.
Mitigation 1: a written protocol — insufficient, and we can say why
It is documented, sessions read it, and it prevented the first failure mode. It cannot prevent the
second or third, because both turn on a fact no amount of care can change:
git statusreports that afile is dirty; it never reports who is editing it or why. You cannot avoid a collision you cannot see.
Two of three incidents happened to sessions that had read this protocol. That is the measurement that
told us to stop writing rules and start building tools.
Mitigation 2:
session-claims.js— working code, and its measured limitAn advisory claims registry. Before editing a shared file, a session claims it; at commit, it
releases.
The design decision worth copying: one file per session, at
.session-claims/<label>.json. Asingle shared registry file would race exactly like the file it protects — two sessions appending to one
index collide the same way two sessions editing one document do. Because no two sessions ever write the
same path, the registry can never itself become the contention point, and "who holds X" is a lock-free
directory scan. The claims are git-ignored: they are ephemeral facts about one machine's live sessions
and are useless to anyone else.
A
CONFLICTresult exits 3, so it can gate a script.What it fixed: collisions became visible. Sessions can now see that a file is held, by whom, and
for how long, and can choose to work elsewhere.
What it did not fix, measured on 2026-08-13: it is advisory. On that day
master-work-items.mdwentCONTESTED — two sessions held it simultaneously — and nothing stopped either. A third session
(ours) needed to correct two stale rows in that file and simply could not, so the corrections are still
outstanding. Separately, one session's
git pushcarried an unrelated session's commit with it.Visibility is a real improvement over a rule. It is not a solution.
Mitigation 3: the inbox pattern — built 2026-08-13, and measured over 7 days
The insight: every approach above tries to make it safe for two sessions to edit one file. The
alternative is that they never do.
master-work-items.d/<session-label>.md—containing the rows or fragments it wants added. Zero contention, structurally, because no other
session will ever write that filename.
Why this is stronger: it converts a long edit window — minutes or hours during which a session
holds a contended file, including the time it spends waiting for a human to approve a diff — into a
short append window measured in seconds. The failure mode requires two sessions to be in the same
file at the same moment; this makes that window almost nonexistent rather than merely visible.
We got our own specification wrong, and found out on day one
The paragraph this section replaces said the pattern suits "row-shaped, appendable content", and we
nearly built it append-only on that basis. That was wrong, and it would have failed the exact case
that commissioned the work.
Of the first four operations real sessions queued, three were corrections to existing rows, not
appends — a story number replacing a
needs a storyplaceholder, a status moving from planned toshipped. Close-outs mostly revise something a session wrote earlier. And the two updates that were
blocked on 2026-08-13, the ones that motivated building this at all, were both stale flags needing
correction. An append-only inbox could not have expressed any of them.
So the fragment format carries four directives:
@@ note: <line>@@ section: <exact heading>@@ row: <exact heading>@@ replace: <exact existing line>Everything anchors on a heading or an exact line, never a line number — and that mattered within
hours. One fragment sat queued while other sessions added content above its target; its anchor resolved
to line 2092, then 2149, then 2151, and landed correctly each time. A line-based format would have
written into the wrong section twice.
The tool refuses rather than guesses. A missing or ambiguous heading, a table row whose column count
differs from its header, a
@@ replace:target that is not unique, or content with no anchor skips thatfragment whole and leaves it on disk. One session's typo cannot block another's queued work, and
nothing ever half-lands.
The refusal that matters most: if the shared file already carries uncommitted changes, the merge
refuses the entire run and exits non-zero. The fragment stays queued, costing nothing. That is the case
this report's structural finding said had no clean answer — and the answer turns out to be that not
landing yet is safe once your work is no longer sitting inside the contended file.
Measured: 2026-08-13 to 2026-08-20
master-work-items.mdEvery one of the 18 merge commits contains exactly one file. The bundling that produced all three
incidents went to zero through the pattern — by construction, not by care. Nine of the 23 fragments
landed inside a shared merge commit, which under the old convention would have been nine sequential
edits to one contended file.
It is not airtight, and the way it leaks is the most useful thing we learned
Three sessions edited the file directly anyway — 08-15, 08-18 and 08-20 — despite the rule being in
the standards document every session reads at startup, and despite a session-start check. Roughly one
every two days. The 08-18 one is the worst shape:
master-work-items.mdbundled with an ADR, a prompt,the standards document and two tool files in a single six-file commit. Exactly the signature the pattern
exists to eliminate.
Compliance is therefore ~88% (18 inbox merges against 3 direct edits), not 100%, and the residue is
not carelessness in any interesting sense. It is instruction decay: a rule that a session read hours
earlier competes with everything else in its context, and a long session drifts back to the obvious act
of editing the file in front of it. We can write the rule, check it at startup, and build a tool that
refuses bad input — and none of that reaches a session that simply never invokes the tool.
And it cannot cross a machine boundary at all. A second developer's clone has the tool via
git pullbut their sessions were never told the convention; their commits still edit the file directly, andone of them was a
git mergeresolving conflicts inside it. Every mitigation in this report isper-machine userland scaffolding, and the second developer is invisible to all three.
Honest limits
too. That window is milliseconds instead of hours, which is the whole of the claim.
--targetflag. A fragment saying "insert under §X" in a standards document is more fragile than a contested
manual edit. Prose stays on the claims registry — so this is a second mechanism, not a replacement.
so a session that dies leaves rows
git statuswill never show. We made the read-only dry run asession-start check to cover it, but it is a real failure mode and it was ours to find.
committed the whole index rather than by pathspec (so it could sweep what it existed to prevent), and
it deleted fragments even when the commit failed. Neither lost data. Both are the kind of mistake a
userland reimplementation of a coordination primitive invites.
What we think belongs at the tool level
Offered as observations from the field, not as a specification. Items 6–8 were added 2026-08-21 after
seven days of running mitigation 3, and they are the ones we could not have written before building it.
in userland, an answer to "who else is working in this directory right now?" — a question the harness
is much better placed to answer, since it already knows which sessions are open and which files each
has edited.
git add/git commit, not the edit. A warning at commit time — "session Bhas uncommitted edits to 2 of the files in your index" — would have prevented all three of our
incidents, and requires no locking or coordination protocol.
git commitcommits the index, not your paths. This surprises people, and it is the direct causeof incident 1. A selective
git addoffers no protection against changes another actor pre-staged.Our own merge tool shipped with this bug in its first week, in the tool written to prevent it.
test passed. Nothing failed loudly. The cost lands later, in
git blameand in code archaeology, andby then two of ours were pushed and unrewritable.
that encourages holding finished edits uncommitted while waiting on a human increases exposure. We
now prefer committing promptly and presenting the SHA.
one. Mitigation 3 removes the collision structurally, and still leaked at about one direct edit
every two days: 18 inbox merges against 3 sessions that simply edited the file. The rule was in the
standards document every session reads at startup, plus a session-start check, plus a tool that
refuses bad input. None of that reaches a session that never invokes the tool. A tool can only
refuse the calls it receives. The harness sees the write itself, which is the only place a
convention about writes can actually be enforced — even a soft "3 sessions have this file in an
inbox; are you sure you want to edit it directly?" at write time would have caught all three.
developer's clone received all three mitigations via
git pulland adopted none of them, because theconventions live in that developer's session context, not in the repo. Their commits kept editing the
contended file directly and one was a
git mergeresolving conflicts inside it. Any coordinationbuilt per-machine has this ceiling; a repo-level or account-level signal does not.
safety defects in our merge tool were caught within a week by other sessions that had only the tool
and its documentation — including the index-vs-pathspec bug in point 3, and consuming queued fragments
even when the commit failed. Concurrency made the tooling better as well as harder; the same sessions
that collide also cross-check.
What you can take
Two pieces of working code, both in daily use, both dependency-free, both about 200–600 lines:
tools/session-claims.js— the advisory claims registry. The one-file-per-session design has heldup completely; it is the visibility layer and still the right answer for prose documents.
tools/merge-work-items.js— the inbox merge tool. Anchors fragments by heading rather than linenumber, refuses ambiguous anchors rather than guessing, and refuses the whole run when the target is
already dirty.
Take them, improve them, or treat them as a sketch of the requirement — our interest is in the problem
being solved, not in our solution surviving. If we had to keep one finding, it is point 6: we removed
the structural collision and the residue was purely that a long-running session forgets a rule. That part
is not solvable in userland.
The incident SHAs are ours and the repository is private, but the diffs and commit messages can be shared
on request if the detail is useful.