Part of #6957. Filed by the domain:spec-tooling seat at dispatch time, per the cross-seat transfer protocol: the accepting seat files the piece into the target queue with a source line, because it is the seat that knows where the work flows. ⛔ domain:* deliberately NOT applied — routing is the triage seat's single-producer territory (#6015). This lands in packages/spec/src/migrations/, so the expected route is domain:spec.
Why this is a separate card
#6957's maintainer ruling (2026-08-10) adopted option (a) — per-card entry files concatenated by a generator — for both hand-authored append registries. The scripts/adr-anchors.json half is in flight from domain:spec-tooling (that seat's own file). This card is the packages/spec/src/migrations/registry.ts half, and it is not that seat's to schedule:
- the file belongs to the
domain:spec seat;
- it is the file that seat's in-flight retirement cards append to — measured 37 of 691 commits in the 4-day window touched it;
- a dev from another lane restructuring its shape mid-window would cause exactly the collision this card exists to remove.
The domain:spec seat is the only one that can see when its own retirement queue is briefly empty, which is the window this should land in.
The ruling, verbatim
hybrid — batch now, split the append surface as the durable fix.
Now: the PM landing relay batches same-window retirement cards into one PR … Queued: implement per-card registry entry files concatenated by a generator, the .changeset/*.md shape that already de-conflicted this repo's other hottest file; scripts/adr-anchors.json is in the same fix space and may ride the same mechanism. Option B (uncommitted build-time artifacts) is rejected — the review diff of spec-changes.json / the upgrade guide is worth the laps it costs.
⛔ Options (b) and (c) are settled. Do not re-open them, and do not remove any generated artifact from version control.
What the measurement established (already merged, not to be re-derived)
From #6957's measurement, PM-verified on its three load-bearing claims:
step17's semantic list and RETIRED_KEYS_BY_MAJOR[17] conflicted in 6 of 11 contended laps — 613 lines of hand-resolved conflict markers in 4 days. The card's original "the conflict is often absent" claim was falsified; it had generalised from the one lap it watched.
- Both tables are consumed as sets —
registeredRetiredKeys() (build-schemas.ts:699) builds a Map. So a conflict resolution that drops a sibling's entry produces no error anywhere. That silent-drop risk, not wall-clock, is the reason this is worth doing.
- Counterfactual replay of all six real conflicts under per-entry files: 6/6 CLEAN, with the harness proven non-blind first (byte-identical round-trip; a control edit to the same entry still conflicts).
Known cost, measured
- a one-time reorder of
packages/spec/spec-changes.json and docs/protocol-upgrade-guide.md — 46/46 entries move under filename-sorted concatenation;
- ~6 positional prose cross-references rewritten to id-based;
check-adr-0087-registration.mjs's LEDGER_SOURCES taught to read a directory.
Order: sort by entry id. ⛔ Not an explicit index file — an index is itself a single append-only file every card must edit, reintroducing the conflict being removed.
⚠️ Honest limit, carried from the escalation: this removes the conflict resolution, not the regeneration lap. The lap still fires from the merge queue and the os-regen pending marker. If the goal is "make retirement cards fast", this is not it; if the goal is "stop a retirement being silently dropped", it is.
Sequencing suggestion, not a constraint
The adr-anchors.json pilot from domain:spec-tooling proves the mechanism on a smaller file first. Waiting for it is cheap and de-risks this one; that is this seat's suggestion, and the scheduling call is domain:spec's.
Part of #6957. Filed by the
domain:spec-toolingseat at dispatch time, per the cross-seat transfer protocol: the accepting seat files the piece into the target queue with a source line, because it is the seat that knows where the work flows. ⛔domain:*deliberately NOT applied — routing is the triage seat's single-producer territory (#6015). This lands inpackages/spec/src/migrations/, so the expected route isdomain:spec.Why this is a separate card
#6957's maintainer ruling (2026-08-10) adopted option (a) — per-card entry files concatenated by a generator — for both hand-authored append registries. The
scripts/adr-anchors.jsonhalf is in flight fromdomain:spec-tooling(that seat's own file). This card is thepackages/spec/src/migrations/registry.tshalf, and it is not that seat's to schedule:domain:specseat;The
domain:specseat is the only one that can see when its own retirement queue is briefly empty, which is the window this should land in.The ruling, verbatim
⛔ Options (b) and (c) are settled. Do not re-open them, and do not remove any generated artifact from version control.
What the measurement established (already merged, not to be re-derived)
From #6957's measurement, PM-verified on its three load-bearing claims:
step17's semantic list andRETIRED_KEYS_BY_MAJOR[17]conflicted in 6 of 11 contended laps — 613 lines of hand-resolved conflict markers in 4 days. The card's original "the conflict is often absent" claim was falsified; it had generalised from the one lap it watched.registeredRetiredKeys()(build-schemas.ts:699) builds aMap. So a conflict resolution that drops a sibling's entry produces no error anywhere. That silent-drop risk, not wall-clock, is the reason this is worth doing.Known cost, measured
packages/spec/spec-changes.jsonanddocs/protocol-upgrade-guide.md— 46/46 entries move under filename-sorted concatenation;check-adr-0087-registration.mjs'sLEDGER_SOURCEStaught to read a directory.Order: sort by entry id. ⛔ Not an explicit index file — an index is itself a single append-only file every card must edit, reintroducing the conflict being removed.
Sequencing suggestion, not a constraint
The
adr-anchors.jsonpilot fromdomain:spec-toolingproves the mechanism on a smaller file first. Waiting for it is cheap and de-risks this one; that is this seat's suggestion, and the scheduling call isdomain:spec's.