Skip to content

[finding] an ablation in packages/spec leaks the mutation into the COMMITTED authorable-surface baseline — restoring only the mutated source leaves a tree whose next build refuses, as a false red #13265

Description

@os-trump

Measured while executing #13137 (PR #13264), domain:cli dev seat, session session_01TvqBFLRzXdSPcbusDoED9k. Recording only — filed unassigned, no lane. ⛔ Not a defect in the ablation preflight's own job; a gap in what anything tells you the RESTORE leg has to cover.

What happened, in the order it happened

An ablation of packages/spec/src/data/field.zod.ts (declare a key on the FieldSchema shape, delete five alias-table entries), run exactly as the ablation discipline prescribes:

  1. mutate, prove it on disk by blob hash — OK;
  2. pnpm --filter @objectstack/spec build, then node scripts/ablation-dist-preflight.mjs packages/spec MARKER"marker present in 26 built files", OK;
  3. run the suite — reds in the predicted places, OK;
  4. restore the mutated source with git checkout HEAD -- packages/spec/src/data/field.zod.ts, blob hash back to its HEAD value, git diff HEAD for that path empty — OK;
  5. rebuild — ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL, exit 1, in ~5 seconds.

Why step 5 failed

The build in step 2 runs gen:schema, which rewrote the committed baseline. git diff HEAD over the whole tree, after step 4:

 packages/spec/authorable-surface/data.json | 1 +
 "data/Field:reference",
+ "data/Field:referenceTo",
"data/Field:referenceVia",

So on the rebuild the key is present in the baseline and gone from the Zod graph — which the surface gate correctly reads as a retirement, and refuses with the ADR-0049 / ADR-0104 enforce-or-remove prescription (tombstone, D2 conversion, major changeset). Every word of that refusal is right. It is answering a question nobody asked, about a key that never existed.

Why it is worth a card

The failure is silent in the direction that matters. After step 4 the tree is, by every check the ablation discipline names, restored:

  • git diff HEAD -- packages/spec/src/data/field.zod.ts — empty;
  • git hash-object on that path == its HEAD blob;
  • grep for the injected marker in that file — 0.

⇒ an agent that scopes its restore proof to the path it mutated — which is exactly what the discipline says to do, and what a trap written around one file does — concludes the tree is clean and keeps measuring. The next thing it sees is either a build refusal it did not cause, or, if it skips the rebuild, a suite still running against dist/ that still carries the ablation. In this run the restore-leg rebuild failed and ablation-dist-preflight --absent then correctly reported "marker still present in 26 built files" — but that pairing only held because the rebuild's non-zero exit was captured. A restore leg that reads only the suite's colour would have reported a false red on an already-restored tree.

⚠️ The general shape, which is not specific to this one gate: a package whose build writes committed artifacts turns an ablation into a two-file mutation, and only one of the two files is the one you chose to mutate.packages/spec is the instance because gen:schema writes authorable-surface/, json-schema.manifest/ and api-surface/; AGENTS.md documents those artifacts and their merge driver, but says nothing about ablation, and scripts/ablation-dist-preflight.mjs proves presence/absence in dist/ and deliberately says nothing about the working tree.

What would close it (⛔ not a proposal I acted on)

Any one of these; the first is the cheapest and needs no new code:

  1. A sentence in the ablation discipline: restore with git checkout HEAD -- <package> (the whole package), and prove it with a WHOLE-TREE git status --porcelain, not with a per-path diff. That is what recovered this run.
  2. A tree-cleanliness assertion in ablation-dist-preflight.mjs --absent — it is already the restore leg's designated instrument, and it already refuses loudly rather than passing quietly. It would have named the leaked baseline line in step 5's place, before the confusing gate refusal.
  3. Name the generated-artifact packages wherever the ablation rebuild requirement is stated, so "rebuild the package you mutated" reads as "and restore what that rebuild wrote".

Reproduction

Any mutation of an authorable key on a packages/spec metadata schema, followed by that package's build. The key does not have to be plausible — the baseline records whatever the Zod graph had at build time.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions