I3974 — One allocator surveys every number (issue ids, ADR numbers, case numbers), and two branches adding one number under different names are refused
The survey lib/adr.sh (mj_adr_claims) and scripts/case-numbers each implement for one identifier (this tree, the sibling worktrees, every ref, the peer board) is shared by both and extended to a third kind, issue ids, inside the existing units rather than a new one: majordomus adr next keeps its output, scripts/case-numbers next keeps its output, and a kind argument (issue, adr, case) makes either answer for any kind from the one survey. The board source is defined with its producer and its parser in the same unit: next --reserve announces a named claim id:<kind>:<number> on the peer board, and the survey reads exactly those claim names; no free-text announcement is parsed. The collision gate (scripts/ci/collision-gate) gains the number check: a pull request adding an issue id, ADR number or case number that another unmerged branch adds under a different file name is refused (exit 11) naming that branch; the same path with different content is already refused by path. The first step diagnoses why adr check, which doctor runs in pre-commit and case 271 holds, did not stop ADR 0168 being taken twice, and records the cause in this issue before the code changes.
READY wave 0 · p1 · implementation profile · runs alone
Ready. Every dependency is done, so majordomus plan start I3974 will be accepted.
Objective
The survey lib/adr.sh (mj_adr_claims) and scripts/case-numbers each implement for one identifier (this tree, the sibling worktrees, every ref, the peer board) is shared by both and extended to a third kind, issue ids, inside the existing units rather than a new one: majordomus adr next keeps its output, scripts/case-numbers next keeps its output, and a kind argument (issue, adr, case) makes either answer for any kind from the one survey. The board source is defined with its producer and its parser in the same unit: next --reserve announces a named claim id:<kind>:<number> on the peer board, and the survey reads exactly those claim names; no free-text announcement is parsed. The collision gate (scripts/ci/collision-gate) gains the number check: a pull request adding an issue id, ADR number or case number that another unmerged branch adds under a different file name is refused (exit 11) naming that branch; the same path with different content is already refused by path. The first step diagnoses why adr check, which doctor runs in pre-commit and case 271 holds, did not stop ADR 0168 being taken twice, and records the cause in this issue before the code changes.
Why
Numbers collided repeatedly on 2026-10-21 and 2026-10-22: ADR 0168 was taken by two branches and train 4 had to renumber one to 0221; issue id blocks are handed out per seat by message because no allocator surveys issue ids; the parallel-forks memory records forks taking one number. Path-based collision detection cannot see 0168-a.md and 0168-b.md as one claim.
Current state
Verified at 355a0cee79a: lib/adr.sh mj_adr_claims and scripts/case-numbers survey tree, worktrees, refs and board for ADR and case numbers separately; adr check asks the other refs whether a different decision stands at an identity the tree adds (case 271_adr_allocation); nothing surveys issue ids; no code writes or parses an id reservation on the board; scripts/ci/collision-gate compares added paths only and so misses one number under two names. Why adr check did not stop 0168 is not yet diagnosed.
Desired state
One command answers the next free number of each kind from one survey, and the collision gate refuses one number under two names across branches.
Scope
- lib/adr.sh
- scripts/case-numbers
- scripts/ci/collision-gate
- scripts/collision-check
- test/cases/99_adr.sh
- test/cases/271_adr_allocation.sh
- test/cases/560_a_case_number_names_one_case.sh
- test/cases/566_the_collision_guard_has_a_caller.sh
- .ai/repo/rules/project/a-number-names-one-case.v1.md
- .ai/repo/project/issues/I3974.yaml
Out of scope
- Renumbering any existing record; the case-numbers baseline stays the record of old collisions
Dependencies
None. This issue is a root of the graph.
Acceptance criteria
- Two fixture branches adding .ai/repo/adrs/0300-a.md and .ai/repo/adrs/0300-b.md, and two adding test/cases/9990_a.sh and test/cases/9990_b.sh, are each refused by the collision gate naming both branches (case two_branches_taking_one_number_are_refused)
- next for kind issue names a number above every issue id in the tree, the worktrees, every ref and every id:issue claim on the board, and lists each source
- next --reserve announces id:<kind>:<number>, and a second session's next skips it
- adr next and case-numbers next answer exactly as before on the repository
- The cause of the 0168 collision is recorded in this issue
Validation
- bin/majordomus plan validate
- env -u MAJORDOMUS_SHARE bash test/run.sh 99_adr
- env -u MAJORDOMUS_SHARE bash test/run.sh 271_adr_allocation
- env -u MAJORDOMUS_SHARE bash test/run.sh 560_a_case_number_names_one_case
- env -u MAJORDOMUS_SHARE bash test/run.sh 566_the_collision_guard_has_a_caller
Evidence required
- one_allocator
Evidence
None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.
Timeline
- started
- —
- verified
- —
- completed
- —
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I3974.yaml. Read it back with majordomus plan show I3974.