docs/solutions/developer-experience/2026-04-19-slate-explicit-normalization-cuts-should-live-in-one-fixture-override-registry.md
packages/slate had already chosen a narrower live normalization contract:
heavier adjacent-text and spacer cleanup is explicit-only, not something every
ordinary structural op does automatically.
The engine was partly there, but the old legacy transform harness was still treating the opposite behavior as kept truth.
That is how one honest engine fix turned into a wall of fake failures.
bun test ./packages/slate/test/snapshot-contract.ts --bail 1 first failed on
the old transforms/normalization/move_node.tsx expectationbun test ./packages/slate/test failed on 55 legacy transform fixtures55 failures like 55 unrelated bugsKeep the engine on the explicit-only normalization contract, then classify the stale legacy family once at the harness boundary.
The concrete changes were:
packages/slate/src/core/normalize-node.ts
so adjacent-text canonicalization is explicit-only instead of triggered by
every ordinary op.packages/slate/test/snapshot-contract.ts
by rewriting stale rows and helper expectations that no longer matched the
live claim.packages/slate/test/fixture-claim-overrides.ts
for the legacy fixture family that still encoded implicit adjacent-text or
spacer canonicalization.packages/slate/test/index.spec.ts
skip those rows from one place instead of scattering skip markers through
the fixture tree.slate was still being reopened.The key implementation seam is small:
const canCanonicalizeAdjacentText =
options.explicit && !touchesDirectChildCleanup
And the harness ownership stays equally blunt:
const isExplicitCut = isExplicitCutFixture(fixturePathFromTestRoot)
const testFn = /\\bexport const skip\\s*=\\s*true\\b/.test(source)
? it.skip
: isExplicitCut
? it.skip
: it
The important distinction is between:
Legacy fixtures are evidence. They are not allowed to silently override a live contract that has already been chosen and documented.
Once the explicit-only normalization posture is the live contract, the stale legacy rows stop being “bugs to fix” and become “cuts to classify”.
Putting that classification in one registry does three useful things:
That is a lot better than reverting the core or hand-editing fixture outputs forever.
snapshot-contract.ts for the live broad current oracleslate API decisions, the docs are lying.Related docs: