Two machines filing issues in the same hour both read main correctly and both took "the next free number". main lags every open pull request — seven that evening — so they collided twice. The second collision reached main with records, cycle and index all reporting success. cycle.py now refuses a tree where two issue folders share a leading number, and names both. Proven by adding a duplicate and watching it fail. The colliding records become 153 and 154, renumbered in the branch that lands last, because renumbering a branch whose author is still pushing only moves the race. The check catches a collision; it does not prevent one. Taking a number still means reading the open pull requests as well as main — issue 155 says so.
54 lines
2.5 KiB
Markdown
54 lines
2.5 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-29
|
|
located-in: [hq 00-META/checks/cycle.py]
|
|
fixed-by: hq issue/two-records-share-a-number-and-nothing-says-so
|
|
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.
|