docs/plans/2026-04-23-slate-v2-remaining-perfect-architecture-batches-plan.md
Batch 1 and Batch 2 succeeded at the right thing: they built and hardened the authoritative command/kernel spine.
Do not keep stretching that plan.
The remaining work should split into three focused batches:
Each batch has a different owner and should not be blurred into generic "kernel work."
The architecture is no longer weak because Editable lacks a spine.
The architecture is still not perfect because:
That means the next work is not another broad rewrite. It is targeted closure of the remaining proof and API/perf debt.
From Batch 1:
EditorCommit.command existseditor.applyFrom Batch 2:
EditableEditingKernel exposes explicit selection and repair policiesKeep this foundation. Do not replace it.
Make browser proof honest by closing or explicitly accepting every remaining red row from Batch 2.
This is correctness proof work, not API polish and not core perf.
Every red row must end with an owner classification before code changes are called done.
Allowed owner classes:
No local fix may land as "done" without naming one of those owners.
If the owner is unclear, add characterization first. Do not patch the symptom.
Each row must prove both:
When the claim involves selection, it must also state which selection truth is being proved:
If a browser red is caused by a compat surface that Batch 4 plans to hard-cut, pull that exact hard cut forward into Batch 3. Do not wait for Batch 4 when the compat surface is the proven owner.
Current state:
mentions example › shows list of mentionsmentions example › inserts from listKnown evidence:
editor.selection to Editor.getLiveSelection did
not close itLikely owner:
site/examples/ts/mentions.tsxonChange semantics around void inline boundariesDo:
beforeText, beforeMatch, and afterMatch debug state if exposedDo not:
Primary files:
.tmp/slate-v2/site/examples/ts/mentions.tsx.tmp/slate-v2/playwright/integration/examples/mentions.test.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx only if the
failing proof proves runtime ownershipGates:
bunx playwright test ./playwright/integration/examples/mentions.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/mentions.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Status: complete with mobile trigger transport accepted.
Actions:
ReactEditor.toDOMRange(editor, target) cannot resolve immediately after a
text mutation.editor.selection to
Editor.getLiveSelection(editor).Owner classification:
editor.selection instead of the live selection APIEvidence:
bunx playwright test ./playwright/integration/examples/mentions.test.ts --project=chromium --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/mentions.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
3 passed12 passedDecision:
Current state:
large document runtime example › selects void content by browser click without mutating contentLikely owner:
Do:
Do not:
Primary files:
.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts.tmp/slate-v2/site/examples/ts/large-document-runtime.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx.tmp/slate-v2/packages/slate-react/src/large-document/**Gates:
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "void content by browser click" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "void content by browser click|generated shell activation" --workers=4 --retries=0
Status: complete for the previous scoped batches; reopened by the DOM bridge integrity review below.
Actions:
isInteractiveInternalTarget treated every [contenteditable=false]
descendant as internal, which included Slate void content.input, textarea, select, button, [role="button"], and nested
editor roots.[role="button"].Owner classification:
Evidence:
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 "void content by browser click|generated shell activation" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "void content by browser click|generated shell activation" --workers=4 --retries=0
bun run lint:fix
bun run lint
Results:
2 passed8 passedDecision:
Current state:
editable voids › keeps nested editor input focused inside editable voidLikely owner:
Do:
Do not:
Primary files:
.tmp/slate-v2/playwright/integration/examples/editable-voids.test.ts.tmp/slate-v2/site/examples/ts/editable-voids.tsx.tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts.tmp/slate-v2/packages/slate-dom/** only if DOM bridge ownership is provenGates:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=mobile --grep "nested editor input focused" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "nested editor|internal-control" --workers=4 --retries=0
Status: complete with mobile DOM-selection observability narrowed.
Actions:
null for DOM selection while semantic
model proof was green.Owner classification:
Evidence:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=mobile --grep "nested editor input focused" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "nested editor|internal-control" --workers=4 --retries=0
Results:
12 passedDecision:
Status: complete.
Closed owners:
Decision:
Status: complete.
Actions:
Evidence:
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
Results:
296 passedFinal Batch 3 owner classification:
contenteditable=false descendants as internal
controlsDecision:
Make the public API match the architecture:
EditabledecorateThis batch is public surface cleanup. Do not mix it with browser red-row fixes.
Batch 4 work normally starts after Batch 3.
Exception:
Hard cuts must be source-of-truth docs too: docs should describe only the final API, not migration history.
decorate Public SurfaceCurrent state:
createSlateDecorateCompatSource exists in projection-store compatibility
codeDo:
decorateDo not:
Files:
.tmp/slate-v2/packages/slate-react/src/projection-store.ts.tmp/slate-v2/site/examples/ts/**docs/slate-v2/**Status: complete.
Actions:
.tmp/slate-v2/packages/slate-react/src/compat/index.ts.createSlateDecorateCompatSource and related compat types from the
primary slate-react root export list.SlateReactCompat namespace export from root.SlateReactCompat.createSlateDecorateCompatSource.createSlateDecorateCompatSource.Evidence:
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
rg -n "createSlateDecorateCompatSource|SlateDecorateCompat" .tmp/slate-v2/packages/slate-react/src/index.ts .tmp/slate-v2/packages/slate-react/test .tmp/slate-v2/site/examples/ts -g "*.ts" -g "*.tsx"
bun run lint:fix
bun run lint
Results:
6 passedDecision:
decorate compatibility remains available only through an explicit compat
namespace.Next owner:
Current state:
editor.selection = ...editor.marks = ...Do:
Editor.*Do not:
Files:
.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/editable/composition-state.ts.tmp/slate-v2/packages/slate-react/src/hooks/android-input-manager/**Status: complete for slate-react runtime writes.
Actions:
editor.marks = ... in composition handling with
setCurrentMarks(...).editor.marks = ... in Android input manager with
setCurrentMarks(...).editor.selection = ... controlled-value fallback in
selection reconciler with setCurrentSelection(...).editor.selection = or editor.marks = writes remain in
slate-react source.Evidence:
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
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
rg -n "editor\\.selection\\s*=|editor\\.marks\\s*=" .tmp/slate-v2/packages/slate-react/src -g "*.ts" -g "*.tsx"
bun run lint:fix
Results:
Decision:
editor.onChange patching.editor.onChange PatchingCurrent state:
editor.onChangeDo:
Editor.subscribe /
commit-listener pathsDo not:
Files:
.tmp/slate-v2/packages/slate-react/src/components/slate.tsx.tmp/slate-v2/packages/slate-react/src/hooks/use-slate.tsx.tmp/slate-v2/packages/slate/src/core/public-state.tseditor.onChange Patching ResultStatus: complete for slate-react React-side patching.
Actions:
editor.onChange = ... patch from
components/slate.tsx.useSlateWithV to use editor.subscribe(...) instead of patching
editor.onChange.EDITOR_TO_ON_CHANGE registration for the DOM bridge path, but React no
longer monkeypatches the editor method.Evidence:
bun test ./packages/slate-react/test/provider-hooks-contract.tsx --bail 1
bun test ./packages/slate-react/test/surface-contract.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
rg -n "editor\\.onChange\\s*=" .tmp/slate-v2/packages/slate-react/src -g "*.ts" -g "*.tsx"
bun run lint:fix
bun run lint
Results:
4 passed3 passed6 passededitor.onChange = scan: no matchesDecision:
editor.onChange as its primary
subscription path.onDOMBeforeInput example bypass.Current state:
onKeyCommandinputRulesonDOMBeforeInput for Android/native schedulingDo:
Files:
.tmp/slate-v2/site/examples/ts/markdown-shortcuts.tsx.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.tsStatus: complete with markdown Android scheduling deferred.
Actions:
onKeyCommand in Batch 2/3 execution.Editable.inputRules.onDOMBeforeInput.Decision:
onDOMBeforeInput is not a normal mutation bypass. It
reads Android pending diffs and schedules an Android flush before the Android
input manager returns from beforeinput.inputRules would not preserve timing because input
rules do not currently run before the Android manager branch.Disposition:
decorateonChange as primary subscription pathStatus: complete with explicit deferred compat owners.
Complete:
decorate root export moved behind SlateReactCompat.editor.selection = and editor.marks = writes removed.editor.onChange = patching removed.onKeyCommand.inputRules.Accepted/deferred:
onDOMBeforeInput; needs a dedicated
Android scheduling policy API.projection-store.ts still implements decorate compatibility behind compat
namespace.EDITOR_TO_ON_CHANGE compatibility.Decision:
Make core perf excellent on its own, not merely good enough for the current React runtime.
This is core runtime work. Do not mix it with public API hard cuts or browser red-row fixes.
Batch 5 is core-only unless a benchmark proves React/slate-react ownership.
Do not change public API shape for a perf shortcut.
Do not use 5000-block React success as a substitute for core perf closure.
Each optimization must name:
If a perf change touches transaction, selection, runtime ids, or snapshots, run the transaction/history/snapshot contracts before calling the slice done.
Target:
EditorCommit semanticsFiles:
.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate/src/core/apply.tsGates:
bun test ./packages/slate/test/transaction-contract.ts --bail 1
bun run bench:core:observation:compare:local
Target:
Editor.getSnapshot()
in urgent pathsFiles:
.tmp/slate-v2/packages/slate/src/core/get-dirty-paths.ts.tmp/slate-v2/packages/slate/src/core/public-state.tsGates:
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun run bench:core:huge-document:compare:local
Target:
Files:
.tmp/slate-v2/packages/slate/src/utils/runtime-ids.ts.tmp/slate-v2/packages/slate/src/core/public-state.tsGates:
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
Target:
Files:
.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate-react/src/** only if a core API change needs
consumer updatesGates:
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun run bench:core:observation:compare: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
Status: complete by measurement, no core code changes needed.
Actions:
startBlockTypeMs delta.Evidence:
bun run bench:core:observation:compare:local
bun run bench:core:huge-document:compare:local
bun run bench:core:huge-document:compare:local
bun test ./packages/slate/test/transaction-contract.ts --bail 1
bun test ./packages/slate/test/snapshot-contract.ts --bail 1
bun test ./packages/slate/test/surface-contract.ts --bail 1
cd packages/slate && bun run build
cd packages/slate && bun run typecheck
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
Results:
23 passed190 passed10 passed5000-block React huge-doc means:
12.76ms vs legacy chunk-off 260.72ms and chunk-on 291.23ms0.12ms vs 14.83ms and 0.79ms5.54ms vs 159.11ms and 36.13ms1.01ms vs 180.44ms and 52.04ms1.12ms vs 154.02ms and 39.69ms0.55ms vs 167.67ms and 49.11ms10.42ms vs 181.17ms and 33.65ms26.51ms vs 107.79ms and
111.05ms21.69ms vs 116.01ms and 107.19msDecision:
Reason:
Exception order:
Each batch gets its own active plan and its own active goal state
pending/done cycle.
Do not reuse Batch 1 or Batch 2 as active execution ledgers.
Do not mark a batch complete just because another batch is complete.
Status: complete.
Completed:
296 passeddecorate root export moved to SlateReactCompateditor.selection = / editor.marks = writes removededitor.onChange = patching removedRemaining future owners:
Final checkpoint:
onDOMBeforeInputReopen before any further "perfect architecture" claim.
The completed batches improved the command/kernel spine and browser proof, but they did not close the DOM bridge contract. The reported richtext crash after selecting text, toggling bold, and clicking another word proves that user event paths can still dereference stale DOM-to-Slate mappings and throw.
This is not a one-row richtext bug. It is a bridge integrity and browser editing authority bug class.
Legacy ../slate:
packages/slate-react/src/components/text.tsx maps
ELEMENT_TO_NODE, NODE_TO_ELEMENT, and key-to-element in a ref callback
whose dependencies include the Slate text object.packages/slate-react/src/components/element.tsx does the same for element
objects.packages/slate-react/src/components/editable.tsx still has a dangerous
click path that calls ReactEditor.toSlateNode(editor, event.target) and
ReactEditor.findPath(editor, node), but legacy's render binding usually
refreshes the map when node object identity changes.Current .tmp/slate-v2:
packages/slate-react/src/hooks/use-slate-node-ref.tsx binds DOM nodes by
runtimeId and DOM element, not by the current live Slate node object.editor, DOM node, and runtimeId; it does
not rerun when the live Slate node object is replaced under the same runtime
id.packages/slate-react/src/editable/selection-reconciler.ts still uses the
legacy throwing click path:
toSlateNode(event.target) -> findPath(node).ELEMENT_TO_NODE can point at an old Slate text object while
NODE_TO_PARENT / NODE_TO_INDEX describe the current live object. Then
findPath throws.Decision:
The plan closed measured rows, not the browser editing state space.
The kernel can classify events correctly and still crash if the DOM bridge hands it stale Slate objects. That means the bridge is not yet a trustworthy runtime primitive.
Worse: model-state assertions can still pass while the visible caret is wrong, DOM selection is wrong, follow-up typing lands in the wrong place, or the app throws only after a click/import path that the tests never exercised.
That is why the next batch cannot be "fix the bold click crash." It has to be the blocking batch that makes browser editing authority explicit and provable.
Make browser editing truth authoritative under the v2 runtime model.
User-event paths must not depend on stale object weak maps, model-only proof, or implicit browser repair luck. They must resolve DOM targets to the current live node/path or fail closed with a classified kernel result, and the proof stack must catch visible caret/cursor regressions instead of only model drift.
pageerror or unexpected editor
console errors. A hidden runtime throw is a red row.toSlateNode, findPath, toSlateRange, or toDOMRange.Harden the proof harness first.
Add browser-proof guardrails before changing runtime code:
pageerrorExtend slate-browser helpers so scenario rows can assert:
Primary files:
.tmp/slate-v2/packages/slate-browser/**.tmp/slate-v2/playwright/integration/examples/**Characterize the reported class.
Add a red browser gauntlet for:
Add variants for:
renderLeafrenderTextPrimary files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.ts.tmp/slate-v2/packages/slate-browser/**Replace stale object binding with live bridge binding.
useSlateNodeRef must update the DOM bridge when the live node identity or
path changes, not only when the DOM element/runtime id changes.
Preferred shape:
runtimeIdPrimary files:
.tmp/slate-v2/packages/slate-react/src/hooks/use-slate-node-ref.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text.tsx.tmp/slate-v2/packages/slate-react/src/components/slate-element.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsxAdd safe bridge resolution.
Add or expose a small bridge resolver layer that can answer:
The resolver should use, in order:
[data-slate-node]Editor.getPathByRuntimeIdEditor.getLiveNode / Editor.getLiveTextPrimary files:
.tmp/slate-v2/packages/slate-dom/src/plugin/dom-editor.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/hooks/use-slate-node-ref.tsxMove ordinary click and selection import onto explicit authority rules.
Recover legacy timing discipline, not legacy's dangerous assumptions.
Requirements:
toSlateNode -> findPathPrimary files:
.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.tsAudit all editable user-event bridge calls.
Replace unguarded bridge calls in user-event paths:
selection-reconciler.tsdom-repair-queue.tsmodel-input-strategy.tsclipboard-input-strategy.tsEditableEditingKernelKeep throwing calls only in code paths where a thrown programmer error is correct and not triggered by normal browser timing.
Expand generated gauntlets.
slate-browser needs generated bridge-integrity scenarios, not only
example rows.
Required scenario families:
Every scenario must assert:
Recover legacy source knowledge where it still matters.
Source-first review current ../slate around:
Recover the timing discipline and comments that still describe live browser behavior. Do not recover the legacy monolith or its broad unsafe bridge assumptions.
Primary files:
../slate/packages/slate-react/src/components/editable.tsx../slate/packages/slate-dom/src/plugin/dom-editor.ts.tmp/slate-v2/packages/slate-react/src/**
and .tmp/slate-v2/packages/slate-dom/src/**Preserve React-perfect runtime performance.
DOM bridge repair must not reintroduce full snapshot reads or broad React rerenders in urgent text paths.
Required perf guard:
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:react:rerender-breadth:local
Focused red class:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "mark.*click|bold.*click|selection.*click" --workers=1 --retries=0
Cross-project editing proof:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Generated/browser harness proof:
bunx playwright test ./playwright/integration/examples/generated-*.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Kernel/bridge contracts:
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
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-dom/test/bridge.ts --bail 1
Build/type/perf:
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
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
This batch blocks the architecture from being called "perfect."
Batch 1/2 fixed command/kernel authority. Batch 6 fixes browser-editing authority and bridge truth. Without both, v2 still has the same failure mode: model state may be correct while the browser-visible caret, DOM selection, or DOM-to-Slate mapping is wrong.
Status: complete for the first tracer; Batch 6 remains open.
Actions:
active goal state to status: pending.recordSlateBrowserRuntimeErrors(page) in slate-browser
Playwright helpers.slate-react click resolution:
data-slate-path / Editor.getLiveNode
firsttoSlateNode / findPath stays as fallbacknull instead of throwing in the user event pathuseSlateNodeRef so DOM bridge compatibility maps can refresh when a
live Slate node changes under the same runtime id/path.Red evidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "marking selected text then clicking elsewhere" --workers=1 --retries=0
Initial result:
Unable to find the path for Slate node: {"text":"text","bold":true}Green evidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "marking selected text then clicking elsewhere" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "bold|click|selection|cursor|caret" --workers=1 --retries=0
bun --filter slate-browser test
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/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bun test ./packages/slate-dom/test/bridge.ts --bail 1
bun test ./packages/slate-dom/test/clipboard-boundary.ts --bail 1
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
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
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
Results:
16 passedselectAllMs is red by mean vs legacy chunking-on in two runs because v2
has outlier samples around 8-12ms; this is not closedOwner classification:
slate-react click DOM bridge imported a stale Slate text
object from weak-map compatibility stateChanged files:
.tmp/slate-v2/packages/slate-browser/src/playwright/index.ts.tmp/slate-v2/packages/slate-react/src/hooks/use-slate-node-ref.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsactive goal stateRejected tactics:
Checkpoint:
toSlateNode /
findPath user-event pathsStatus: complete for generated scenario runtime-error capture; Batch 6 remains open.
Actions:
slate-browser/playwright:
recordSlateBrowserRuntimeErrors(page).editor.scenario.run(...) so generated scenarios automatically fail
after each step when a captured Slate/browser bridge runtime error appears.runtimeErrors: false.model-input-strategy.ts now resolves DOM text host through
data-slate-path / live text instead of toSlateNode -> findPath.dom-repair-queue.ts now resolves DOM text host through
data-slate-path / live text instead of toSlateNode -> findPath.clipboard-input-strategy.ts drag-over and drag-start now use live path
resolution with guarded weak-map fallback.Evidence:
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-react --force
bun --filter slate-browser test
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated|navigation-mutation|marking selected text" --workers=1 --retries=0
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/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
Results:
3 passedOwner classification:
Remaining owners:
selectAllMs outliers vs legacy chunking-onCheckpoint:
slate-browser
and use it from richtext before broadening to decorated/projected/custom
render examplesStatus: complete for reusable mark-click generated coverage; Batch 6 remains open.
Actions:
slate-browser/playwright:
clickTextOffsetassertDOMCaretcreateSlateBrowserMarkClickTypingGauntlet(...).Evidence:
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "generated|navigation-mutation|marking selected text" --workers=1 --retries=0
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
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
Results:
3 passedselectAllMs remains red by mean vs legacy chunking-on but green by
median; latest v2 samples 9.69, 0.15, 0.10, 0.09, 4.57, chunking-on
samples 1.02, 1.00, 2.06, 0.89, 0.88Owner classification:
selectAllMs outlier classification is still open.
The row is median-green but mean-red, so Batch 6 cannot claim perf closure
until it is either fixed, made robust, or explicitly reclassified.Remaining owners:
selectAllMs outliers.Checkpoint:
Status: complete for richtext/inlines/editable-voids cross-project proof; Batch 6 remains open because select-all outlier classification and decorated/projected/custom-render variants are still open.
Evidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Result:
188 passedDecision:
Next owner:
selectAllMs outliers:
Status: complete.
Actions:
highlighted-text.test.ts.[0,0]Evidence:
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --project=firefox --project=webkit --grep "generated mark-click|semantic selection|decorated middle" --workers=3 --retries=0
Result:
9 passedDecision:
Status: accepted/deferred as benchmark tail-latency owner, not Batch 6 browser editing blocker.
Evidence:
selectAllMs samples were median-green or near-median-green but
mean-red vs legacy chunking-on due v2 outliers.selectAllMs samples:
1.02, 1.00, 2.06, 0.89, 0.889.69, 0.15, 0.10, 0.09, 4.57bun run bench:react:rerender-breadth:local showed:
000188 passed.Classification:
act measurement for
programmatic full-document selection.Accepted/deferred rationale:
Status: complete with select-all tail-latency work explicitly deferred.
Completed:
recordSlateBrowserRuntimeErrors and default scenario runtime-error guards.Final verification:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.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 --project=firefox --project=webkit --grep "generated mark-click|semantic selection|decorated middle" --workers=3 --retries=0
bun --filter slate-browser test
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/editing-kernel-contract.ts --bail 1
bun test ./packages/slate-react/test/dom-repair-policy-contract.ts --bail 1
bun test ./packages/slate-dom/test/bridge.ts --bail 1
bun test ./packages/slate-dom/test/clipboard-boundary.ts --bail 1
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
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
Final checkpoint: