Skip to content

ID allocation: strict mode for new specs, and collision-aware diagnostics #28

Description

@tylerwillis

Numeric document IDs are a global counter with no locking, and the resulting collisions have now put two production main branches red, three days apart. This is the highest-cost operational defect we have hit with 2119.

Evidence

panopticon, 2026-08-07: three concurrent branches each allocated REQ-050. Each branch's lint was green — the collision does not exist until the merge. Main's gate went red with 8 violations (2 duplicate-ID + 6 stale, 5 of them collision-caused). The project log records six collision waves in three days, the largest hitting five branches at once, with three prior repair commits (#177, 22ea6ef9, #209).

phoneopticon, red right now and undiagnosed for days:

check: FAIL — 34 violation(s), 0 uncovered, 0 failing review(s), 28 stale review(s)

REQ-009  openai-compatible-chat-adapter        x  notifier-rotation-and-launch
REQ-010  structured-action-approval            x  task-notification-dwell
REQ-011  coordinator-install-and-phone-config  x  notification-preferences

Both halves of each pair are on main, merged via separate PRs that were individually green.

Two distinct problems

1. The advisory "prefer file-scoped IDs" rule does not hold

File-scoped kebab-case namespaces exist precisely to end this class — a filename cannot be double-allocated silently. But REQ-052 and REQ-053 on panopticon, and REQ-009/010/011 on phoneopticon, were all allocated numerically after file-scoped IDs shipped, by agents that had read the guidance. A rule that documents a preference and enforces nothing gets ignored under concurrency, which is exactly when it matters.

Proposed: a config option (e.g. new_specs: file-scoped in .2119.yml) that makes a newly added numeric-grammar spec a lint error while grandfathering every existing file. Turn the README's advice into a gate. Repos mid-migration keep working; new drift stops.

2. The diagnostics point at the expensive wrong repair

When REQ-001.1.7 fires, the output is dominated by derived staleness — 28 "stale review(s)" on phoneopticon. That reads as "go re-review 28 requirements," which is hours of judgment review that cannot possibly work: each colliding ID resolves to two documents, so it has two evidence hashes and no verdict satisfies both. Recording one re-stales the other, permanently. The gate is mathematically unfixable by reviewing, and nothing in the output says so.

Proposed: when a duplicate document ID is detected, suppress the derived per-requirement staleness noise for the affected IDs and emit one actionable message — which files collide, that the staleness is an inherent flip-flop, and that the repair is renumber → prune → re-review only what genuinely changed.

3. Optional: catch it before the merge

lint --merge-base <ref> — evaluate the branch's document IDs against the merge base as well as the working tree, so a branch that will collide fails on the branch rather than after the merge. This is where the cost actually is: every collision so far was invisible until it was already on main.

Priority

Quality (gate integrity — a permanently red gate trains people to ignore it) and speed (removes repeated multi-hour repairs). No trade against either.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions