docs/solutions/developer-experience/2026-04-27-slate-react-runtime-owner-cuts-need-static-inventories-and-browser-proof.md
EditableDOMRoot accumulated hot editing policy while React was supposed to be
projection and wiring. Moving that policy safely required more than extracting
helpers: every bridge had to be assigned to a runtime owner and then proved in
browser rows that exercise real selection, repair, IME, void, table, and
decoration flows.
EditableDOMRoot owned generic root selectors, DOM-selection import,
repair queue construction, event frame creation, kernel trace payloads,
composition state transitions, and Android manager lifecycle.bun check:full release discipline failed until the escape-hatch inventory
reflected the reduced react-runtime:stale count.EditableDOMRoot.bun check:full also passed.Use static inventories as the architecture lock, then use browser proof as the regression lock.
The durable pattern is:
// test/kernel-authority-audit-contract.ts
expectAuthorityInventory(/\bbeginEditableEventFrame\(/g, {
'packages/slate-react/src/editable/runtime-kernel-trace.ts': {
count: 3,
next: 'central-owner',
owner: 'Runtime kernel trace engine',
rationale:
'Non-selectionchange event frames are owned by the runtime kernel trace engine.',
},
})
Then make EditableDOMRoot call runtime methods named by intent:
const {
beginKeyDownEventFrame,
recordKeyDownTrace,
repairDOMInputWithTrace,
} = useRuntimeKernelTraceEngine({
domRepairQueue,
editor,
inputController,
})
Do the same for root render facts:
const {
islandPlan,
mountedTopLevelRanges,
mountedTopLevelRuntimeIds,
topLevelRuntimeIds,
} = useLargeDocumentRootSources({
largeDocumentConfig,
promotedIslandIndex,
})
For root event handlers, keep EditableDOMRoot on a single facade import and
compose event families behind that facade:
const eventRuntime = useEditableEventRuntime({
android,
composition,
repair,
selection,
state,
trace,
})
const {
handleBeforeInput,
handleBlur,
handleCompositionEnd,
handleCopy,
handleCut,
handleDragOver,
handleDrop,
handleFocus,
handleInput,
handleKeyDown,
handleMouseDown,
handlePaste,
handleDOMBeforeInput,
handleDOMInput,
handleReactBeforeInput,
handleReactInput,
} = eventRuntime.handlers
For root orchestration, use the same rule. EditableDOMRoot should call one
root runtime facade instead of assembling selectionchange, Android, repair,
trace, selector wakeups, and global lifecycle listeners in React:
const {
callbackRef,
eventRuntime,
isComposing,
receivedUserInput,
rootRef,
shellBackedSelection,
} = useEditableRootRuntime({
autoFocus,
decorate,
editor,
inputController,
readOnly,
rootPropsRef,
scrollSelectionIntoView,
state,
})
Guard the root boundary explicitly:
expectSourceOwnershipInventory(
editableRootRuntimeFiles,
/useEditableRootRuntime\(/g,
{
'packages/slate-react/src/components/editable.tsx': {
count: 1,
owner: 'Editable root component',
next: 'keep-as-root-facade-call',
rationale:
'EditableDOMRoot may instantiate the root runtime facade, but root policy lives behind that facade.',
},
}
)
The root should not import every event-family worker directly. Event-family modules can be small and policy-specific, but the root boundary should see one event runtime capability and one root runtime capability.
Close with proof at three levels:
bun check:full for release-quality closureStatic inventories make architectural drift visible. If someone adds a direct
useSlateSelector, syncEditorSelectionFromDOM, beginEditableEventFrame,
recordEditableKernelTrace, or forceRender() call in the wrong place, the
package guard fails before browser symptoms become a whack-a-mole queue.
Browser proof catches the other half: timing and DOM authority bugs that static ownership cannot prove. The combination is what keeps a runtime-owner refactor from becoming a cosmetic file split.
bun check:full should fail.editor.selection / editor.children / editor.marks /
editor.operations / editor.apply / editor.onChange match, update the
release inventory only after confirming the remaining match is intentional.runtime-selection-engine, runtime-repair-engine,
runtime-kernel-trace, runtime-composition-engine,
runtime-android-engine, runtime-event-engine,
runtime-root-engine, and root-selector-sources.EditableDOMRoot against direct event-family imports. One facade import
is the intended shape; many worker imports means React is assembling the event
runtime again.EditableDOMRoot against direct root policy calls:
useRuntimeAndroidEngine, selectionchange scheduler/import controller setup,
repair runtime setup, trace runtime setup, root commit wakeups, and global
drag/selection lifecycle attachment.EditableDOMRoot.bun check:full.slate-react typecheck, selection/editing kernel contracts, targeted
slate-browser/slate-dom/slate-react build, focused browser rows for
hovering toolbar, search, mentions, tables, images, and large-document
runtime, then bun check:full with 628 browser tests passed and 4
replay-artifact rows skipped.