--- status: resolved opened: 2026-09-29 located-in: [hq 00-META/checks/cycle.py] fixed-by: hq f89aef9 (PR 192) amended-design: --- # 155 — Two records may share a number, and every check passes ## What was observed On 2026-09-29 two machines opened issues against this repository within the same hour. Both read `main` correctly and both took "the next free number", and they collided twice: | | one machine opened | the other had already used | |---|---|---| | first | 147, 148 | 147, 148 on an unmerged branch | | second | 149, 150 | 149 on an unmerged branch, 150 from renumbering the first collision | The first collision was reconciled by hand before merging. The second was **merged into `main`**, and `records.py`, `cycle.py` and `index.py` all reported success over a tree holding `149-a-declaration-that-shrinks-to-empty` beside `149-an-adopted-machines-data-cannot-be-placed-where-it-is`, and two folders numbered 150. ## Why The number is allocated as `max(main) + 1`, and `main` lags every open pull request — seven of them that evening. Two readers of the same `main` therefore compute the same next number, and neither is doing anything wrong. The existing reconciliation precedent (a second record numbered 127 became 149) assumed a single writer, which stopped being true when a second machine began filing its own findings. ## Why it matters An issue number is how every other record cites this one — `fixed-by:`, `located-in:`, a decision record's consequence, a commit message. Two records answering to one number is a citation that resolves to whichever folder the reader happened to open, and the failure is silent on both sides: the citer is not wrong, and the cited record exists. It is also exactly the class this repository says it does not permit — a rule (`00-META/process/03-issues.md`: "take the next free number") enforced by nothing. ## How it was fixed, and how the fix is checked `cycle.py` now refuses a tree in which two issue folders share a leading number, and names both. Proven by adding a duplicate and watching it fail, then removing it and watching it pass. The colliding records were renumbered 153 and 154, in the branch that landed last — renumbering a branch whose author is still pushing only moves the race. **The check catches the collision; it does not prevent it.** Allocating a number still needs the open pull requests read as well as `main`. That is a habit the check now backstops rather than one it replaces, and playbook [03](../../00-META/process/03-issues.md) now says so at the step where the number is taken. [ADR 0080](../../02-DECISIONS/0080-the-development-cycle-is-checked.md) enumerates what `cycle.py` enforces and named four things; it carries a progressive insight naming the fifth. The decision stands — this is one more thing frontmatter and file names can carry, found by its absence.