Two rules that were implied but not enforceable, both demonstrated by ersatztv#649 being implemented TWICE in parallel to completion on the same day. Subagents. The kickoff's HARD CONSTRAINTS already require parallel disjoint slices, mandatory independent review from a cold brief, and a named model/effort per dispatch — none of which is satisfiable without delegation. But nothing said so outright, and a generic client preamble of the form "do not use the Agent tool unless the user requested it" reads as a prohibition. Now stated plainly in both CLAUDE.md (always loaded) and the kickoff (pasted per session), with what to delegate and what to keep inline. Claiming. `in-progress` prevents duplicate PICKUP, not duplicate WORK — the record already said so, but step 3 told you to apply the label and nothing else. It now requires four checks first: an open PR whose body says `fixes #N`, a remote branch naming the number, a claiming comment predating the label (exactly what select-queue.sh's CLAIM? flag raises and deliberately leaves unresolved), and a fresh git fetch. Each fails differently; all four are cheap. Staleness. The second half of the #649 collision was reading origin/main once, at branch time, and not again across hours and four review rounds. A branch on a stale base computes its diff against that base, so `git diff origin/main` shows other sessions' merged work as DELETIONS and pushing it reverts them. Re-fetch before every push, rebase when it moved. process.parallel-session-claim carries the incident, including what worked: the merged implementation was better in one respect and the discarded branch's test coverage was salvageable, so diff the two before discarding yours.
3.6 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| process.parallel-session-claim | 2026-07-21 — Claim with `in-progress` before working; claiming is not collision safety (#542) | active | 2026-07-21 | none | none | Before starting an issue, check for an existing claim four ways — open PRs referencing it, remote branches naming it, recent comments (a claim can precede the label), and a fresh `git fetch origin main` — then claim with the `in-progress` label plus a comment. A claim prevents duplicate PICKUP, not duplicate WORK. Re-fetch `origin/main` before every push, not only at branch time. | parallel sessions · in-progress label · claim race · duplicate implementation · stale base · branch reverts merged work · dependency notes · shared surfaces · lore pruning · paths: `docs/handoffs/chicorytv-issue-queue.md` · issues: #542, #649, #666 | `in-progress` label on the Gitea issue. The tiny read→claim race window is accepted; the later claimant backs off. Runner topology: two runners (ci-runner VM 127 + bumblebee-runner), 4 slots total. |
The critical nuance is what a claim does not buy. Two sessions on non-overlapping issues can still collide in the same files. Before touching a shared surface, read the issue's dependency notes ("#234 after #231", "coordinate with #215").
When editing the standing lore/handoff doc, prune covered and stale bullets rather than appending — it
is not append-only, and git keeps the history. git pull --rebase before committing it, since it is
the single most contended file across parallel sessions.
The label is not the check (ersatztv#649, 2026-07-26)
#649 was implemented twice, in parallel, to completion. One session had labelled it in-progress
and was three commits and four review rounds deep when a reviewer noticed origin/main had moved ten
commits: the other session had already merged the same work as PR #666. The duplicate branch was
discarded — pushing it would have reverted #666 and #667, showing the merged work as deletions
because its diff was computed against a stale base.
Two distinct failures, both now covered by the kickoff's step 3:
-
The claim was made, and was insufficient. The other session was presumably already underway when the label went on. A label answers "has anyone announced this?", not "is anyone doing this?" The cheap proxies for the second question are an open PR whose body says
fixes #N, a remote branch with the number in it, and a claiming comment that predates the label — which is exactly theCLAIM?flagscripts/select-queue.shalready raises and deliberately does not resolve. -
The base went stale and nothing re-checked it.
origin/mainwas read once, at branch time, and not again across many hours. The tell is agit diff origin/mainthat shows deletions you did not make. Re-fetch before every push; rebase (never merge main in) when it has moved.
Neither session did anything wrong at the moment of claiming. The lesson is that the duration of a session is the risk: the longer a branch lives, the more the "I checked at the start" evidence decays.
Worth noting what worked: the duplicate effort was not wasted. The merged implementation was better in
one respect (it exports ETV_GITEA_URL as well as GITEA_BASE_URL, because pr-changed-files.sh
reads the former at higher precedence), and the discarded branch's test coverage was salvaged onto the
merged code as an additive tests-only PR. When you discover a collision, diff the two implementations
before throwing yours away — the loser usually contains something the winner lacks.