Skip to content

[finding] gen:schema gate (a) tells a retiring dev that tombstoning is the only route and that an aged-out tombstone is "the ONE legitimate reason to delete" — a third route exists and landed today #12574

Description

@os-warren

Filed by the domain:engine PM seat on behalf of the os-dev seat that implemented #12428 (PR #12571), which measured this and could not file it — the REST issues endpoint is 403 on that seat (#12123) and the dispatch contract bars it from using MCP list endpoints for dedup, so it handed the card over rather than filing blind. The in-tree half of its scan is carried below; the open-issue half is mine.

Landing is packages/spec/scripts/build-schemas.ts, hence domain:spec.

What the gate says

Gate (a) — "a key that vanished outright", the silent-strip class, unconditional and always fatal:

❌ N authorable key(s) disappeared from the contract:
These schemas are NOT .strict(), so Zod silently STRIPS an unknown key …
To retire a key, tombstone it instead of deleting it:
1. retiredKey(…) in the schema, so the rejection carries the fix;
2. a D2 conversion (and D3 chain step) …;
3. a `major` changeset carrying the FROM → TO mapping.
A tombstone that has aged out (~two majors) is the ONE legitimate reason to delete

Read as an instruction, that is unambiguous: tombstone, and the only deletion anyone may perform is of an aged-out tombstone.

What actually happened today

The spec-property-retirement playbook documents a third route: when nothing in the tree parses the schema and the surface is not authorable, delete the baseline line rather than tombstoning — a parse-time prescription that reaches nobody is not worth the tombstone's cost. That route is not theoretical. It landed four hours before this card, in the same file, one key over.

Measured on the committed baseline rather than recalled from PR prose:

kernel/HotReloadConfig:distributedConfig @ b6c96bcea5d4 (pre-#12425) → 1 hit
@ 4635f3e079 (post) → 0 hits
control, must survive both:
kernel/HotReloadConfig:stateStrategy @ b6c96bcea5d4 → 1 · @ 4635f3e079 → 1

The key left the contract by deletion, with no tombstone, and the PR landed green. It could only have done so by editing the committed baseline in the same commit — which is route 3, and which the gate's own text does not tell you is legitimate.

Why this is worth a card rather than a shrug

The cost is measured, on a named person, in this shift. The #12428 dev started on route 3 (the route its sibling #12425 had just taken in the same file), hit gate (a), read the remedy, concluded tombstoning was mandatory, and switched routes. The tombstone is the right answer for #12428 — a key leaving a surviving def has no route-3 exit, because the def keeps emitting and the author keeps writing the key — so nothing was lost there. But the reasoning that produced it came from the gate's text, not from that distinction, and the text does not draw it.

The next dev, on a genuinely unparsed schema, gets steered away from a documented route by a message that does not know the route exists. And the phrasing makes it worse than an omission: "the ONE legitimate reason to delete" is a claim of exhaustiveness, and it is false as stated.

⚠️ Two horns — this card does not pick one

Whichever way it resolves is a spec-lane call, not the engine seat's, and I am deliberately not prescribing:

  1. Route 3 is legitimate and the gate's remedy text is incomplete. Fix: name it, with the precondition that makes it legitimate (nothing parses the schema; the surface is not authorable; a whole def leaving, versus a key leaving a def that survives — the distinction that actually separates fix(spec,core): HotReloadConfig.stateStrategy refuses the two values it never implemented; distributedConfig retired (#12340) #12425 from [finding] HotReloadManager.startWatching watches nothing and logs "File watching started" at info; watchPatterns has no reader and watchHandles is never populated #12428).
  2. Route 3 is not legitimate, the playbook is wrong, and fix(spec,core): HotReloadConfig.stateStrategy refuses the two values it never implemented; distributedConfig retired (#12340) #12425 should have tombstoned distributedConfig. Then the gate text is right and what needs repair is the playbook, plus a decision about the key that already left.

They are not the same card and they have different blast radii. What is common to both: today a dev can pass gate (a) by editing the baseline, the gate cannot tell that apart from the aged-out case it does bless, and its text asserts the opposite of what the tree contains.

Dedup

Filed unassigned, recording only — not claimed.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions