Skip to content

Semantic-entry id uniqueness in the migration registry is guarded only by the shard FILENAME rule — no runtime assertion would see a duplicate #19732

Description

@os-warren

Filed by the domain:spec execution seat 2, session session_01UDXER3sdqfeVYpEWZs5mZx, 2026-09-22T14:10Z.

⛔ Unlabelled and unrouted — an execution seat files, triage grades and routes. Suggested lane: domain:spec.

The gap

Semantic-entry id uniqueness in the migration registry is defended by exactly one mechanism, and nothing in the test suite would see it fail.

The defence, verified at origin/main:

  • shardNameFor(major, id)packages/spec/scripts/build-migration-registry.ts:147 — makes the filename a pure function of the entry id;
  • parseEntry (:211-215) hard-errors when they disagree: 「filename does not match its entry id (…) — expected …」;
  • a directory cannot hold two files under one name, so two entries sharing (major, id) cannot coexist on disk — they collide as an add/add git conflict (:64).

loadEntries' own comment states this is the whole defence, quoted verbatim:

「Duplicates cannot occur — the name is a function of the id and a directory cannot hold two files with one name — which is why nothing here checks for them.」 (:250-252)

⛔ What the test suite actually asserts — measured, not assumed

packages/spec/src/migrations/migrations.test.ts's registry integrity block (:27) asserts conversion-id reality, toMajor agreement, non-empty acceptance criteria (:46) and the floor rules. Every new Set in the file is about conversion ids:

site what it asserts
:24 CONVERSION_IDS — a lookup set for conversion existence
:342, :459 the conversion ids applied in a run
:566 new Set(convertedIds).size === convertedIds.length — no conversion applied twice in one run, a runtime-application property

none is a registry-wide semantic-entry id uniqueness assertion. ⛔ If the filename rule were ever relaxed — a shard convention change, a generator rewrite, a directory layout move — a duplicate semantic id would be invisible to the whole suite.

Why it is worth a card rather than a shrug

The mechanism is sound today and this is ⛔ not a live defect: no duplicate exists, and the gate is provably alive (its shipped --self-test drives the red path, and an on-disk ablation — a second entry file carrying an already-registered id — moves the real gate from exit 0 to exit 1 with the duplicate id named).

What makes it a card is the shape: a load-bearing invariant whose only guard is an incidental property of the filesystem, with an explicit in-code comment telling the next reader not to check for it. That comment is correct about today and is exactly what would keep a reviewer from noticing if the premise moved.

⚠️ And the premise is not hypothetical: the same registry already carries one hand-written region (step18.rationale) outside the generated markers, where a merge can silently drop content with no gate going red. The two together say this file's invariants are defended unevenly.

The cheap remedy, offered rather than prescribed

A runtime assertion in the registry integrity block — every (major, id) appears once across the loaded entries — is a few lines and would fail loudly if the filename premise ever stopped holding. ⛔ This card does not decide that: adding a required assertion to a shipped gate is a judgement for triage and, if it becomes a new required gate, for the maintainer.

Provenance

Found by the os-dev round that resolved PR #19600's merge conflict, while re-deriving the registry's invariants two independent ways. ⭐ It named the gap rather than resting on its own green result — the round's invariant re-derivation passed, and it reported the limit of what that pass proves. The seat re-verified the assertion inventory first-hand before filing.

Duplicate-search words

shardNameFor, semantic entry id uniqueness, build-migration-registry parseEntry, registry integrity duplicate, duplicates cannot occur


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions