docs/plans/2026-04-23-slate-v2-perfect-architecture-master-plan.md
Keep Slate’s data model and operation semantics.
Replace the public/runtime shape.
The final target is:
Slate model + operations
Lexical-style read/update lifecycle
ProseMirror-style transaction and DOM-selection discipline
Tiptap-style extension and product DX
React 19.2 optimized runtime APIs and renderer behavior
This is the best architecture for:
Do not optimize for backward compatibility. Do not optimize for a neat migration story. Optimize for the best final system that Plate and Yjs can migrate to.
The old plan fixed real bugs, but it was still too incremental. It kept discovering API families one by one and risked semantic method sprawl.
The final architecture must stop treating every regression as a new command family and instead force all editing behavior through one public lifecycle and one internal mutation truth.
The public headline is:
all coherent reads happen in `editor.read`
all public writes enter the same `editor.update` boundary
primitive editor methods are safe inside updates
operations remain collaboration truth
commits are local runtime truth
slateeditor.read and editor.update as the public lifecycleslate-react fed by live reads and dirty commitsCut from the final primary API, docs, examples, and normal plugin patterns:
editor.selectioneditor.childreneditor.markseditor.operationsTransforms.* as the primary mutation storyeditor.apply as an extension pointeditor.onChange as an extension pointReactEditor.runCommanddecorate as the primary overlay APIfocus().chain().run() ceremony for normal toolbar commandsAllowed internally:
editor.read / editor.update$function public stylefocus().chain().run() as required public UXeditor.chain()editor.read(() => {
editor.getSelection();
editor.getChildren();
editor.getMarks();
editor.getOperations();
editor.getLastCommit();
});
Rules:
editor.read must be documented as sucheditor.update(() => {
editor.setNodes({ type: "heading-one" });
});
Rules:
at never imports DOM selectionEditorCommitKeep flexible primitives as the main power-user/plugin API:
editor.select(selection)editor.setNodes(props, options?)editor.unsetNodes(key, options?)editor.wrapNodes(element, options?)editor.unwrapNodes(options?)editor.insertNodes(nodes, options?)editor.removeNodes(options?)editor.mergeNodes(options?)editor.splitNodes(options?)editor.moveNodes(options?)editor.insertText(text, options?)editor.delete(options?)editor.insertFragment(fragment, options?)editor.insertBreak(options?)Rule:
editor.updateat is omitted, they use the transaction targetat is explicit, they use exactly that targetAllowed:
editor.toggleMark('bold')editor.toggleBlock('heading-one')But convenience methods are not the architecture.
Do not grow one semantic core method for every custom node type.
Custom node-type behavior should be written from primitives inside
editor.update.
Hard rule:
editor.updateDefer, but only as sugar:
editor.chain().setNodes(props).wrapNodes(wrapper).run();
Rules:
editor.updateeditor.update
-> transaction
-> internal target resolution when needed
-> operations
-> EditorCommit
-> history / collaboration / render / DOM repair
Internal target resolution rules:
editor.readeditor.getSelection()slate-reactPublic shape:
editor.extend({
name: "todo",
methods: {
toggleTodo() {
this.update(() => {
this.setNodes({ type: "todo", checked: true });
});
},
},
normalizers: [],
commands: [],
});
Rules:
editor.updateDeferred by default:
Reason:
React consumes runtime facts, not ownership of editor truth:
React must not use Editor.getSnapshot() on urgent paths.
Direct DOM text sync must be a strict capability:
The goal is not “fix bugs faster”. The goal is “stop unknown bugs from escaping”.
Required scenario families:
editor.updateEvery user-facing row asserts:
If a red row is unclear:
Reject fake-green tests:
The architecture is only battle-tested when:
Before broad closure:
Allowed:
docs/plans/**docs/research/**docs/slate-v2/**active goal state.tmp/slate-v2/packages/slate/**.tmp/slate-v2/packages/slate-dom/**.tmp/slate-v2/packages/slate-react/**.tmp/slate-v2/packages/slate-browser/**.tmp/slate-v2/site/examples/ts/**.tmp/slate-v2/playwright/integration/examples/**.tmp/slate-v2/scripts/benchmarks/**.tmp/slate-v2/package.jsonDo not touch:
.tmp/slate-v2/packages/slate-hyperscript/**.tmp/slate-v2/packages/slate-history/** unless a focused failing proof proves
history ownershipImplement:
editor.readeditor.updateFiles:
.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/create-editor.ts.tmp/slate-v2/packages/slate/src/core/public-state.tsTests:
.tmp/slate-v2/packages/slate/test/read-update-contract.ts.tmp/slate-v2/packages/slate/test/transaction-contract.ts.tmp/slate-v2/packages/slate/test/commit-metadata-contract.tsImplement:
Files:
.tmp/slate-v2/packages/slate-browser/src/playwright/**.tmp/slate-v2/playwright/integration/examples/**.tmp/slate-v2/packages/slate-react/test/** fixturesTests:
Implement:
editor.updateat bypasses DOM freshnessFiles:
.tmp/slate-v2/packages/slate/src/editor/**.tmp/slate-v2/packages/slate/src/transforms-node/**.tmp/slate-v2/packages/slate/src/transforms-text/**.tmp/slate-v2/packages/slate/src/transforms-selection/**Tests:
.tmp/slate-v2/packages/slate/test/primitive-method-runtime-contract.ts.tmp/slate-v2/packages/slate/test/editor-methods-contract.ts.tmp/slate-v2/packages/slate/test/transaction-target-runtime-contract.tsImplement:
EditorCommit metadata contract:
Files:
.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate-history/** only if a focused proof proves
ownershipTests:
.tmp/slate-v2/packages/slate/test/commit-metadata-contract.ts.tmp/slate-v2/packages/slate/test/bookmark-contract.ts.tmp/slate-v2/packages/slate/test/collab-history-runtime-contract.tsImplement:
Transforms.* from primary docs/examplesapply/onChange as extension pointsTests:
.tmp/slate-v2/packages/slate/test/public-field-hard-cut-contract.ts.tmp/slate-v2/packages/slate/test/write-boundary-contract.tsImplement:
editor.extend(...)defineEditorExtension(...)Tests:
.tmp/slate-v2/packages/slate/test/extension-methods-contract.tsImplement:
slate-reactTests:
.tmp/slate-v2/packages/slate-react/test/target-runtime-contract.ts.tmp/slate-v2/packages/slate-react/test/dom-text-sync-contract.ts.tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx.tmp/slate-v2/packages/slate-react/test/projections-and-selection-contract.tsxImplement:
Tests:
.tmp/slate-v2/playwright/integration/examples/**.tmp/slate-v2/packages/slate-browser/src/playwright/**Keep green:
Core:
bun test ./packages/slate/test/read-update-contract.ts --bail 1
bun test ./packages/slate/test/commit-metadata-contract.ts --bail 1
bun test ./packages/slate/test/primitive-method-runtime-contract.ts --bail 1
bun test ./packages/slate/test/editor-methods-contract.ts --bail 1
bun test ./packages/slate/test/bookmark-contract.ts --bail 1
bun test ./packages/slate/test/public-field-hard-cut-contract.ts --bail 1
bun test ./packages/slate/test/write-boundary-contract.ts --bail 1
bun test ./packages/slate/test/extension-methods-contract.ts --bail 1
bun test ./packages/slate/test/collab-history-runtime-contract.ts --bail 1
bun test ./packages/slate/test/transaction-contract.ts --bail 1
bun test ./packages/slate/test/transaction-target-runtime-contract.ts --bail 1
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun test ./packages/slate/test/surface-contract.ts --bail 1
React:
bun test ./packages/slate-react/test/target-runtime-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
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-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
Browser:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "toolbar|selection|navigation|delete|paste|undo" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/plaintext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Perf:
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 bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Build / type / lint:
bunx turbo build --filter=./packages/slate --filter=./packages/slate-dom --filter=./packages/slate-react --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate --filter=./packages/slate-dom --filter=./packages/slate-react --filter=./packages/slate-browser --force
bun run lint:fix
bun run lint
editor.read and editor.update are the public lifecycle contracteditor.updateTransforms.* is dead as the primary mutation storyStatus: complete for the first lifecycle slice.
Actions:
active goal state from blocked to
pending.tmp/slate-v2/packages/slate/test/read-update-contract.ts.tmp/slate-v2/packages/slate/test/commit-metadata-contract.tsread / update on the public surface contracteditor.read(fn)editor.update(fn, options?)Editor.read(editor, fn)Editor.update(editor, fn, options?)EditorCommiteditor.update(...) started inside
editor.read(...) outside an active updateread / update through current core instead of inventing a second
transaction engineEvidence:
bun test ./packages/slate/test/read-update-contract.ts --bail 1
bun test ./packages/slate/test/commit-metadata-contract.ts --bail 1
bun test ./packages/slate/test/transaction-contract.ts --bail 1
bun test ./packages/slate/test/transaction-target-runtime-contract.ts --bail 1
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun test ./packages/slate/test/surface-contract.ts --bail 1
bun test ./packages/slate/test/editor-methods-contract.ts --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate --force
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Results:
2 passed1 passed23 passed4 passed190 passed10 passed6 passedslate passedslate passedOwner classification:
Rejected tactics:
tx.resolveTarget() publiclyCheckpoint:
editor.read is still a minimal coherent boundary, not the final richer
legality/flush storyslate-browser / Playwright helpers and classify current
substrate gaps before changing more runtime codeStatus: in progress.
Actions:
slate-browser/playwright substrate and the example test
suitescreateEditor() React test is a liar, but bare core editors in
React event-contract rows are still a real fixture smellselection.selectDOM(...) to
.tmp/slate-v2/packages/slate-browser/src/playwright/index.tseditor.selection.select(...)editor.selection.selectDOM(...)editor.selection.get()editor.get.text()[1, 0][1, 0, 0]plaintext.test.ts off its local handle text helper and onto
openExample(...).get.text()paste-html.test.ts off its local selectWithHandle(...) helper
and onto editor.selection.select(...)shadow-dom.test.ts off its local handle helpers and onto the
shared harness for:
editable-voids.test.ts off its local handle helpers and onto the
shared harness for:
large-document-runtime.test.ts onto the shared harness for:
highlighted-text.test.ts off its remaining local delete-forward
helperwaitForSelectionSync() incorrectly required non-empty selected text,
which broke collapsed semantic selections in decorated/projection textSelection.toString()selection.select(...) assumed every semantic handle selection must
immediately produce a DOM rangeEvidence:
bunx turbo build --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate-browser --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "toolbar heading|toolbar bold|toolbar alignment|toolbar list|generated mark typing" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --grep "rich HTML paste|generated clipboard" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/plaintext.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --workers=1 --retries=0
bun run lint:fix
bun run lint
Results:
slate-browser build: passedslate-browser typecheck: passed6 passed2 passed1 passed4 passed8 passed17 passed8 passedOwner classification:
Rejected tactics:
selectDOMRange / getSelectionWithHandle helper soup in the
highest-signal richtext suiteCheckpoint:
richtext.test.tsrichtext.test.ts helper soup and decide which helpers belong in the
shared substrate, which are intentional suite-local browser-path helpers,
and which should be deletedStatus: in progress.
Actions:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts:
selection.location()assert.domCaret(...)richtext.test.ts harness-owned rows off local helper soup and
onto the shared substrate:
openExample(...) +
harness APIs:
richtext.test.ts after the migration:
getDOMSelectionLocationgetDOMSelectionSnapshotselectDOMRangeactive goal state; it falsely claimed Batch 2 and later batches were
already complete while Batch 2 substrate work was still activeEvidence:
bunx turbo build --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate-browser --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation and mutation chained|records kernel policies for browser command and repair traces|applies toolbar heading to the browser-selected paragraph|applies toolbar heading from browser target even when model selection is already heading|applies toolbar bold to the browser-selected text|records core command metadata for text input and delete|records selectionchange and repair kernel results|imports programmatic DOM selection through explicit browser handle|records repair trace with observable DOM and model selection|plain text paste over selected range" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "inserts text through browser input|types at the browser-selected end of a block|ArrowDown then ArrowRight" --workers=1 --retries=0
bun run lint
Results:
slate-browser build: passedslate-browser typecheck: passed10 passed3 passedOwner classification:
Locator handle wrappers in richtext.test.ts: transitional
suite debt, not the shared substrate contractactive goal state: execution-memory owner,
not product/runtime truthRejected tactics:
Checkpoint:
richtext.test.ts still has too many raw-locator rows freelancing outside
the harnesspackages/slate-react/test/** lying-fixture sweep is still openopenExample(...)
and harness APIspackages/slate-react/test/** fixture-owner
classification after the richtext helper cluster is smallerrichtext.test.ts helper cluster,
especially backspace/delete/arrow/undo rows that still use bare Locator
wrappersStatus: in progress.
Actions:
richtext.test.ts browser-editing cluster onto
openExample(...) + shared harness APIs:
selection.select(...)selection.get()get.modelText()assert.text(...)assert.domCaret(...)expectDOMCaretAtTextEnd(...)expectVisualCaretAtEndOfFirstBlock(...)expectDOMCaretAfterInsertedTextBeforeSuffix(...)
those still encode screenshot-adjacent browser geometry checks, not generic
harness truthEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace at selected text end|Delete before trailing punctuation|Backspace deletes selected range|Delete deletes selected range|ArrowLeft and ArrowRight" --workers=1 --retries=0
bun run lint
Results:
5 passedOwner classification:
Rejected tactics:
Locator handle wrappersCheckpoint:
richtext history leftpackages/slate-react/test/** fixture-owner sweep is still waiting behind
this burn-downrichtext.test.ts local helpers are mostly
visual/browser-geometry onlyrichtext raw helper cluster, starting with
undo + insertion rows that still use local handle wrappersslate-react fixture cleanup before the main richtext helper
cluster is smallerStatus: in progress.
Actions:
openExample(...) + shared harness APIs:
selectWithHandlegetSelectionWithHandledeleteFragmentWithHandleinsertTextWithHandleundoWithHandlegetTextWithHandleexpectDOMCaretInTextNodeselection.select(...) only synced model/handle selectionEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "visual caret after browser insertion at the selected text end|visual caret after browser insertion before trailing punctuation|visual caret after browser insertion inside a text leaf|undoes inserted text|repairs DOM after Mac keyboard undo|undo restores deleted selected text" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "browser word movement|browser line extension|browser triple click" --workers=1 --retries=0
bunx turbo build --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate-browser --force
bun run lint
Results:
6 passed3 passedslate-browser build: passedslate-browser typecheck: passedOwner classification:
getBrowserUndoHotkey(...)selectEndOfFirstBlockWithDOMSelection(...)
these are intentionally narrow browser-path utilities, not the main substrate
debt anymorepackages/slate-react/test/** fixture-owner sweepRejected tactics:
selection.select(...)Checkpoint:
richtext.test.ts is finally mostly honest; continuing to mine it
before checking packages/slate-react/test/** would be comfort workpackages/slate-react/test/** liar rowspackages/slate-react/test/** lying-fixture sweep and
classify the first concrete owner clusterStatus: in progress.
Actions:
.tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsxcreateEditor() even though the
same file already used withReact(createEditor()) for richer interaction
rowswithReact(createEditor()):
Evidence:
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/dom-text-sync-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 "navigation|Backspace|Delete|kernel|transition" --workers=1 --retries=0
bun run lint
Results:
large-doc-and-scroll.tsx: 15 passeddom-text-sync-contract.ts: 1 passedprojections-and-selection-contract.tsx: 6 passedslate-dom + slate-react build: passedslate-dom + slate-react typecheck: passed14 passedOwner classification:
slate-react owner cluster is real and runnable, not speculativeRejected tactics:
large-doc-and-scroll.tsx rows in one blind sweepcreateEditor() in slate-react tests as automatically wrongCheckpoint:
slate-react
rows still use bare editors for DOM/event contracts, that is just lazy debtslate-react test fixtures, not example helper
souplarge-doc-and-scroll.tsx may have more DOM/event rows still on bare
editorsreact-editor*.tsx / editable-behavior*.tsx files may still
hide stale test ownership or redundant contract coverageslate-react DOM/event cluster before editinglarge-doc-and-scroll.tsx DOM/event rows first, then
decide whether the next owner is more large-doc fixtures or duplicated
ReactEditor/editable contract filescreateEditor() row is lyingStatus: in progress.
Actions:
active goal state from blocked to
pendinglarge-doc-and-scroll.tsx rows after Slice 1createEditor() rows in that file as
render-shape or DOM-sync capability rows, not DOM/event liar rows.tmp/slate-v2/packages/slate-react/test/react-editor-contract.tsx.tmp/slate-v2/packages/slate-react/test/react-editor.test.tsxEvidence:
bun test ./packages/slate-react/test/react-editor-contract.tsx --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation|Backspace|Delete|kernel|transition" --workers=1 --retries=0
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
react-editor-contract.tsx: 3 passedlarge-doc-and-scroll.tsx: 15 passeddom-text-sync-contract.ts: 1 passedprojections-and-selection-contract.tsx: 6 passed14 passedslate-dom + slate-react build: passedslate-dom + slate-react typecheck: passedFailed probes:
react-editor-contract.tsx was already red in isolation on the
mid-transform DOM-selection text assertion:
expected window.getSelection().toString() to be bar, received empty textbun test filters for react-editor*.test.tsx did not match because
these files are import-wrapper or legacy-shaped files under the current Bun
discovery behaviorOwner classification:
large-doc-and-scroll.tsx: render-shape /
DOM-sync capability fixtures, not automatic liar rowsreact-editor.test.tsx: stale test ownership, hard-cut in favor
of react-editor-contract.tsxRejected tactics:
createEditor() row in large-doc-and-scroll
without owner classificationCheckpoint:
test/bun/editable.spec.tsx still appears to duplicate
editable-behavior.tsx while carrying a few unique render contractstest/bun/editable.spec.tsx against
editable-behavior.tsxtest/bun/editable.spec.tsx vs editable-behavior.tsx and either
migrate unique NODE_TO_KEY / translate rows into a canonical contract or
prove the old spec is still the canonical ownerStatus: in progress.
Actions:
.tmp/slate-v2/packages/slate-react/test/bun/editable.spec.tsx
against the canonical contract files.tmp/slate-v2/packages/slate-react/test/editable-behavior.tsx.tmp/slate-v2/packages/slate-react/test/surface-contract.tsxtest/bun/editable.spec.tsx fileEvidence:
bun test ./packages/slate-react/test/editable-behavior.tsx --bail 1
bun test ./packages/slate-react/test/surface-contract.tsx --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation|Backspace|Delete|kernel|transition" --workers=1 --retries=0
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
editable-behavior.tsx: 3 passedsurface-contract.tsx: 3 passedlarge-doc-and-scroll.tsx: 15 passeddom-text-sync-contract.ts: 1 passedprojections-and-selection-contract.tsx: 6 passed14 passedslate-dom + slate-react build: passedslate-dom + slate-react typecheck: passedOwner classification:
test/bun/editable.spec.tsx: stale duplicate fixture fileeditable-behavior.tsxsurface-contract.tsxRejected tactics:
test/bun/editable.spec.tsx as a shadow contract just because it
still passedCheckpoint:
test/bun/use-slate*.spec.tsx files may still overlap
provider-hooks-contract.tsx.test.tsx import files are intentional discovery shims until
Bun path filtering is cleaned uptest/bun/use-slate.spec.tsx and
test/bun/use-slate-selector.spec.tsxtest/bun/use-slate*.spec.tsx against
provider-hooks-contract.tsx and any selector-specific contract filesStatus: in progress.
Actions:
.tmp/slate-v2/packages/slate-react/test/bun/**
hook specs against provider-hooks-contract.tsxuse-slate-selector.spec.tsx as duplicated by
provider-hooks-contract.tsxuse-slate.spec.tsx and found it red in isolation:
it expected late useSlateWithV subscribers to see a global version of 1,
but the current hook exposes a per-subscriber local version and late
subscribers mount at 0Evidence:
bun test ./packages/slate-react/test/provider-hooks-contract.tsx --bail 1
bun test ./packages/slate-react/test/bun/use-slate.spec.tsx --bail 1
bun test ./packages/slate-react/test/bun/use-slate-selector.spec.tsx --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
provider-hooks-contract.tsx: 4 passeduse-slate-selector.spec.tsx pre-delete probe: 1 passeduse-slate.spec.tsx pre-delete probe: failed on stale late-subscriber
global-version expectationlarge-doc-and-scroll.tsx: 15 passeddom-text-sync-contract.ts: 1 passedprojections-and-selection-contract.tsx: 6 passedslate-dom + slate-react build: passedslate-dom + slate-react typecheck: passedOwner classification:
use-slate-selector.spec.tsx: duplicate stale fixtureuse-slate.spec.tsx: stale legacy expectation around useSlateWithVprovider-hooks-contract.tsxRejected tactics:
test/bun/** as a parallel test namespace after all behavior was
either duplicated or staleCheckpoint:
slate-react fixture clusters are gonecreateEditor() usages appear tied to projection/store/render
contracts, not DOM event liar rowsslate-react contract test sweep to catch accidental
fixture deletion falloutpackages/slate-react/test/** contract sweep, then
reassess whether Batch 2 should pivot to cross-browser generated gauntlet
closureStatus: in progress.
Actions:
slate-react contract sweep over canonical non-wrapper
test filesapp-owned-customization.tsx mounted dynamic React/DOM contracts with bare
core editorswithReact(createEditor())withReact read Android
capability through a module-load slate-dom constantwithReactEvidence:
bun test ./packages/slate-react/test/app-owned-customization.tsx --bail 1
bun test ./packages/slate-react/test/with-react-contract.tsx --bail 1
bun test ./packages/slate-react/test/annotation-store-contract.tsx ./packages/slate-react/test/app-owned-customization.tsx ./packages/slate-react/test/dom-repair-policy-contract.ts ./packages/slate-react/test/dom-text-sync-contract.ts ./packages/slate-react/test/editable-behavior.tsx ./packages/slate-react/test/editing-kernel-contract.ts ./packages/slate-react/test/large-doc-and-scroll.tsx ./packages/slate-react/test/primitives-contract.tsx ./packages/slate-react/test/projections-and-selection-contract.tsx ./packages/slate-react/test/provider-hooks-contract.tsx ./packages/slate-react/test/react-editor-contract.tsx ./packages/slate-react/test/selection-controller-contract.ts ./packages/slate-react/test/surface-contract.tsx ./packages/slate-react/test/use-selected.test.tsx ./packages/slate-react/test/widget-layer-contract.tsx ./packages/slate-react/test/with-react-contract.tsx --bail 1
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation|Backspace|Delete|kernel|transition" --workers=1 --retries=0
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
app-owned-customization.tsx: 4 passedwith-react-contract.tsx: 1 passedslate-react contract sweep: 59 passed14 passedslate-dom + slate-react build: passedslate-dom + slate-react typecheck: passedOwner classification:
withReact
must evaluate Android capability when the editor is created, not at module
import timeslate-react contract sweep: green after the fixture and
runtime fixesRejected tactics:
withReact depend on a stale module-load browser capability for a
creation-time behavior branchCheckpoint:
slate-react contract sweep is greenStatus: complete for the Batch 2 substrate/breadth owner.
Actions:
29 failed / 183 passedslate-browser point helpers assumed one DOM string per Slate text nodedata-slate-path even when rendered text hosts only
preserved DOM orderharness.type() focused the editor and clobbered the browser DOM selection
created by native click setup_next/data/... access-control page errors were
harness noise, not editor runtime failuresnative-selection instead of
unknown-selectionslate-browser Playwright helpers to:
data-slate-path is unavailableharness.type()native-selection reason for explicit DOM import while still requiring
selectionPolicy.kind === 'import-dom'slate-browser before rerunning static Playwright proofEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/plaintext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --workers=1 --retries=0
bunx turbo build --filter=./packages/slate-browser --force
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "highlighted text|keeps caret editable after cutting inline link text|applies toolbar heading from browser target even when model selection is already heading" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=webkit --grep "applies toolbar heading to the browser-selected paragraph|applies toolbar alignment from browser target even when model selection already has alignment|imports programmatic DOM selection through explicit browser handle" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/plaintext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun --filter slate-react test
bun --filter slate-browser test
Results:
29 failed, 183 passed8 passed40 passed3 passed212 passed1 fileslate-browser + slate-dom + slate-react build: passedslate-browser + slate-dom + slate-react typecheck: passedslate-react package tests: 39 passedslate-browser package tests: 18 passedOwner classification:
Rejected tactics:
import-dom selection
policyCheckpoint:
slate-browser substrate now handles projected/decorated
multi-leaf text instead of relying on local test hackseditor.update targeteditor.updateStatus: in progress.
Actions:
editor.updateat resolves through the active transaction targetat bypasses target freshnesswrapNodesremoveNodessplitNodesinsertTextdeleteinsertFragmentunwrapNodesliftNodesmoveNodesmergeNodesinsertNodesinsertBreakdeleteBackwarddeleteForwarddeleteFragmentinitialAt behavior preservedEvidence:
bun test ./packages/slate/test/primitive-method-runtime-contract.ts --bail 1
bun test ./packages/slate/test/transaction-target-runtime-contract.ts --bail 1
bun test ./packages/slate/test/read-update-contract.ts ./packages/slate/test/transaction-contract.ts ./packages/slate/test/commit-metadata-contract.ts --bail 1
bun test ./packages/slate/test/editor-methods-contract.ts ./packages/slate/test/transforms-contract.ts --bail 1
bun test ./packages/slate/test --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/plaintext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
15 passed4 passed26 passed15 passedpackages/slate/test suite: 1015 passed, 94 skippedslate build: passedslate typecheck: passed210 passed, 2 failedOwner classification:
insertText with active marks lost implicit selection
advancement when the resolved transaction target started being passed as an
explicit at to nested insertNodesruns generated mark typing gauntlet without illegal kernel transitions in
Chromium and FirefoxRejected tactics:
Checkpoint:
at
semanticsinsertText using the fresh
transaction target and advancing selectioninsertText, then rerun
focused core and browser gatesStatus: complete for the current primitive-runtime owner.
Actions:
insertText through a fresh
transaction targetinsertText so an implicit-target insert selects after the
inserted marked leaf while user-explicit at still bypasses implicit
selection behaviorset_selection updates the cached implicit target to the live
model selection so later primitives do not re-import stale DOM selection[0,1], while the string contract
stays etsetNodes and unsetNodesslateEvidence:
bun test ./packages/slate/test/primitive-method-runtime-contract.ts --bail 1
bunx turbo build --filter=./packages/slate --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --grep "runs generated mark typing gauntlet without illegal kernel transitions" --workers=1 --retries=0
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun test ./packages/slate/test/read-update-contract.ts ./packages/slate/test/commit-metadata-contract.ts ./packages/slate/test/primitive-method-runtime-contract.ts ./packages/slate/test/editor-methods-contract.ts ./packages/slate/test/bookmark-contract.ts ./packages/slate/test/transaction-contract.ts ./packages/slate/test/transaction-target-runtime-contract.ts ./packages/slate/test/snapshot-contract.ts ./packages/slate/test/surface-contract.ts --bail 1
bun test ./packages/slate/test --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/plaintext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
bunx turbo build --filter=./packages/slate --filter=./packages/slate-dom --filter=./packages/slate-react --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --filter=./packages/slate-browser --force
Results:
M followed by plain
ARK18 passedslate rebuild:
2 passed190 passed262 passed1015 passed, 94 skippedslate build: passedslate typecheck: passed212 passedreadChildrenLengthAfterEachMs: 0.71ms current vs 1.52ms legacynodesAtRootAfterEachMs: 6.44ms current vs 9.79ms legacypositionsFirstBlockAfterEachMs: 0.84ms current vs 2.19ms legacyselectAllMs:
startBlockTypeMs: 0.61ms current vs 0.76ms legacymiddleBlockTypeMs: 0.33ms current vs 0.54ms legacyreplaceFullDocumentWithTextMs: 3.67ms current vs 9.49ms legacyinsertFragmentFullDocumentMs: 4.02ms current vs 9.14ms legacyselectAllMs: 0.01ms current vs 0.01ms legacyslate, slate-dom, slate-react,
slate-browser): passedslate-dom with transient
workspace resolution errors for module slateslate-dom, slate-react, and
slate-browser: passedOwner classification:
slate was invalid evidence; the
rebuilt focused row proved the source patchRejected tactics:
MARK browser assertionsetNodes/unsetNodes remain outside the primitive runtime matrixCheckpoint:
94 skipped) and belongs to
later closure, not this primitive target sliceTransforms.* hard cuts are still openbookmark-contract, collab-history-runtime-contract, and
history package contracts before React workStatus: complete for the current commit/bookmark/history/collaboration owner.
Actions:
selectionChanged: true when the operation moves selectioninsert_text and set_selection metadata capture so
selectionBefore and marksBefore are read before mutationselectionChanged: true.tmp/slate-v2/packages/slate/test/collab-history-runtime-contract.tsslate and slate-history gates, including the full slate package
suite and core perf guardrailsEvidence:
bun test ./packages/slate/test/commit-metadata-contract.ts --bail 1
bun test ./packages/slate/test/commit-metadata-contract.ts ./packages/slate/test/transaction-contract.ts --grep "selection-only commit metadata|captures update tags|insertText commit metadata" --bail 1
bun test ./packages/slate/test/commit-metadata-contract.ts ./packages/slate/test/bookmark-contract.ts ./packages/slate/test/collab-history-runtime-contract.ts --bail 1
bun test ./packages/slate-history/test/history-contract.ts ./packages/slate-history/test/integrity-contract.ts --bail 1
bun test ./packages/slate/test/commit-metadata-contract.ts ./packages/slate/test/bookmark-contract.ts ./packages/slate/test/collab-history-runtime-contract.ts ./packages/slate/test/transaction-contract.ts ./packages/slate/test/surface-contract.ts ./packages/slate/test/snapshot-contract.ts --bail 1
bun test ./packages/slate-history --bail 1
bun test ./packages/slate/test --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --filter=./packages/slate-history --force
bunx turbo typecheck --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate-history --force
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Results:
selectionChanged: false3 passed10 passed20 passed233 passedslate-history package suite: 14 passed, 1 skippedpackages/slate/test suite: 1015 passed, 94 skippedslate + slate-history build: passedslate typecheck: passedslate-history typecheck failed with stale/empty slate/dist
workspace artifacts; direct slate package build restored dist, and the
rerun passedreadChildrenLengthAfterEachMs: 1.01ms current vs 1.19ms legacynodesAtRootAfterEachMs: 6.94ms current vs 8.71ms legacypositionsFirstBlockAfterEachMs: 1.04ms current vs 1.73ms legacystartBlockTypeMs: 0.72ms current vs 0.69ms legacymiddleBlockTypeMs: 0.37ms current vs 0.52ms legacyreplaceFullDocumentWithTextMs: 3.73ms current vs 8.49ms legacyinsertFragmentFullDocumentMs: 4.10ms current vs 8.39ms legacyselectAllMs: 0.01ms current vs 0.01ms legacyOwner classification:
selectionChanged was a real commit metadata bug, not a history test issuedist artifacts can make slate-history typecheck look broken after
Turbo build; direct package build is a valid recovery probe, not a product
workaroundRejected tactics:
selectionChangedslate-history typecheck after stale artifacts caused a false
module-resolution failureCheckpoint:
slate and slate-history suites are green for current expectations94 skipped Slate rows remain and are a later closure ownerTransforms.* still teach stale-state habitsTransforms.*editor.selection behind docs language that still implies it
is fresh mutable stateTransforms.* as the primary mutation storyStatus: complete for the current public hard-cut owner.
Actions:
.tmp/slate-v2/packages/slate/test/public-field-hard-cut-contract.ts.tmp/slate-v2/packages/slate/test/write-boundary-contract.tseditor.childreneditor.selectioneditor.markseditor.operationsBaseEditor type surface so those mirrors are readonlysetChildren(...) instead of
assigning to the public fieldeditor.setChildren(...)editor.select(...)editor.addMark(...)Editor.replace(...)slate-hyperscript editor creation to initialize through
setChildren(...) and Editor.replace(...)Transforms.* usage:
Editor.apply or onChange as
extension interception pointsEditor.replace(...) when available, with legacy assignment fallback for the
old repoEvidence:
bun test ./packages/slate/test/public-field-hard-cut-contract.ts ./packages/slate/test/write-boundary-contract.ts --bail 1
bun test ./packages/slate/test/surface-contract.ts ./packages/slate/test/interfaces-contract.ts ./packages/slate/test/transaction-contract.ts ./packages/slate/test/accessor-transaction.test.ts ./packages/slate/test/snapshot-contract.ts ./packages/slate-history/test/integrity-contract.ts --bail 1
bun test ./packages/slate/test --bail 1
bun test ./packages/slate-history --bail 1
rg -n "\\bTransforms\\." docs/concepts docs/walkthroughs docs/api -g '!CHANGELOG.md'
rg -n "editor\\.(children|selection|marks|operations)\\s*=" packages/slate/test packages/slate-history/test packages/slate-hyperscript/src packages/slate/src packages/slate-history/src -g '!node_modules'
rg -n "wrapp\\w+ .*Editor\\.apply|editor\\.apply\\s*=|editor\\.onChange\\s*=|listen to writes by wrapping|Callback method" docs/concepts docs/walkthroughs docs/api -g '!CHANGELOG.md'
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --filter=./packages/slate-history --filter=./packages/slate-hyperscript --force
bunx turbo typecheck --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate-history --force
bunx turbo typecheck --filter=./packages/slate-hyperscript --force
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Results:
3 passed245 passedpackages/slate/test suite: 1015 passed, 94 skippedslate-history package suite: 14 passed, 1 skippedTransforms.* in docs/concepts, docs/walkthroughs,
or docs/api, excluding changelogsonChange override as extension pointsslate, slate-history, and slate-hyperscript build: passedslate module
resolution in dependent packagesslate build plus separate package typechecks passed for:
slateslate-historyslate-hyperscripteditor.children = ...readChildrenLengthAfterEachMs: 0.93ms current vs 1.19ms legacynodesAtRootAfterEachMs: 7.84ms current vs 8.75ms legacypositionsFirstBlockAfterEachMs: 1.31ms current vs 1.73ms legacystartBlockTypeMs: 0.52ms current vs 0.92ms legacymiddleBlockTypeMs: 0.37ms current vs 0.80ms legacyreplaceFullDocumentWithTextMs: 3.35ms current vs 9.54ms legacyinsertFragmentFullDocumentMs: 2.98ms current vs 10.63ms legacyselectAllMs: 0.03ms current vs 0.01ms legacyOwner classification:
slate-hyperscript and benchmarks were stale fixture/setup owners, not
reasons to keep public field writes aliveEditor.apply remains the explicit low-level operation writer, but primary
docs no longer teach it as a plugin interception pointonChange still exists for framework callbacks and internal wrappers;
replacement extension ergonomics belong to Batch 6 and React package cleanup
belongs to Batch 7Rejected tactics:
Checkpoint:
slate-react and slate-dom
compose behavioreditor.update(...) and primitive editor methodsapply/onChange composition still exists outside primary docsextension-methods-contract.tsStatus: complete for the current extension-runtime owner.
Actions:
.tmp/slate-v2/packages/slate/test/extension-methods-contract.tsdefineEditorExtension(...)editor.extend(...)Editor.extend(editor, extension)Editor.defineEditorExtension(extension)extend to the public editor method surface contracteditor.extend(...) and
defineEditorExtension(...) instead of direct method replacementEvidence:
bun test ./packages/slate/test/extension-methods-contract.ts --bail 1
bun test ./packages/slate/test/extension-methods-contract.ts ./packages/slate/test/extension-contract.ts ./packages/slate/test/transaction-contract.ts ./packages/slate/test/surface-contract.ts --bail 1
bun test ./packages/slate/test --bail 1
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate --force
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
rg -n "editor\\.\\w+\\s*=|with[A-Z][A-Za-z]* = editor|with[A-Z][A-Za-z]*\\(createEditor|override command|override the behaviors|methods to override|schema-specific-instance-methods-to-override|Callback Method|accessor seam" docs/concepts docs/walkthroughs docs/api -g '!CHANGELOG.md'
Results:
defineEditorExtension was not exported3 passed42 passedpackages/slate/test suite: 1015 passed, 94 skipped190 passedslate build: passedslate typecheck failed on unsafe Editor to record casts inside the
extension runtime; local mutable-record helper fixed the cast boundaryslate typecheck rerun: passedwithReact /
withYjs framework wrappers and no direct method-replacement plugin storyreadChildrenLengthAfterEachMs: 0.77ms current vs 1.48ms legacynodesAtRootAfterEachMs: 7.08ms current vs 9.71ms legacypositionsFirstBlockAfterEachMs: 0.58ms current vs 2.11ms legacystartBlockTypeMs: 0.54ms current vs 1.05ms legacymiddleBlockTypeMs: 0.51ms current vs 0.66ms legacyreplaceFullDocumentWithTextMs: 4.38ms current vs 10.38ms legacyinsertFragmentFullDocumentMs: 4.17ms current vs 9.32ms legacyselectAllMs: 0.04ms current vs 0.01ms legacyOwner classification:
withReact, withYjs, and cursor wrappers remain later React/collab package
owners, not Batch 6 core blockersRejected tactics:
Checkpoint:
editor.update(...) from React command pathsStatus: complete for the current React target-runtime owner.
Actions:
.tmp/slate-v2/packages/slate-react/test/target-runtime-contract.tsxeditor.setBlock({ type: 'heading-one' }) updates paragraph 2resolveEditableImplicitTarget(...)Editable command paths to use that owner instead of local
click/keydown target patchesEvidence:
bun test ./packages/slate-react/test/target-runtime-contract.tsx --bail 1
bun test ./packages/slate-react/test/target-runtime-contract.tsx ./packages/slate-react/test/editing-kernel-contract.ts ./packages/slate-react/test/selection-controller-contract.ts ./packages/slate-react/test/dom-repair-policy-contract.ts ./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
cd ./packages/slate-react && bun run test
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate --filter=./packages/slate-dom --filter=./packages/slate-react --force
cd ./packages/slate && bun run build
bunx turbo typecheck --filter=./packages/slate --force
bunx turbo typecheck --filter=./packages/slate-dom --force
bunx turbo typecheck --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "toolbar|selection|navigation|delete|paste|undo" --workers=1 --retries=0
Results:
resolveEditableImplicitTarget did not exist2 passed10 passed15 passed6 passedslate-react package test script: 39 passedslate, slate-dom, and slate-react build: passedslate/dist is being rewrittenslate build plus sequential package typechecks passed for:
slateslate-domslate-react21 passed
Owner classification:
Rejected tactics:
tx.resolveTarget() or command policy objects to app/plugin authorsslateCheckpoint:
Status: complete for the current browser-closure owner.
Actions:
Transforms.select(...)slate-browser Playwright typing so an app-owned control focused
inside the editor root is not treated as usable editor keyboard focusslate-browser shadow-DOM selection helpers to use the editor root's
Document | ShadowRoot selection API when availablewaitForSelectionIfPresent(...) to wait only for selections already
contained by the editor root, so WebKit shadow-DOM semantic rows do not hang
on a non-contained document selectiondist before browser proof, because Playwright tests import
slate-browser/playwright from package outputEvidence:
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/check-lists.test.ts --project=chromium --project=firefox --grep "keeps selection through focus on checkbox" --workers=2 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "deletes backward after directly synced model typing" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=mobile --grep "keeps caret editable after rich HTML paste over selected content" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=webkit --grep "edits content|generated shadow DOM|add a new line" --workers=1 --retries=0
bun test:integration-local
bun run lint:fix
bun run lint
cd ./packages/slate-react && bun run test
bun --filter slate-browser test
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
cd ./packages/slate && bun run build
bunx turbo typecheck --filter=./packages/slate-browser --force
bunx turbo typecheck --filter=./packages/slate-dom --force
bunx turbo typecheck --filter=./packages/slate-react --force
Results:
bun test:integration-local failed with 481 passed, 7 failedShadowRoot2 passed4 passed1 passed3 passed488 passedslate-react package test script: 39 passedslate-browser package test script: 18 passedslate-browser, slate-dom,
slate-reactslate build plus sequential package typechecks passed for:
slate-browserslate-domslate-reactOwner classification:
dist; browser harness edits need a package
build before Playwright evidence is trustworthyRejected tactics:
dist was staleCheckpoint:
dist must stay fresh before Playwright rowsbun test:integration-local green with no broad skip debtdist Playwright output as evidenceStatus: complete for the current master-plan closure target.
Actions:
docs/solutions/logic-errors/2026-04-24-slate-browser-proof-must-separate-model-owned-handles-root-selection-and-usable-focus.mdEvidence:
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 bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Results:
000middleBlockType: +12.39msmiddleBlockSelectThenType: +20.92msOwner classification:
Rejected tactics:
Checkpoint:
488 passedbun test:integration-local and the Batch 9 perf gates green
before future closure claimsactive goal state to done and run bun completion-checkdist as browser evidenceDo not call this complete because a few toolbar rows are green.
Stop only when:
While paused:
active goal state should stay blocked