docs/solutions/developer-experience/2026-04-28-slate-v2-migration-backbone-lanes-need-browser-contracts-before-completion.md
A Slate v2 architecture lane is not done just because the raw API hard cuts compile. If the goal includes future Plate or slate-yjs migration, raw Slate must prove the shared backbone: extension groups, deterministic operations, commit metadata, local runtime targets, and replayable browser contracts.
active goal state correctly stayed pending after the public API and
React DX cuts because migration-backbone proof still had runnable work.bun check:full exited 0, but Playwright still reported retry-resolved
rows. That is acceptable only after the exact rows pass cleanly with retries
disabled.Keep the adapter boundary strict and prove the migration backbone directly.
Raw Slate contracts should cover:
editor.read((state) => state.table.rowCount())
editor.update((tx) => {
tx.table.insertRow()
return tx.table.rowCount()
})
Collaboration/runtime contracts should cover:
const commits = local.applyOperations(operations, { tag: 'remote-import' })
remote.applyOperations(commits[0].operations, { tag: 'remote-import' })
expect(remote.children).toEqual(local.children)
Browser stress contracts should be generated from plugin-style rows, not example-specific patches:
const registry = createSlateBrowserPluginContractRegistry([
defineSlateBrowserPluginContract({
name: 'media',
rows: [
{ family: 'block-void-navigation', routes: ['images', 'embeds'] },
{ family: 'stale-target-remote-rebase', routes: ['images'] },
],
}),
])
The stale-target row should prove local runtime identity rules:
captureRuntimeId('firstImage', [1])
applyOperations(remoteRemoveAndMoveOps, { tag: 'remote-rebase' })
assertCapturedRuntimeIdPath('firstImage', null)
assertCapturedRuntimeIdPath('secondImage', [1])
assertLastCommitTags(['remote-rebase'])
Close the browser gate only after the full gate and any retry-resolved rows are clean under a focused retry-disabled rerun:
bun check:full
PLAYWRIGHT_RETRIES=0 bun run playwright \
playwright/integration/examples/richtext.test.ts \
playwright/integration/examples/inlines.test.ts \
--project=chromium --project=firefox \
-g "persistent native word-delete|generated inline cut typing gauntlet"
This keeps raw Slate small and publishable while still giving Plate, slate-yjs, and similar libraries a real migration path. The proof lives at the common runtime layer: state/tx groups, operations, commits, metadata, runtime ids, and browser replay rows.
The focused retry-disabled rerun prevents a green exit code from hiding a real flake introduced by the current slice.
blocked reserved for no-autonomous-progress states. If a proof owner
remains runnable, status stays pending.slate-browser owns replayable regression contracts.bun check:full reports retry-resolved rows, rerun those exact rows with
PLAYWRIGHT_RETRIES=0 before setting completion to done.