docs/plans/2026-04-28-slate-v2-root-runtime-selector-guard-hard-cut-plan.md
Done.
Execution started from complete-plan on 2026-04-28.
Execution completed on 2026-04-28.
Current next owner: none. Completion target met.
Close the remaining React/runtime architecture findings without touching public DX yet.
This plan covers only:
EditableDOMRoot root runtime ownership.Public renderVoid / void-shell DX work is intentionally out of scope. It
comes after this internal runtime cut.
The pasted review is directionally right but stale in one important detail.
EditableTextBlocks already consumes useLargeDocumentRootSources(...) and
usePlaceholderValue(...) from root-selector-sources.ts. So Finding 2 is not
"inline generic selectors still sit directly in editable-text-blocks.tsx" in
the current checkout.
The remaining problem is stricter:
root-selector-sources.ts still owns generic useSlateSelector(...) calls.EditableDOMRoot still wires too much runtime orchestration directly.React wires refs, listeners, props, and rendered children.
Runtime modules own editing policy, selection import/export, repair, Android, composition, tracing, force render, and root selector facts.
No hot render component should contain broad selector predicates or snapshot walks inline.
renderVoid.bun check.EditableTextBlocks should consume named facts only:
const rootSources = useEditableRootSources({
largeDocumentConfig,
placeholder,
promotedIslandIndex,
});
or the equivalent split hooks:
const topLevelRuntimeIds = useRootRuntimeIds()
const selectedTopLevelIndex = useSelectedTopLevelIndex(enabled)
const placeholderValue = usePlaceholderValue(placeholder)
const islandPlan = useLargeDocumentRootSources(...)
Allowed generic selector owner:
packages/slate-react/src/editable/root-selector-sources.tsForbidden generic selector owners:
components/editable-text-blocks.tsxcomponents/editable.tsxEditableDOMRoot should instantiate one root runtime facade:
const rootRuntime = useEditableRootRuntime({
editor,
inputController,
readOnly,
rootRef: ref,
scrollSelectionIntoView,
shellBackedSelection,
state,
});
It should receive runtime-owned capabilities:
const {
androidInputManagerRef,
callbackRef,
eventRuntime,
isComposing,
repairRuntime,
selectionRuntime,
traceRuntime,
} = rootRuntime;
EditableDOMRoot may attach returned refs/listeners and render. It should not
decide policy.
Add authority tests that fail when forbidden root code returns:
useSlateSelector(...) in EditableDOMRootuseSlateSelector(...) in EditableTextBlocksEditor.getSnapshot(...) in EditableTextBlocksEditableDOMRootEditableDOMRootEditableDOMRootEditableDOMRootEditableDOMRootEditableDOMRootEditableDOMRootThe only tolerated imports in EditableDOMRoot should be root facades and
React-only presentation helpers.
Purpose: make the lane honest before moving code.
Actions:
EditableDOMRoot, EditableTextBlocks, and
root-selector-sources.ts.Acceptance:
root-selector-sources.ts may use generic
useSlateSelector(...).EditableTextBlocks adds inline generic selectors or snapshot
reads.EditableDOMRoot adds inline generic selectors.Likely files:
.tmp/slate-v2/packages/slate-react/test/kernel-authority-audit-contract.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx.tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.tsDriver gates:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx
bun --filter slate-react typecheck
Purpose: complete item 1 without over-building.
Actions:
root-selector-sources.ts.useRootRuntimeIdsuseSelectedTopLevelIndexusePlaceholderValueuseLargeDocumentRootSourcesuseEditableRootCommitWakeupuseEditableRootSources(...) only if it reduces
root component churn and test setup.shouldUpdate predicates beside each source.Acceptance:
EditableTextBlocks has no useSlateSelector(...).EditableTextBlocks has no Editor.getSnapshot(...).Driver gates:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx
bun --filter slate-react typecheck
Browser gates if root rendering changes:
PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/large-document-runtime.test.ts playwright/integration/examples/search-highlighting.test.ts --project=chromium
useEditableRootRuntime(...)Purpose: close the remaining Finding 1 owner, not just shrink the file.
Actions:
editable/runtime-root-engine.ts or equivalent.EditableDOMRoot:
useEditableEventRuntime(...).useEditableRootRuntime(...) may compose existing runtime modules, but it
must not become a dumping ground. It should read like orchestration of named
runtime capabilities.Acceptance:
EditableDOMRoot calls useEditableRootRuntime(...).EditableDOMRoot does not construct selectionchange handlers directly.EditableDOMRoot does not construct Android manager directly.EditableDOMRoot does not construct repair or trace runtimes directly.EditableDOMRoot does not subscribe to selector runtime directly.EditableDOMRoot attaches returned refs/listeners and renders.Likely files:
.tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/editable/runtime-selection-engine.ts.tmp/slate-v2/packages/slate-react/src/editable/runtime-repair-engine.ts.tmp/slate-v2/packages/slate-react/src/editable/runtime-kernel-trace.ts.tmp/slate-v2/packages/slate-react/src/editable/runtime-android-engine.ts.tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.tsDriver gates:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx
bun --filter slate-react typecheck
bun --filter slate-react test:vitest test/selection-controller-contract.test.ts test/selection-runtime-contract.test.ts test/editing-kernel-contract.test.ts test/editing-epoch-kernel-contract.test.ts test/target-runtime-contract.test.ts
Browser gates:
PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/hovering-toolbar.test.ts playwright/integration/examples/richtext.test.ts playwright/integration/examples/search-highlighting.test.ts --project=chromium --grep "hovering toolbar|paste|undo|search"
PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/mentions.test.ts playwright/integration/examples/tables.test.ts playwright/integration/examples/images.test.ts playwright/integration/examples/large-document-runtime.test.ts --project=chromium
Purpose: make the architecture durable.
Actions:
kernel-authority-audit-contract.ts with a root runtime inventory.EditableDOMRoot forbidden calls/imports.EditableTextBlocks selector ownership.root-selector-sources.ts.Forbidden in EditableDOMRoot after this phase:
useSlateSelector(
Editor.getSnapshot(
useEditableRootCommitWakeup(
useRuntimeAndroidEngine(
createRuntimeSelectionChangeHandler(
createRuntimeSelectionChangeScheduler(
createRuntimeSelectionImportController(
useEditableSelectionReconciler(
subscribeSelectionOnlyDOMExport(
useRuntimeRepairEngine(
useRuntimeKernelTraceEngine(
attachEditableSelectionChangeListener(
attachEditableGlobalDragLifecycleListeners(
Allowed:
useEditableRootRuntime(
useEditableEventRuntime(
useEditableRootRef( only if root ref composition stays React-only )
If root ref composition needs selection/event policy, move it behind
useEditableRootRuntime(...) too.
Acceptance:
Driver gates:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx
bun --filter slate-react typecheck
Purpose: prove this was not cosmetic.
Actions:
bun check:full only when the lane is otherwise complete.Final gates:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx
bun --filter slate-react typecheck
bun --filter slate-react test:vitest test/selection-controller-contract.test.ts test/selection-runtime-contract.test.ts test/editing-kernel-contract.test.ts test/editing-epoch-kernel-contract.test.ts test/target-runtime-contract.test.ts
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/hovering-toolbar.test.ts playwright/integration/examples/richtext.test.ts playwright/integration/examples/search-highlighting.test.ts --project=chromium --grep "hovering toolbar|paste|undo|search"
PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/mentions.test.ts playwright/integration/examples/tables.test.ts playwright/integration/examples/images.test.ts playwright/integration/examples/large-document-runtime.test.ts --project=chromium
bun lint:fix
bun check:full
Completion criteria:
EditableTextBlocks consumes named root selector sources only.root-selector-sources.ts.EditableDOMRoot calls one root runtime facade for root policy.EditableDOMRoot calls one event runtime facade for event handlers.EditableDOMRoot.bun check:full passes.useEditableRootRuntime(...) becomes a new god hookMitigation:
Mitigation:
shouldUpdate predicates next to selector ownership.Mitigation:
bun check:full.Mitigation:
When execution starts:
active goal state to status: pending.active goal state with this plan.pending while any phase has a runnable next move.done only after Phase 4 final gates pass.blocked only when no autonomous progress is possible.Actions:
complete-plan.active goal state to status: pending.active goal state for this lane..tmp/slate-v2/packages/slate-react/test/kernel-authority-audit-contract.ts.EditableDOMRoot root runtime orchestration inventory for the
Phase 2 burn-down.Editor.getSnapshot(...) read in
EditableTextBlocks as mounted-node child runtime-id resolution, not root
selector debt.Commands:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsxbun --filter slate-react typecheckEvidence:
slate-react typecheck passed.Changed files:
active goal stateactive goal statedocs/plans/2026-04-28-slate-v2-root-runtime-selector-guard-hard-cut-plan.md.tmp/slate-v2/packages/slate-react/test/kernel-authority-audit-contract.tsDecision:
root-selector-sources.ts, and the
remaining root runtime orchestration is inventoried before moving policy.Rejected tactics:
EditableTextBlocks to zero Editor.getSnapshot(...) in this
lane. The current read is inside the mounted node selector for child runtime
id resolution, not the root selector path named by the review.Next action:
Actions:
EditableTextBlocks already consumes named root facts clearly.usePlaceholderValue(...) ignores
selection-only commits.Commands:
bun --filter slate-react test:vitest test/provider-hooks-contract.test.tsxbun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsxbun --filter slate-react typecheckEvidence:
slate-react typecheck passed.Changed files:
.tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsxdocs/plans/2026-04-28-slate-v2-root-runtime-selector-guard-hard-cut-plan.mdDecision:
Rejected tactics:
useEditableRootSources(...) yet. The split hooks are already
named, and a facade would be cosmetic unless Phase 2 proves it reduces root
runtime wiring.Next action:
useEditableRootRuntime(...) and move remaining
root runtime orchestration out of EditableDOMRoot.Actions:
.tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts.useEditableRootRuntime(...).useEditableEventRuntime(...).EditableDOMRoot to root facade wiring, event facade wiring, refs,
listeners, props, and rendering.EditableDOMRoot..tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.ts.slate-react/src core-field reference.Commands:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsxbun --filter slate-react typecheckbun --filter slate-react test:vitest test/selection-controller-contract.test.ts test/selection-runtime-contract.test.ts test/editing-kernel-contract.test.ts test/editing-epoch-kernel-contract.test.ts test/target-runtime-contract.test.tsbun lint:fixbun test ./packages/slate/test/escape-hatch-inventory-contract.ts --bail 1Evidence:
slate-react typecheck passed.bun lint:fix passed after removing stale EditableDOMRoot destructures.Changed files:
.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts.tmp/slate-v2/packages/slate-react/test/kernel-authority-audit-contract.ts.tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx.tmp/slate-v2/packages/slate/test/escape-hatch-inventory-contract.tsdocs/plans/2026-04-28-slate-v2-root-runtime-selector-guard-hard-cut-plan.mdDecision:
Rejected tactics:
EditableDOMRoot
for convenience.Next action:
Actions:
bun check:full passed.Commands:
bun --filter slate-react test:vitest test/kernel-authority-audit-contract.test.ts test/surface-contract.test.tsx test/provider-hooks-contract.test.tsxbun --filter slate-react typecheckbun --filter slate-react test:vitest test/selection-controller-contract.test.ts test/selection-runtime-contract.test.ts test/editing-kernel-contract.test.ts test/editing-epoch-kernel-contract.test.ts test/target-runtime-contract.test.tsbunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --forcePLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/hovering-toolbar.test.ts playwright/integration/examples/richtext.test.ts playwright/integration/examples/search-highlighting.test.ts --project=chromium --grep "hovering toolbar|paste|undo|search"PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/mentions.test.ts playwright/integration/examples/tables.test.ts playwright/integration/examples/images.test.ts playwright/integration/examples/large-document-runtime.test.ts --project=chromiumbun check:fullEvidence:
slate-react typecheck passed.slate-browser, slate-dom, and slate-react;
the existing is-hotkey unresolved external warning remained non-fatal.bun check:full passed:
slate-react vitest passed: 18 files, 105 testsslate-browser proof passed: 20 testsChanged files:
active goal statedocs/plans/2026-04-28-slate-v2-root-runtime-selector-guard-hard-cut-plan.mdDecision:
EditableDOMRoot no longer owns root policy bodies, and hot root render
selector ownership is fenced by named source modules plus guards.Rejected tactics:
bun check:full for this lane. The
requested browser regression families are covered by focused rows and the
full integration matrix.Next action: