docs/plans/2026-04-23-slate-v2-authoritative-command-kernel-batch-2-plan.md
Batch 1 is good enough to build on.
Do not repeat Batch 1. Do not keep adding proof rows around the current timing model.
Batch 2 should pivot to the stricter architecture:
event
-> kernel dispatch
-> target owner
-> intent
-> command
-> selection import/export policy
-> transaction
-> repair policy
-> DOM repair execution
-> trace
Exactly one object owns that chain: EditableEditingKernel.
Everything else becomes a worker.
Batch 1 proved the spine, not the perfect editor.
The remaining risk is not missing test rows. The remaining risk is authority leakage:
selectionchange is traceable, but pure programmatic DOM-selection import is
not a supported API/proof yetIf Batch 2 starts by fixing a single browser symptom, it is already wrong.
Keep these as non-negotiable facts:
EditorCommit.command records command metadataeditor.applyDo not replace those systems in Batch 2.
Batch 2 extends and tightens them.
EditableEditingKerneldecorate as primary APIIn scope:
.tmp/slate-v2/packages/slate-react/src/editable/**.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/hooks/android-input-manager/**.tmp/slate-v2/packages/slate-browser/src/**.tmp/slate-v2/playwright/integration/examples/**.tmp/slate-v2/site/examples/ts/** only for proof examples and input-rule
migration.tmp/slate-v2/packages/slate/** only if kernel result or command metadata
needs core support.tmp/slate-v2/packages/slate-dom/** only if DOM selection or clipboard bridge
ownership is provenOut of scope unless a focused failing proof proves ownership:
.tmp/slate-v2/packages/slate-history/**.tmp/slate-v2/packages/slate-hyperscript/**EditableEditingKernel must return the whole decision, not just traces.
Target result:
type EditableKernelResult = {
command: EditableCommand | null;
handled: boolean;
intent: InputIntent | null;
nativeAllowed: boolean;
ownership: EditableOwnership;
selectionPolicy: EditableSelectionPolicy;
repairPolicy: EditableRepairPolicy;
trace: EditableKernelTraceEntry;
};
Required policies:
selectionPolicy: none | import-dom | export-model | preserve-model | clear | shellrepairPolicy: none | sync-selection | repair-caret | repair-text | force-renderThe kernel decides policies. Workers execute them.
SelectionController owns import/export mechanics only.
It should not decide whether selection should be imported or exported.
Required APIs:
SelectionController.importDOMSelection(result);
SelectionController.exportModelSelection(result);
SelectionController.clearSelection(result);
SelectionController.captureDOMSelectionSnapshot();
SelectionController.captureModelSelectionSnapshot();
Rules:
import-domexport-modelselectionchange side effectDOMRepairQueue executes repair only.
It must not decide why repair is needed.
Required repair tags:
after-commandafter-native-inputafter-historyafter-compositionafter-clipboardafter-shell-activationafter-selection-importEvery repair trace must include:
Current files stay, but their authority changes:
input-controller.ts: classify onlykeyboard-input-strategy.ts: execute keyboard worker branch onlymodel-input-strategy.ts: execute beforeinput/input worker branch onlyclipboard-input-strategy.ts: execute clipboard worker branch onlycomposition-state.ts: execute composition worker branch onlycaret-engine.ts: compute movement onlyselection-controller.ts: import/export onlydom-repair-queue.ts: repair onlyThey should not independently set selection source, schedule repair, or mutate selection except through a kernel result.
slate-browser should become the executable spec layer.
Batch 2 generated gauntlet families:
Each scenario must assert:
Do not pretend mobile Playwright keyboard transport is native proof.
Batch 2 must classify mobile text input into one of these:
No blanket mobile skip.
No hidden semantic-handle proof labeled as native input.
Batch 2 should burn down bypasses that directly threaten the kernel:
onKeyDownonDOMBeforeInputeditor.selection writes in runtime codeeditor.marks writes in composition/Android code unless wrapped as a
named compatibility mirroreditor.onChange patching in React hooksdecorate as primary overlay APIDo not hard-cut everything at once.
Cut or wrap one bypass only when a proof row demonstrates the new path.
Goal:
Actions:
editable.tsxeditable/**Exit gates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selectionchange|repair|navigation|core command metadata" --workers=1 --retries=0
bun run lint
Goal:
Actions:
EditableKernelResultEditableSelectionPolicyEditableRepairPolicyTest files:
.tmp/slate-v2/packages/slate-react/test/editing-kernel-contract.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsExit gates:
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel|selectionchange|repair|core command metadata" --workers=1 --retries=0
Goal:
Actions:
SelectionController.importDOMSelectionSelectionController.exportModelSelectionTest files:
.tmp/slate-v2/packages/slate-react/test/selection-controller-contract.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsExit gates:
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "selection import|ArrowDown|ArrowRight|selectionchange and repair" --workers=4 --retries=0
Goal:
Actions:
DOMRepairQueue execute only policy objectsTest files:
.tmp/slate-v2/packages/slate-react/test/dom-repair-policy-contract.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsExit gates:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|undo|repair|text input" --workers=4 --retries=0
Goal:
Actions:
Test files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.ts.tmp/slate-v2/playwright/integration/examples/mentions.test.ts.tmp/slate-v2/playwright/integration/examples/inlines.test.ts.tmp/slate-v2/playwright/integration/examples/editable-voids.test.ts.tmp/slate-v2/playwright/integration/examples/paste-html.test.ts.tmp/slate-v2/playwright/integration/examples/shadow-dom.test.ts.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.tsExit gates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/mentions.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Goal:
Actions:
Exit gates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/plaintext.test.ts ./playwright/integration/examples/mentions.test.ts --project=mobile --workers=1 --retries=0
Goal:
Actions:
Exit gates:
rg -n "editor\\.apply|editor\\.selection\\s*=|editor\\.marks\\s*=|editor\\.onChange\\s*=|onDOMBeforeInput|decorate|renderChunk" packages/slate-react/src site/examples/ts
bun run lint
Goal:
Required closure gates:
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/mentions.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
bun run lint:fix
bun run lint
Completion criteria:
Batch 2 should not swallow the whole rewrite.
Defer these unless Batch 2 evidence proves they are immediate blockers:
createSlateDecorateCompatSource to a compat namespace/packageactive goal state should be:
status: pending while Batch 2 work remainsstatus: done only when Phase 7 closure criteria passstatus: blocked only when no autonomous progress is possible and exact
missing evidence is namedDo not set completion to done because Batch 1 is done.
Batch 2 has its own proof target.
Status: complete.
Actions:
active goal state to status: pending for Batch 2 execution.Evidence:
rg -n "createEditableKernelResult|recordEditableKernelTrace|requestEditableRepair|requestRepair|applyEditableRepairRequest|selectionSourceTransition|preferModelSelection|syncEditorSelectionFromDOM|syncEditableDOMSelectionToEditor|applyEditableDOMSelectionChange" .tmp/slate-v2/packages/slate-react/src/components/editable.tsx .tmp/slate-v2/packages/slate-react/src/editable -g "*.ts"
rg -n "editor\\.apply|editor\\.selection\\s*=|editor\\.marks\\s*=|editor\\.onChange\\s*=|onDOMBeforeInput|decorate|renderChunk|EditableRoot|EditableBlocks" .tmp/slate-v2/packages/slate-react/src .tmp/slate-v2/site/examples/ts -g "*.ts" -g "*.tsx"
rg -n "test\\.skip|\\.skip\\(|skip\\(" .tmp/slate-v2/playwright/integration/examples -g "*.ts"
rg -n "createSlateBrowser.*Gauntlet|ScenarioStep|kind: 'custom'|metadata:|transport: 'semantic|transport: 'native|mobile" .tmp/slate-v2/packages/slate-browser/src/playwright/index.ts .tmp/slate-v2/playwright/integration/examples/richtext.test.ts .tmp/slate-v2/playwright/integration/examples/mentions.test.ts .tmp/slate-v2/playwright/integration/examples/inlines.test.ts
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selectionchange|repair|navigation|core command metadata" --workers=1 --retries=0
bun run lint
Results:
6 passedtest.skip rows found under playwright/integration/examplesOwner classification:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/caret-engine.ts.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/**editor.marks = ... in composition and Android pathseditor.selection = ... in selection reconcilereditor.onChange = ... in React hooks/componentsonDOMBeforeInput example props in hovering toolbar and markdown
shortcutsdecorate compatibility in projection storeFailed probes recorded:
docs/solutions/test-failures/2026-04-22-slate-browser-selectionchange-proof-must-separate-traceability-from-programmatic-import.md.Decision:
EditableKernelResult, so later import/export
work has one authority.Continue checkpoint:
requestRepair and selection-source transitions are still distributed
across strategies, and mobile proof is explicitly semantic-handle in multiple
rows.bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel|selectionchange|repair|core command metadata" --workers=1 --retries=0editing-kernel-contract.ts test
for selection/repair policy defaults and illegal combinations, then implement
the smallest policy model in editing-kernel.ts.Status: complete.
Actions:
packages/slate-react/test/editing-kernel-contract.ts.EditableSelectionPolicy and EditableRepairPolicy types.selectionPolicy and repairPolicy to EditableKernelResult.EditableKernelTraceEntry.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/test/editing-kernel-contract.tsEvidence:
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel|selectionchange|repair|core command metadata" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
3 passed1 passed15 passed6 passed8 passedFailed probes recorded:
selectionPolicy was
missing. This was the intended RED.Decision:
Continue checkpoint:
requestRepair directly.bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel policy" --workers=1 --retries=0bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1Status: complete.
Actions:
selectionPolicy and
repairPolicy fields.DOMRepairQueue instead of
collapsing it into generic repair-caret.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel policies" --workers=1 --retries=0
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "kernel policies|kernel|selectionchange|repair|core command metadata" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "kernel policies" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated navigation|Arrow|word movement|line extension|navigation and mutation|core command metadata|kernel policies|selectionchange and repair" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
3 passed1 passed15 passed6 passed9 passed4 passed40 passedFailed probes recorded:
repairCaretAfterModelTextInsert
lost its specific reason and recorded generic repair-caret. Fixed by
threading the reason through DOMRepairQueue.Decision:
Continue checkpoint:
syncEditorSelectionFromDOM and
syncEditableDOMSelectionToEditor directly from handlers.bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selection import|ArrowDown|ArrowRight|selectionchange and repair" --workers=1 --retries=0SelectionController policy-wrapper
contracts that reject DOM import/export unless the kernel policy allows it.selectionchange import claims.Status: complete.
Actions:
packages/slate-react/test/selection-controller-contract.ts.selection-controller.ts:
executeEditableSelectionImportexecuteEditableSelectionExportChanged files:
.tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts.tmp/slate-v2/packages/slate-react/test/selection-controller-contract.tsEvidence:
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selection import|ArrowDown|ArrowRight|selectionchange and repair" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed3 passed1 passed15 passed6 passed3 passedFailed probes recorded:
executeEditableSelectionImport and
executeEditableSelectionExport did not exist.Decision:
Continue checkpoint:
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "ArrowDown|ArrowRight|selectionchange and repair" --workers=1 --retries=0executeEditableSelectionImport using the existing kernel decision policy.Status: complete.
Actions:
selectionPolicy to EditableKeyDownKernelDecision.executeEditableSelectionImport.input-controller.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/input-controller.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "ArrowDown|ArrowRight|selectionchange and repair" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
3 passed2 passed3 passedFailed probes recorded:
selection-controller but was not re-exported through input-controller.
Fixed by adding the re-export.Decision:
Continue checkpoint:
selectionPolicy for keydown uses unknown-selection; later
phases should replace this with richer reason taxonomy.bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selectionchange and repair|ArrowDown|ArrowRight" --workers=1 --retries=0executeEditableSelectionExport with an explicit export-model
policy.Status: complete.
Actions:
selection-reconciler.ts through executeEditableSelectionExport.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.tsEvidence:
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "selectionchange and repair|ArrowDown|ArrowRight" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed3 passed6 passed3 passedFailed probes recorded:
Decision:
export-model policy.Continue checkpoint:
syncSelectionForBeforeInput can still import target ranges and DOM
selection based on local booleans.bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "text input and delete|selectionchange and repair" --workers=1 --retries=0ReactEditor.toSlateRange.Status: complete.
Actions:
allowDOMSelectionImport to syncSelectionForBeforeInput.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "text input and delete|selectionchange and repair" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "ArrowDown|ArrowRight|selectionchange and repair|kernel policies" --workers=1 --retries=0
bun run lint:fix
bun run lint
rg -n "syncEditorSelectionFromDOM\\(|syncEditableDOMSelectionToEditor\\(|executeEditableSelectionImport\\(|executeEditableSelectionExport\\(" .tmp/slate-v2/packages/slate-react/src -g "*.ts" -g "*.tsx"
Results:
2 passed3 passed1 passed15 passed2 passed4 passedexecuteEditableSelectionImportexecuteEditableSelectionImportexecuteEditableSelectionExportFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "programmatic DOM selection import" --workers=1 --retries=0bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1executeEditableSelectionImport and records a kernel trace.selectionchange as the import API.Status: complete.
Actions:
importDOMSelection() to the slate-react browser handle.executeEditableSelectionImport.editor.selection.importDOM() to the slate-browser Playwright harness.selectionchange.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "programmatic DOM selection" --workers=1 --retries=0
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "programmatic DOM selection" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated navigation|Arrow|word movement|line extension|navigation and mutation|core command metadata|kernel policies|selectionchange and repair|programmatic DOM selection" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed, editing-kernel
3 passed, projections/selection 6 passed, large-doc/scroll 15 passed4 passed44 passedFailed probes recorded:
programmatic DOM selection import but the test title was
imports programmatic DOM selection through explicit browser handle; reran
with the correct grep.Decision:
requestRepair is still a
worker-owned callback, so repair authority is not yet centralized.Continue checkpoint:
selectionchange.bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|undo|repair|text input" --workers=1 --retries=0applyEditableRepairRequest.importDOMSelection as public app API.Status: complete.
Actions:
packages/slate-react/test/dom-repair-policy-contract.ts.executeEditableRepairPolicy.applyEditableRepairRequest execution with the repair policy gate.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/test/dom-repair-policy-contract.tsEvidence:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|undo|repair|text input" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed3 passed2 passed10 passedFailed probes recorded:
executeEditableRepairPolicy did not exist.Decision:
applyEditableRepairRequest entry point.DOMRepairQueue still consumes operation-specific methods, not policy
objects. That is the next owner.Continue checkpoint:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "repair|text input|undo" --workers=1 --retries=0DOMRepairQueue.repair
method and route text-insert caret repair through it.Status: complete.
Actions:
DOMRepairQueue.repair(repairPolicy).Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.tsEvidence:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "repair|text input|undo" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed3 passed1 passed6 passedFailed probes recorded:
Decision:
DOMRepairQueue has a policy-shaped entry point and text-insert repair uses
it.repair-caret path still calls the old method directly from
applyEditableRepairRequest; migrate that next.Continue checkpoint:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|repair" --workers=1 --retries=0repair-caret execution through
DOMRepairQueue.repair.Status: complete.
Actions:
repair-caret execution through DOMRepairQueue.repair.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.tsEvidence:
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|repair" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed3 passed1 passed9 passedFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "repair trace DOM selection" --workers=1 --retries=0bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1Status: complete.
Actions:
Changed files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "repair trace" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "repair trace|kernel policies|text input|repair" --workers=4 --retries=0
Results:
2 passed20 passedFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated navigation" --workers=1 --retries=0bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --workers=1 --retries=0Status: complete.
Actions:
createSlateBrowserClipboardPasteGauntlet.selectAll and pasteHtml.paste-html.test.ts.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/paste-html.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --grep "generated clipboard paste" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated clipboard paste" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
1 passed4 passedFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --grep "inline|read-only|cut" --workers=1 --retries=0bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --grep "selection|void|nested" --workers=1 --retries=0Status: complete.
Actions:
createSlateBrowserInlineCutTypingGauntlet.inlines.test.ts.LINK) instead of brittle
whitespace/render-wrapper text.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/inlines.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --grep "generated inline cut" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated inline cut" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
4 passedFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --grep "selection|void|nested" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browserStatus: complete.
Actions:
createSlateBrowserInternalControlGauntlet.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/editable-voids.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --grep "generated internal-control" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated internal-control" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
4 passedFailed probes recorded:
openExample used strict
getByRole('textbox') on an example with multiple textboxes. Fixed by
making the harness choose the first textbox by default.Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "composition" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browserStatus: complete.
Actions:
createSlateBrowserCompositionGauntlet.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "generated composition" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated composition" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
4 passedFailed probes recorded:
Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --grep "line|typing|generated" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browserStatus: in progress.
Actions:
createSlateBrowserNavigationTypingGauntlet.openExample to [data-cy="outer-shadow-root"].Evidence:
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --grep "generated shadow" --workers=1 --retries=0
bun run lint:fix
bun run lint
Result:
editor.selection.select(...) could not observe a Slate selection through
the slate-browser harness and received null.Owner classification:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.tsDecision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --grep "generated shadow" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browseropenExample can bind
to the nested shadow editor handle, then re-add the generated shadow gauntlet.Status: complete.
Actions:
createSlateBrowserEditorHarness(page, name, root) so generated
scenarios can bind to explicit editor locators, including nested shadow DOM
textboxes.createSlateBrowserTextInsertionGauntlet.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/shadow-dom.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --grep "generated shadow" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated shadow" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
4 passedFailed probes recorded:
openExample(..., surface.scope) still could not bind to the nested shadow
editor handle for generated selection setup.select is still not enough for this shadow row; the test
uses existing shadow handle setup, then generated insertion/assertion steps.Decision:
Continue checkpoint:
createSlateBrowserEditorHarness can be abused to bypass
openExample; keep it as a proof helper for non-standard surfaces.bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "shell|activation|generated" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browsercreateSlateBrowserEditorHarness where openExample is enough.Status: complete.
Actions:
createSlateBrowserShellActivationGauntlet.Editable and was classified as a native-owned editor command.isInteractiveInternalTarget so role-button/contenteditable=false
descendants are internal targets.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/packages/slate-react/src/editable/input-controller.ts.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.tsEvidence:
bun run build
bun run typecheck
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "generated shell activation|activates shells by keyboard" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated shell activation" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed4 passedFailed probes recorded:
command cannot be native-owned.assert.selection requires
DOM selection, while shell activation is intentionally model-selection-backed
without DOM focus. Fixed the generated gauntlet to assert through the handle.Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "bold|mark|generated" --workers=1 --retries=0bun run build && bun run typecheck from packages/slate-browserStatus: complete.
Actions:
createSlateBrowserMarkTypingGauntlet.ControlOrMeta+b does not trigger the
richtext is-hotkey('mod+b') path in this environment; switched the
generated row to Control+b.Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bun run build
bun run typecheck
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated mark" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated mark" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
4 passedFailed probes recorded:
insertText does not
exercise the browser/app hotkey mark path.ControlOrMeta+b; is-hotkey('mod+b') matched Control+b in this test
environment.Decision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated" --workers=4 --retries=0bun run build && bun run typecheck from packages/slate-browserStatus: complete.
Actions:
Evidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated" --workers=4 --retries=0
Results:
32 passedDecision:
Continue checkpoint:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/plaintext.test.ts ./playwright/integration/examples/mentions.test.ts --project=mobile --workers=1 --retries=0.tmp/slate-v2/playwright/integration/examples/**Status: complete.
Actions:
takeSelectionSnapshotForRoot so semantic-handle
selection can be asserted without requiring a DOM selection range first.Evidence:
rg -n "project\\.name === 'mobile'|project\\.name !== 'mobile'|browserName === 'webkit'|browserName === 'firefox'|return$|test\\.skip|\\.skip\\(" .tmp/slate-v2/playwright/integration/examples -g "*.ts"
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/plaintext.test.ts ./playwright/integration/examples/mentions.test.ts --project=mobile --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=mobile --grep "runs a traced slate-browser scenario" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/mentions.test.ts --project=mobile --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
Classification:
keyboard.insertText nor semantic handle insertion opened the portalFailed probes recorded:
Decision:
Continue checkpoint:
rg -n "editor\\.apply|editor\\.selection\\s*=|editor\\.marks\\s*=|editor\\.onChange\\s*=|onDOMBeforeInput|decorate|renderChunk" .tmp/slate-v2/packages/slate-react/src .tmp/slate-v2/site/examples/ts -g "*.ts" -g "*.tsx"bun run lintStatus: complete.
Actions:
onKeyDown to onKeyCommand.Changed files:
.tmp/slate-v2/site/examples/ts/richtext.tsxEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated mark|kernel policies" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated mark|kernel policies" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed8 passedFailed probes recorded:
insertText; native typing
is needed to prove the hotkey mark path.ControlOrMeta+b did not trigger the richtext is-hotkey('mod+b')
path in this environment; Control+b did.Decision:
onKeyDown for mark hotkeys.onKeyCommand hook.Continue checkpoint:
onKeyCommand still allows
app side effects; a future API can make app commands more declarative.onKeyCommand as the extension path, and
generated proof stays green.onDOMBeforeInput and mutable editor field writes remain.rg -n "editor\\.apply|editor\\.selection\\s*=|editor\\.marks\\s*=|editor\\.onChange\\s*=|onDOMBeforeInput|decorate|renderChunk" .tmp/slate-v2/packages/slate-react/src .tmp/slate-v2/site/examples/ts -g "*.ts" -g "*.tsx"bun run lintonKeyCommand in this slice.Status: complete.
Actions:
onDOMBeforeInput to
Editable.inputRules.Changed files:
.tmp/slate-v2/site/examples/ts/hovering-toolbar.tsxEvidence:
rg -n "editor\\.apply|editor\\.selection\\s*=|editor\\.marks\\s*=|editor\\.onChange\\s*=|onDOMBeforeInput|decorate|renderChunk" .tmp/slate-v2/packages/slate-react/src .tmp/slate-v2/site/examples/ts -g "*.ts" -g "*.tsx"
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated mark|kernel policies|markdown|list|h1" --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
6 passedRemaining bypass inventory:
site/examples/ts/markdown-shortcuts.tsx still passes
onDOMBeforeInput={handleDOMBeforeInput} for Android/native scheduling
policy.packages/slate-react/src/editable/selection-reconciler.ts still has direct
editor.selection = ... in a controlled-value DOM sync fallback.packages/slate-react/src/editable/composition-state.ts and
hooks/android-input-manager/android-input-manager.ts still write
editor.marks.packages/slate-react/src/components/slate.tsx and hooks/use-slate.tsx
still patch editor.onChange for React subscription compatibility.projection-store.ts still contains decorate compatibility adapter
implementation.Classification:
onKeyDown -> onKeyCommandonDOMBeforeInput -> inputRuleseditor.selection = ...: needs a core/public selection setter or
transaction-safe mirror APIeditor.marks = ...: composition/Android compatibility mirror,
should move with core marks APIeditor.onChange patching: React compatibility subscription bridge,
should move with final public API hard cutsdecorate compatibility: adapter-only by design, final namespace/package
cut belongs to public API hard-cut batchonDOMBeforeInput Android/native scheduling path; it is already
paired with inputRules and has focused browser proof, but should move to
an explicit input-rule/Android scheduling policy in a later batch.Decision:
Continue checkpoint:
onDOMBeforeInput remains a live example prop, so closure
must name it as accepted/deferred.bun completion-checkStatus: complete with explicit accepted/deferred browser rows.
Actions:
active goal state ready to mark done for Batch 2 scope.Evidence:
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun test ./packages/slate-react/test/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/selection-controller-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
cd packages/slate-browser && bun run build
cd packages/slate-browser && bun run typecheck
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "generated" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/mentions.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
bun run lint:fix
bun run lint
Results:
1 passed15 passed6 passed3 passed2 passed2 passed32 passed283 passed, 13 failed5000-block huge-doc means:
12.42ms vs legacy chunk-off 268.49ms and chunk-on 290.47ms0.38ms vs 15.79ms and 0.92ms3.77ms vs 168.55ms and 41.64ms10.55ms vs 172.14ms and 40.40ms3.54ms vs 157.96ms and 32.47ms0.56ms vs 181.61ms and 32.64ms8.10ms vs 171.19ms and 32.12ms23.73ms vs 107.87ms and
117.40ms19.07ms vs 109.22ms and 109.70msAccepted/deferred broad-suite red rows:
mentions example › shows list of mentionsmentions example › inserts from listEditor.getLiveSelection
did not close itlarge document runtime example › selects void content by browser click without mutating contenteditable voids › keeps nested editor input focused inside editable voidBatch 2 completion criteria mapping:
EditableEditingKernel returns explicit selection and repair policies:
complete.Final checkpoint: