docs/plans/2026-05-01-slate-v2-universal-large-document-performance-ralplan.md
Latest review score: 0.88. Status: active execution.
The previous execution slice made shell mode real: explicit mode: 'shell',
mounted corridor semantics, composition guard, shell-backed selection/copy
contracts, dirty commit payloads, and green shell smoke lanes. That is a real
improvement.
It is still not a default-performance victory, but the 5000-block median gate
changed the severity. Shell is now a credible explicit escape hatch.
DOM-present/default now beats legacy chunk-on hard for interactive ready,
steady middle typing, select-all, visible full-document replacement, and
visible fragment insertion. The 5000-block selection/setup and promote medians
are now inside the default gate after reducing DOM-present root groups to 50
blocks. A true browser-native input proof now exists for DOM-present
large-document typing. nativeSurfaceComplete is measured separately and
remains around one second at 5000 blocks. The remaining red owner is the
10000-block stress shape: selection-inclusive and promote lanes still scale too
poorly even though ready and visible full-document operations crush legacy.
The latest beforeinput pass found a benchmark truth bug: the old
*NativeBeforeInputTypeMs rows dispatched @, which the runtime correctly
routes to Slate's model-owned beforeinput path. Those rows were not native
browser insertion proof. They are now renamed to
*SelectThenModelBeforeInputTypeMs.
Keep the Slate v2 rewrite. Rewrite the large-document default strategy.
The v2 core architecture is right: editor.read, editor.update,
editor.subscribe, editor.extend, transaction dirtiness, runtime ids,
sidecar projections, extension state / tx groups, browser proof contracts,
and direct DOM text sync are the correct foundation.
The original shell-island path was useful as an experimental escape hatch but wrong as the default: it crushed startup/full-document operations and lost steady typing badly. The latest shell-explicit implementation is much better and should be kept. The default DOM-present path is still the active red lane.
Accepted decision, updated after GPT Pro follow-up:
// Public default
<Editable />
// Optional policy override
largeDocument?:
| 'auto'
| 'dom-present'
| 'off'
| 'shell'
| {
mode: 'shell'
activeRadius?: number
islandSize?: number
previewChars?: number
threshold?: number
}
Default auto is two-layer. Layer 1 is always the safe DOM-present baseline:
grouping, scoped subscriptions, active corridor, containment,
content-visibility where proven, direct DOM text sync where safe, and
persistent sidecar range indexes. Layer 2 is aggressive shell/occlusion
escalation, and it stays proof-disabled in the default until browser find,
screen reader, native selection, copy/paste, IME, mobile, undo, history, and
collaboration proof all pass. Until then, shell virtualization is explicit
through mode: 'shell'.
New hard line: default auto may use staged DOM-present mounting only if it
tracks interactiveReady and nativeSurfaceComplete separately and never
leaves stale far DOM exposed as current content after full-document changes.
Execution started by ralph on 2026-05-01.
Current pass:
phase-7-default-claim-gatePrinciples:
Top drivers:
contentEditable=false
role="button" shells, not invisible optimizations.Viable options:
| Option | Verdict | Reason |
|---|---|---|
| Explicit opt-in shell mode | reject as default, keep as escape hatch | safe to ship experimentally, but fails "faster by default" and repeats legacy tuning smell |
| Universal shell islands | reject | huge native/a11y risk and current typing lanes are awful |
| Universal DOM-present auto | partial | safest default, but still needs aggressive shell option for huge research lanes |
| Two-tier auto | choose | safe default plus explicit aggressive mode, matches the evidence |
| Drop islands and only fix dirty runtime | reject | attacks typing but throws away the proven startup/full-doc win |
Chosen option: two-tier auto.
Consequences:
interactiveReady can
be compared to interactive startup only; nativeSurfaceComplete must be
reported separately.Follow-ups:
beforeinput lanes into setup-free event-input proof versus mixed
promotion/setup lanes.Live Slate v2:
largeDocument?: LargeDocumentOptions | null on EditableTextBlocksProps:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:264.LargeDocumentOptions is
'auto' | 'dom-present' | 'off' | 'shell' | { mode: 'shell', ... }; shell
knobs no longer leak into non-shell object modes:
.tmp/slate-v2/packages/slate-react/src/large-document/create-island-plan.ts:3.auto:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:60.largeDocumentMode === 'shell';
auto and dom-present do not build a shell island plan:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:621.LargeDocumentIslandShell
only on the shell path; otherwise auto / dom-present use root groups:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:757.createIslandPlan mounts all runtime ids for active corridor islands,
so activeRadius now means actual mounted corridor:
.tmp/slate-v2/packages/slate-react/src/large-document/create-island-plan.ts:51.pending-mount placeholders, selected
pending groups materialize urgently, background mounting fills eight pending
groups per tick, and the benchmark reports interactiveReady separately
from nativeSurfaceComplete:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:55
and .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:491.content-visibility, then render as
contentEditable={false}, role="button", tabIndex={0}:
.tmp/slate-v2/packages/slate-react/src/large-document/island-shell.tsx:31
and .tmp/slate-v2/packages/slate-react/src/large-document/island-shell.tsx:139..tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.ts:24..tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.ts:139.EditorCommit already has classes, dirty region, dirty paths,
node/decoration/selection impact runtime ids, touched runtime ids, tags, and
text/structure flags:
.tmp/slate-v2/packages/slate/src/interfaces/editor.ts:936..tmp/slate-v2/packages/slate/src/core/public-state.ts:517..tmp/slate-v2/packages/slate/src/core/public-state.ts:1837.subscribeSource exists for commit-source-specific listeners:
.tmp/slate-v2/packages/slate/src/core/public-state.ts:2005..tmp/slate-v2/packages/slate-react/src/dom-text-sync.ts:3..tmp/slate-v2/packages/slate-react/src/hooks/use-slate-node-ref.tsx:80..tmp/slate-v2/packages/slate-react/src/projection-store.ts:31..tmp/slate-v2/packages/slate-react/src/annotation-store.ts:13..tmp/slate-v2/packages/slate-react/src/decoration-source.ts:111..tmp/slate-v2/packages/slate-react/src/widget-store.ts:15.methods and public extension
commands, telling authors to add state or tx groups:
.tmp/slate-v2/packages/slate/src/core/editor-extension.ts:60.createEditor exposes read, subscribe, update, and extend
on the base editor:
.tmp/slate-v2/packages/slate/src/create-editor.ts:486..tmp/slate-v2/packages/slate-react/src/components/slate.tsx:100..tmp/slate-v2/packages/slate-react/src/hooks/use-editor-selector.tsx:51..tmp/slate-v2/packages/slate-react/src/hooks/use-node-selector.tsx:41.Benchmark and legacy evidence:
activeRadius, chunkSize, islandSize,
typeOps, explicit v2 model insert typing lanes, and model-owned
beforeinput lanes:
.tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs:16
and .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs:531.v2Off, v2DefaultOmitted,
v2AutoExplicit, v2DomPresent, v2ShellExplicitRadius0, and
v2ShellExplicitRadius1.interactiveReady, nativeSurfaceComplete,
model-only full-document commit lanes, visible full-document lanes,
*ModelBeforeInputTypeMs, and *SelectThenModelBeforeInputTypeMs.
It still does not prove real browser-native DOM insertion in jsdom.ChunkTree:
../slate/packages/slate-react/src/hooks/use-children.tsx:119.modifiedChunks:
../slate/packages/slate-react/src/components/chunk-tree.tsx:66.../slate/packages/slate-react/src/chunking/reconcile-children.ts:21.1000 works well and content-visibility
belongs on lowest chunks:
../slate/docs/walkthroughs/09-performance.md:64.Research and external review:
docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.md.docs/research/sources/editor-architecture/lexical-read-update-extension-runtime.md.docs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.md.docs/research/sources/editor-architecture/tiptap-extension-command-react-dx.md.docs/research/decisions/slate-v2-source-scoped-overlay-invalidation.md.docs/research/decisions/slate-v2-collaborative-annotation-channels.md.| Dimension | Weight | Score | Evidence |
|---|---|---|---|
| React 19.2 runtime performance | 0.20 | 0.93 | current selectors and DOM sync in Slate provider; React 19.2 research; benchmark-driven dirty payload plan |
| Slate-close unopinionated DX | 0.20 | 0.94 | implicit <Editable /> default; optional policy override; no product comment service; extension state / tx retained |
| Plate and slate-yjs migration backbone | 0.15 | 0.90 | external annotation channel decision; state/tx extension groups; anchor/rebase proof required |
| Regression-proof testing strategy | 0.20 | 0.94 | named benchmark matrix, browser proof families, IME/mobile/select/copy/paste gates |
| Research evidence completeness | 0.15 | 0.95 | live Slate v2, legacy Slate, Lexical, ProseMirror, Tiptap, VS Code, React 19.2, GPT Pro review |
| shadcn-style composability and hook/component minimalism | 0.10 | 0.91 | minimal public API; DOM-present behavior hidden behind internals; Plate/product UI stays above core |
Weighted total: 0.93.
Completion gates pass:
0.85;Slate v2 should win by knowing exactly what changed, not by hiding the document as the default.
Target architecture:
Slate data model + operations
-> editor.update transaction
-> rich EditorCommit dirtiness
-> runtime-id / top-level range indexes
-> DOM-present root groups
-> scoped React selectors
-> direct DOM text sync when safe
-> sidecar projection/annotation/widget indexes
-> browser proof as release spine
The shell-island path remains available for aggressive huge-doc scenarios, but it is not the default editor body.
Live current source already cut the misleading enabled shape. Keep it cut.
Default:
<Editable />
The default turns on safe large-document auto behavior above threshold.
Optional override:
type LargeDocumentOptions =
| "auto"
| "dom-present"
| "off"
| "shell"
| {
mode: "shell";
activeRadius?: number;
islandSize?: number;
previewChars?: number;
threshold?: number;
};
Rules:
auto: default, DOM-present first.dom-present: force safe grouped editable DOM.shell: explicit aggressive mode.off: diagnostic escape hatch.enabled boolean in docs;{ mode: 'shell' } over bare 'shell' because the object shape
makes the aggressive choice visible;Before: current EditorCommit has useful dirtiness fields but not enough
top-level/group-specific and DOM-sync-specific proof fields.
After:
type EditorCommit = {
// existing fields kept
dirtyTextRuntimeIds: readonly RuntimeId[] | null;
dirtyElementRuntimeIds: readonly RuntimeId[] | null;
dirtyTopLevelRuntimeIds: readonly RuntimeId[] | null;
dirtyTopLevelRanges: readonly TopLevelRuntimeRange[] | null;
affectedTextRuntimeIds: readonly RuntimeId[] | null;
affectedNodeRuntimeIds: readonly RuntimeId[] | null;
affectedProjectionRuntimeIds: readonly RuntimeId[] | null;
affectedSelectionRuntimeIds: readonly RuntimeId[] | null;
structuralDirtyRuntimeIds: readonly RuntimeId[] | null;
textDirtyRuntimeIds: readonly RuntimeId[] | null;
markDirtyRuntimeIds: readonly RuntimeId[] | null;
rootRuntimeIdsChanged: boolean;
topLevelOrderChanged: boolean;
fullDocumentChanged: boolean;
};
Simple insert_text ideal result:
textDirtyRuntimeIds = [textId];
dirtyElementRuntimeIds = [paragraphId];
dirtyTopLevelRuntimeIds = [paragraphId];
dirtyTopLevelRanges = [[index, index]];
rootRuntimeIdsChanged = false;
topLevelOrderChanged = false;
fullDocumentChanged = false;
Implementation note: core commits stay renderer-agnostic and frozen. DOM text
sync remains a slate-react capability/result, not a field inside the core
EditorCommit. If Phase 2 needs per-runtime DOM-sync telemetry, add it to the
React selector/change context, not to packages/slate.
Before: useEditorSelectorContext broadcast every commit to every selector
listener, and runtime selectors filtered locally with shouldUpdate.
After:
affectedNodeRuntimeIds notify only those runtime ids;affectedNodeRuntimeIds: null still fan out safely;useNodeSelector / useTextSelector;useEditorSelector runtime-id option is only for runtime-owned
selectors and tests.Hard rules:
Before: legacy chunking wins by memoizing chunks; v2 shell islands win startup by not mounting most content, but lose typing.
After:
EditableRoot
SlateRootGroup range=[0, 999]
SlateRootGroup range=[1000, 1999]
SlateRootGroup range=[2000, 2999]
Each group:
content-visibility only after proof;Update test:
shouldGroupUpdate(groupRange, commit) {
return commit.fullDocumentChanged
|| commit.topLevelOrderChanged
|| rangesOverlap(groupRange, commit.dirtyTopLevelRanges)
|| rangesOverlap(groupRange, commit.selectionTopLevelRanges)
|| rangesOverlap(groupRange, commit.projectionTopLevelRanges)
}
Before: current activeRadius is misleading because neighboring islands can be
marked active while only one active island mounts runtime ids.
After:
2 groups before
and after;Before: projection/annotation stores have the right direction, but large-doc proof must prove source-scoped recompute and runtime bucket locality.
After:
type ProjectionRangeIndex = {
byRuntimeId: Map<RuntimeId, ProjectionSlice[]>;
byTopLevelRuntimeId: Map<RuntimeId, RuntimeId[]>;
intervalTreeByTopLevel: Map<RuntimeId, IntervalTree<ProjectionSlice>>;
sourceVersions: Map<SourceId, number>;
mappedThroughVersion: number;
};
Rules:
decorate pass.Add a ProseMirror-like owner:
useSyncExternalStore posture for external subscriptions.renderLeaf, renderSegment, and renderText supported, but
benchmark fallback typing lanes because DOM sync is disabled for them.tx groups.defineEditorExtension({
name: 'comments',
tx: { comments: ... },
capabilities: {
commands: [
{
id: 'comments.add',
title: 'Add comment',
tx: 'comments.add',
shortcut: 'Mod-Alt-M',
},
],
},
})
Plate should migrate to:
state / tx extension namespaces;editor.api / editor.tf compatibility requirement in raw Slate.Proof:
Collaboration needs deterministic substrate, not current adapter support.
Required:
Document sizes:
| Blocks | Purpose |
|---|---|
| 100 | baseline overhead |
| 250 | small real document |
| 500 | medium document |
| 1000 | chunk threshold sanity |
| 2000 | smoke/debug gate |
| 5000 | main release gate |
| 10000 | stress/nightly gate |
| 25000 | shell/viewport research only |
| 50000 | non-default aggressive research only |
Modes:
legacyChunkOfflegacyChunkOnv2Offv2DefaultOmittedv2AutoExplicitv2DomPresentv2ShellExplicitRadius0v2ShellExplicitRadius1v2ShellAutoCandidate only after proofv2CustomLeafv2ProjectionHeavyv2AnnotationsHeavyv2CollabRemoteEditsGroup sizes:
100, 250, 500, 1000, 2000.50, 100, 250, 500, 1000.Corridor sizes:
0+/-1 group+/-2 groups+/-5 groupsviewport + overscanRequired lanes:
1, 10, 100 text ops;beforeinput typing;5000-block gates:
5 ms median;10 ms median;5 ms, or prove synthetic
select/copy/paste parity;10000-block gates:
15 ms median;No performance superiority claim is allowed until 5000-block typing matches or beats legacy chunk-on within the thresholds above.
Required proof families:
Shell mode proof additions:
| Lens | Applicability | Findings | Plan Delta |
|---|---|---|---|
| Vercel React best practices | applied | selectors must subscribe to derived dirtiness, global listeners must stay centralized, transient typing state must stay out of broad render | commit-gated groups, deferred non-editing UI, no broad root wakeups |
| performance-oracle | applied | current shell path optimizes mount but not typing; projection recompute and group invalidation must be O(affected ranges) | dirty commit payload, group range subscriptions, sidecar range index |
| tdd | applied | behavior must be proved through public/editor browser lanes, not implementation assertions | benchmark matrix plus browser proof rows |
| build-web-apps shadcn | skipped | no product UI/editor chrome is designed in this plan | Plate owns comment UI and toolbars |
| react-useeffect | applied | subscriptions and DOM observer work are external-system effects; derived state should stay in selectors/commit data | one selection bridge owner, useSyncExternalStore posture |
Trigger: public API, React runtime subscription strategy, browser behavior, collaboration, sidecar annotations, and release gates all change.
Blast radius:
packages/slatepackages/slate-reactpackages/slate-dompackages/slate-browserdocs/slate-v2Failure scenario 1: DOM-present groups still rerender too broadly, so ready gets worse and typing still loses.
Mitigation: Phase 1 dirty payload gate must pass before DOM-present grouping can graduate; group update counts become benchmark output.
Failure scenario 2: content-visibility preserves DOM but harms browser find,
screen readers, mobile selection handles, or paint in Safari.
Mitigation: containment/content-visibility is a gated layer, not assumed safe; browser find/a11y/mobile rows decide whether it is enabled by default.
Failure scenario 3: sidecar annotations become the new global decorate pass.
Mitigation: sidecar sources must declare dirtiness and runtime scope; metrics must prove recompute and subscriber wakes stay local.
Rollback / hard-cut answer:
Hard cuts:
largeDocument.enabled as the documented default API.decorate as the primary comments/decorations path.commands mutation maps; keep state / tx and
optional capability metadata.Rejected alternatives:
| Change | Who Feels Pain | Strong Objection | Steelman Antithesis | Tradeoff | Why Keep | Migration / Docs / Proof | Verdict |
|---|---|---|---|---|---|---|---|
largeDocument implicit auto default | app authors | "I did not ask for virtualization." | Keep everything opt-in until proven. | more hidden internals | default is DOM-present, not shell; no user tuning | docs explain auto/off/shell; browser gates | keep |
| Shell islands explicit aggressive mode | large-doc users | "You took away the 40 ms ready win." | Startup is the visible win. | huge docs may need opt-in | typing and native semantics matter more | shell docs and benchmark mode | keep |
| Internal root grouping | Slate maintainers | "This is chunking again." | Legacy chunking was the thing to escape. | adds internal wrapper layer | escape the API, steal the mechanism | no public getChunkSize; group tests | keep |
| Rich commit dirtiness | core maintainers | "Commit gets too big." | Current fields are already enough. | more metadata to maintain | benchmarks show current path wakes too much | unit tests per op class | keep |
| Sidecar range index | plugin authors | "This is more API than decorate." | Keep one callback. | more store concepts | comments/read-only collaboration require sidecar ownership | two-lane comments example | keep |
| Selection bridge owner | browser runtime maintainers | "This is ProseMirror complexity." | Native contenteditable can handle it. | centralized complexity | shell/group/native parity needs one owner | DOM selection/import/export proof | keep |
| Capability metadata for commands | extension authors | "Tiptap commands are familiar." | Expose commands directly. | extra indirection | write lifecycle stays tx; discoverability preserved | command palette example | keep |
| Shell proof gate | product owners | "This slows shipping." | Ship and fix bugs later. | more release work | shell changes browser semantics | find/a11y/IME/mobile gates | keep |
Added:
Strengthened:
activeRadius bug: active means mounted or the name changes.Dropped:
No-change decisions:
editor.read / editor.update;state / tx hard cut;Added:
interactiveReady and
nativeSurfaceComplete;type RootGroupState =
| "fresh-mounted"
| "pending-mount"
| "pending-reconcile"
| "reconciling"
| "detached";
type RootGroupRecord = {
groupId: string;
startIndex: number;
endIndex: number;
runtimeIds: RuntimeId[];
version: number;
state: RootGroupState;
priority: "selection" | "viewport" | "corridor" | "dirty" | "background";
};
type RootGroupRuntime = {
getPlan(): RootGroupPlan;
getState(groupId: string): RootGroupState;
markDirty(groupIds: string[], reason: string): void;
mountUrgent(groupIds: string[]): void;
scheduleBackground(groupIds: string[]): void;
subscribeGroup(groupId: string, listener: () => void): () => void;
};
v2Offv2DefaultOmittedv2AutoExplicitv2DomPresentv2ShellExplicitRadius0v2ShellExplicitRadius1largeDocumentModegroupingEnabledshellEnabledgroupSizecorridorstagedMountingEnabledinteractiveReadyAtnativeSurfaceCompleteAtmountedGroupCountAtReadypendingGroupCountAtReadystaleGroupCountbackgroundMountChunksmaxBackgroundChunkMsStrengthened:
Dropped:
interactiveReady against legacy full readiness without
separately reporting nativeSurfaceComplete;v2NoIsland as a benchmark row name.No-change decisions:
auto DOM-present first;off as a real no-grouping control row;| Dimension | Score | Reason |
|---|---|---|
| React 19.2 runtime performance | 0.86 | Root groups and selector cuts exist, but no group lifecycle or readiness split exists yet. |
| Slate-close unopinionated DX | 0.91 | Policy shape is now good: omitted/auto, dom-present, off, explicit shell. |
| Plate and slate-yjs migration backbone | 0.90 | Sidecar comments and runtime buckets are still the right backbone; no new gap from this review. |
| Regression-proof testing strategy | 0.86 | Strong shell proof exists, but default DOM-present lacks stale-DOM, readiness, and full-doc lifecycle contracts. |
| Research evidence completeness | 0.92 | GPT Pro review, local source, legacy chunking, and compiled editor research agree on the next owner. |
| shadcn-style composability and hook/component minimalism | 0.90 | No product API leak; internals carry the complexity. |
Weighted total: 0.88.
Completion status: pending. The plan has a better next direction, but it is
not execution-ready until the DOM-present lifecycle phase, benchmark rename,
and proof gates are written as the next runnable slice.
Owner: docs/benchmarks.
Gate:
Owner: packages/slate.
Gate:
Owner: packages/slate-react.
SlateRootGroup or equivalent root group layer.renderChunk or getChunkSize.Checkpoint after root-group slice:
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx
for the non-shell DOM-present path.group events prove root groups do not rerender after a local text
edit or parent rerender in the 1001-block focused test.bun check is green in .tmp/slate-v2.33.07 ms78.23 ms96.31 ms33.92 ms83.26 ms131.84 msCheckpoint after mounted-subtree instrumentation:
REACT_HUGE_COMPARE_PROFILE=1 mode. Normal benchmark timing stays
unprofiled.bun --filter slate-react typecheck,
bun lint:fix, and full bun check are green in .tmp/slate-v2.100 -> 70; root runtime, placeholder,
editable-root notifications 10 -> 0; editable root renders 10 -> 0;
DOM sync still skips 10 times because the target is not mounted.87; one root plan/render happens after selection activation.90 -> 80; DOM sync succeeds; no
root plan/render.33.87 ms64.32 ms78.02 ms33.23 ms81.14 ms129.69 msbeforeinput lane before any 5000-block claim.Checkpoint after selection/global wakeup cut:
subscribeSelectionOnlyDOMExport is now profiled as
selection-dom-export and no longer wakes for plain text-only commits.bun --filter slate-react typecheck, bun lint:fix, profiled 2000-block smoke, clean
2000-block smoke, and full bun check are green.70 -> 60;80 -> 70;0 for plain type lanes;32.00 ms62.91 ms71.22 ms32.95 ms82.38 ms136.75 msbeforeinput lane before making the next
architectural call.Gate:
5 ms.10 ms.Owner: packages/slate-react sidecar stores.
Status: complete.
Current source read:
.tmp/slate-v2/packages/slate-react/src/projection-store.ts
owns source dirtiness, runtime scope, source/runtime subscriptions, and
projection metrics..tmp/slate-v2/packages/slate-react/src/annotation-store.ts
owns bookmark-backed annotation rebase, targeted refresh, runtime subscriber
wakeups, and annotation metrics..tmp/slate-v2/packages/slate-react/test/annotation-store-contract.tsx:
projection changes wake only the affected runtime bucket, and document edits
rebase only annotations in impacted runtime buckets across a 40-annotation
set..tmp/slate-v2/playwright/integration/examples/collaborative-comments.test.ts
against .tmp/slate-v2/site/examples/ts/collaborative-comments.tsx: the
reviewer is read-only for the document value, can add/update comments through
the sidecar channel, and writer edits rebase the comment range.Gate:
Owner: packages/slate-dom / packages/slate-react.
Status: complete for local browser proof. Raw Android/iOS device proof remains a release/device gate, not a claim from this pass.
Current source read:
.tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts
already carries both explicit shell selection state and an
isShellBackedSelection classifier.data-slate-large-document-selection="shell-backed"
classification as explicit Ctrl+A/browser-handle paths..tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx now proves a
programmatic full-document model selection across shelled ranges is classified
as shell-backed without expanding shells..tmp/slate-v2/packages/slate-dom/src/plugin/dom-clipboard-runtime.ts now has
a model-backed clipboard fallback when a selection cannot be cloned from the
DOM because some selected content is not mounted..tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts
proves full and partial shell-backed selections write normal text/html Slate
fragment clipboard payloads from a copy event..tmp/slate-v2/packages/slate-browser/src/playwright/index.ts now keeps the
keyboard/OS clipboard helper honest: clipboard.copyPayload() uses the real
shortcut and real clipboard reads only, while clipboard.copyEventPayload()
names the synthetic copy-event payload contract used for shell-backed
selections with unmounted content..tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts
now proves a real mouse drag inside mounted large-document content remains
native/DOM-owned, imports an expanded model selection, and does not get
mislabeled as shell-backed..tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts
freshly pass on Chromium native composition and the Playwright mobile project
synthetic composition lane..tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts now
exposes resolveRangeRef so browser stale-target proofs can observe a live
range ref before unref..tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts now
owns browser-handle range-ref cleanup at root unmount. Reattaching the
browser handle during a render no longer unrefs bookmarks needed by remote
operation rebase proof..tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts
now proves a shell-backed selection bookmark captured in an unmounted island
rebases through a remote text operation, restores into mounted content, and
copies the original selected text through the real keyboard/OS clipboard path.Gate:
Owner: large-document runtime.
mode: 'shell'.Gate:
Owner: docs/examples.
auto, off, dom-present, shell.Gate:
Owner: benchmark runner.
v2LargeDocument -> v2ShellExplicitRadius0 or
v2ShellExplicitRadiusN;v2NoIsland -> one of v2DefaultOmitted, v2AutoExplicit, or
v2DomPresent depending on the actual input.v2Off;v2DefaultOmitted;v2AutoExplicit;v2DomPresent;v2ShellExplicitRadius0;v2ShellExplicitRadius1.Gate:
v2NoIsland;Owner: EditableTextBlocks / root group runtime.
Implement group lifecycle before optimizing startup.
Target full-document replace flow:
nativeSurfaceComplete = false.nativeSurfaceComplete = true only after all groups are fresh.Hard rule:
Gate:
nativeSurfaceComplete;Owner: root group runtime.
Target startup flow:
pending-mount.interactiveReady.nativeSurfaceComplete.Gate:
interactiveReady is at or below legacy chunk-on ready at 5000 blocks;nativeSurfaceComplete is measured separately;nativeSurfaceComplete;Owner: benchmark runner + runtime policy.
Sweep:
250, 500, 1000;4 ms, 8 ms, 16 ms.Initial suspicion:
groupSize=500 | 1000, corridor=viewport+/-1 | +/-2,
backgroundBudget=8 ms;100 unless the sweep proves it.Gate:
Owner: browser input runtime.
Profile after lifecycle is sane:
beforeinput received;Gate:
directModelType + small constant;Default large-document performance can be claimed only when 5000-block, 5-iteration medians pass:
| Area | Required |
|---|---|
| default interactive ready | beats legacy chunk-on |
| default direct/model typing | matches or beats legacy chunk-on |
| default beforeinput typing | <= legacy chunk-on + 10 ms |
| default select+type | matches or beats legacy chunk-on |
| default full replace visible commit | matches or beats legacy chunk-on |
| stale DOM | proven absent |
| nativeSurfaceComplete | measured and bounded |
| sidecar updates | runtime-bucket scoped |
| shell | still explicit |
Use these during implementation:
typeOps=10 and typeOps=100.beforeinput lane.250, 500, 1000, 2000; initial
likely winner is 500 or 1000.+/-1, +/-2, +/-5; initial likely
winner is +/-2.content-visibility default: enable only after browser find/a11y/mobile proof
passes by browser family.dom-present as public mode or only debug mode: decide after
docs/API ergonomics pass.What would change the decision:
| Pass | Status | Evidence Added | Plan Delta | Open Issues | Next Owner |
|---|---|---|---|---|---|
| external-context-prompt | complete | fresh 5000/10000/N-sweep metrics; live v2 source; legacy Slate chunking; React 19.2; comments/decorations pressure | drafted external prompt | none | complete |
| live-slate-api-skeleton | complete | package exports, read/update/subscribe/extend, commit dirtiness, extension registry, React selectors, sidecar stores, browser proof | prompt and plan cite live current owners | none | complete |
| external-scalability-skeleton | complete | Lexical, ProseMirror, Tiptap, VS Code skeletons | external prompt forced comparative answer | none | complete |
| gpt-answer-ralplan | complete | GPT Pro answer chose two-tier DOM-present default and explicit shell mode | full plan rewritten around that verdict | none | complete |
| closure-score | complete | scorecard, proof matrix, objection ledger, phases, gates | score 0.93, ready for user review | none | user review |
| phase-0-stale-claim-cut | complete | docs/slate-v2/slate-react-perf-loop-context.md, true-slate-rc-proof-ledger.md, absolute-architecture-release-claim.md, replacement-gates-scoreboard.md, master-roadmap.md, and affected research decisions now say current shell islands are red for typing/select lanes | stale "v2 shell beats legacy chunk-on typing" claims cut; DOM-present auto and stronger commit dirtiness named as next owner | archived old plans still contain historical claims and are not current truth | phase-1-commit-dirtiness-for-typing |
| phase-1-commit-dirtiness-for-typing | complete | .tmp/slate-v2/packages/slate/src/interfaces/editor.ts, .tmp/slate-v2/packages/slate/src/core/public-state.ts, .tmp/slate-v2/packages/slate/test/commit-metadata-contract.ts, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx; bun check green in .tmp/slate-v2 | core EditorCommit now exposes frozen array/null dirty text, element, top-level, affected, structural/text/mark, root-order, top-level-order, and full-doc facts; React selector contracts prove those facts reach hooks | no benchmark win claimed; DOM text sync stays React-local for now | phase-2-internal-dom-present-grouping |
| phase-2-runtime-selector-fanout-cut | complete | .tmp/slate-v2/packages/slate-react/src/hooks/use-editor-selector.tsx, .tmp/slate-v2/packages/slate-react/src/hooks/use-node-selector.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx; red/green test proves an unrelated runtime-id commit does not invoke shouldUpdate; bun check green in .tmp/slate-v2; 2000-block one-iteration compare still red for typing | Phase 2 starts by removing selector-broadcast fanout before adding DOM/group wrappers | no 5000-block claim; 2000-block smoke still has v2 shell/no-island typing behind legacy chunk-on | phase-2-dom-present-root-groups |
| phase-2-dom-present-root-groups | complete, benchmark-red | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/src/render-profiler.ts, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx; focused tests prove stable root-group render counts; bun --filter slate-react typecheck, focused slate-react tests, bun lint:fix, and bun check green; 2000-block smoke still loses typing | root groups are useful structure, but no performance claim; the next pass must instrument and cut mounted-subtree wakeup breadth | no 5000-block claim; root groups alone did not move direct compare enough | phase-2-mounted-subtree-fanout-instrumentation |
| phase-2-mounted-subtree-fanout-instrumentation | complete, benchmark-red | .tmp/slate-v2/packages/slate-react/src/render-profiler.ts, src/hooks/use-editor-selector.tsx, src/hooks/use-node-selector.tsx, src/hooks/use-editor-selection.tsx, src/hooks/use-element-selected.ts, src/hooks/use-slate-node-ref.tsx, src/editable/root-selector-sources.ts, scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs, test/provider-hooks-contract.tsx; focused tests, bun --filter slate-react typecheck, bun lint:fix, profiled 2000-block smoke, clean 2000-block smoke, and bun check green | instrumentation found root selector wakeups and DOM-sync shell misses; commit metadata now suppresses root runtime/placeholder/editable-root notifications for text-only commits | still red versus legacy chunk-on; shell unmounted middle type is probably a model/remote lane, not native typing; remaining global/selection notify needs a named owner | phase-2-selection-global-wakeup-cut |
| phase-2-selection-global-wakeup-cut | complete, benchmark-red | .tmp/slate-v2/packages/slate-react/src/editable/selection-runtime.ts, .tmp/slate-v2/packages/slate-react/test/selection-runtime-contract.test.ts; profiled smoke shows selection DOM export notify is gone for plain type; focused Vitest, focused slate-react tests, bun --filter slate-react typecheck, bun lint:fix, clean smoke, and bun check green | selection DOM export no longer wakes on plain text-only commits; remaining red lanes need native input proof and shell/model lane classification | select-then-type still wakes selection DOM export because selection moves; 2000-block typing remains red against legacy chunk-on | phase-2-native-beforeinput-typing-lane |
| phase-2-native-beforeinput-typing-lane | complete, benchmark-red | .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; added v2-only native beforeinput event lanes, benchmark-local JSDOM isContentEditable shim, cached text-event target lookup, and delayed dispose for repair timers; 200-block smoke green; profiled 2000-block smoke green; clean 2000-block smoke green; bun lint:fix and bun check green | event-path typing is now measured separately from explicit model insert into unmounted shell ranges; legacy JSDOM has no honest comparable native beforeinput lane, so these lanes are reported only on v2 surfaces and excluded from legacy deltas | 2000-block clean: v2 shell middle beforeinput 119.78 ms, no-island 214.48 ms; event path is slower than direct inserts and wakes selection DOM export every op | phase-2-event-input-selection-export-cut |
| phase-2-event-input-selection-export-cut | complete, benchmark-red | .tmp/slate-v2/packages/slate-react/src/editable/selection-runtime.ts, .tmp/slate-v2/packages/slate-react/test/selection-runtime-contract.test.ts; focused selection contract, bun --filter slate-react typecheck, bun lint:fix, profiled 2000-block smoke, clean 2000-block smoke, and bun check green | text-input commits now skip the selection DOM export listener when caret repair already owns the collapsed selection; native beforeinput selection-dom-export-notify drops from 11 to 1 in the profiled shell lane | clean 2000-block smoke still loses typing: legacy chunk-on middle 31.91 ms, v2 shell middle 58.72 ms, v2 no-island middle 73.84 ms, v2 shell beforeinput 105.09 ms, v2 no-island beforeinput 212.79 ms | phase-2-model-typing-hotpath-instrumentation |
| phase-2-model-typing-hotpath-instrumentation | complete, benchmark-red | .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs, .tmp/slate-v2/packages/slate-react/src/render-profiler.ts, .tmp/slate-v2/packages/slate-react/src/components/slate.tsx; focused provider/selection contracts, bun --filter slate-react typecheck, profiled 200/2000-block smokes, clean 2000-block smoke, and bun check green | profiled benchmark now reports durationByKey for benchmark act, editor.update, provider DOM sync, selector dispatch, callbacks, and focus state; dispose waits 250 ms so queued caret-repair retries finish before globals restore | 2000-block profile says direct typing cost lives almost entirely inside editor.update(tx.text.insert): shell middle type 60.18 ms, text insert update 59.94 ms, provider DOM sync 0.03 ms, selector dispatch 0.12 ms; no-island middle type 66.79 ms, text insert update 66.06 ms, DOM sync 0.85 ms, selector dispatch 0.09 ms | phase-2-core-text-insert-cost-cut |
| phase-2-core-text-insert-cost-cut | complete, selectall/native-red | .tmp/slate-v2/packages/slate/src/core/public-state.ts, .tmp/slate-v2/packages/slate/test/snapshot-contract.ts, .tmp/slate-v2/packages/slate-react/src/render-profiler.ts, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; snapshot/transaction contracts, bun --filter slate typecheck, bun --filter slate-react typecheck, bun lint:fix, profiled 2000-block smoke, clean 2000/5000-block compares, and bun check green | core profiling found direct typing was dominated by full snapshot publication and rollback cloning; path-stable text snapshots reuse the previous index and clone only the edited branch; rollback reuses previous snapshot children until failure; collapsed selection impact no longer scans every indexed path | 5000-block clean shell now beats legacy chunk-on for ready 19.80 vs 301.66 ms, middle type 23.72 vs 34.01 ms, middle select/type 20.44 vs 34.38 ms, promote/type 27.96 vs 37.45 ms, replace 17.10 vs 127.76 ms, fragment 31.52 vs 114.60 ms; still red: selectAll 17.65 vs 1.01 ms, native beforeinput shell 82.97 ms v2-only | phase-2-native-input-and-selectall-cost-cut |
| phase-2-native-input-and-selectall-cost-cut | complete | .tmp/slate-v2/packages/slate/src/core/public-state.ts, .tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts, .tmp/slate-v2/packages/slate-react/src/editable/root-selector-sources.ts, .tmp/slate-v2/packages/slate-react/src/editable/selection-runtime.ts, .tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts, .tmp/slate-v2/packages/slate/test/snapshot-contract.ts, .tmp/slate-v2/packages/slate-react/test/selection-reconciler-contract.ts, .tmp/slate-v2/packages/slate-react/test/selection-runtime-contract.test.ts, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx; focused tests, bun --filter slate-react typecheck, bun lint:fix, profiled 2000/5000-block compares, clean 2000/5000-block compares, and bun check green | broad full-document selection impact no longer materializes every selected runtime id; valid model-owned beforeinput no longer scans the root with Editor.string(editor, []); broad shell-backed selectAll no longer replans the active island or exports an impossible DOM selection; 5000-block shell explicit now passes ready/selectAll/type/select-type/promote/full-doc gates against legacy chunk-on | v2 no-island remains red: 5000-block clean no-island ready 483.13 ms, selectAll 22.03 ms, select/type 107.34 ms, native beforeinput 332.93 ms, replace 155.31 ms, fragment 179.21 ms; shell remains explicit, not default | phase-2-dom-present-render-subtree-cut |
| phase-2-dom-present-render-subtree-cut | partial, native/full-doc-red | .tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts, src/editable/selection-runtime.ts, src/hooks/use-editor-selector.tsx, src/hooks/use-element-selected.ts, test/react-editor-contract.tsx, test/selection-runtime-contract.test.ts; focused contracts, bun --filter slate-react typecheck, bun lint:fix, bun check, profiled 5000-block compares, and clean 5000-block compare /tmp/slate-v2-clean-5000-dom-present-final-this-slice.out | DOM-present selection export now uses direct text/root endpoints, keeps large full-document selections model-backed, skips model-to-DOM selection export for synced text-only commits, and narrows root-order fanout without breaking projection refresh or path-shifting useElementSelected; clean 5000 no-island now passes selectAll 2.04 / 2.13 ms, middle type 17.68 / 13.84 ms, and middle select/type 28.32 / 25.21 ms against legacy chunk-on 1.33 / 1.21, 39.41 / 38.91, and 51.50 / 40.40 | remaining no-shell red: ready 481.23 / 477.40 ms, promote/type 68.16 / 67.26 ms, native beforeinput 359.92 / 354.78 ms, replace full doc 589.01 / 593.40 ms, insert fragment 548.98 / 560.25 ms; shell explicit remains green but not default | phase-2-dom-present-native-and-full-doc-owner |
| phase-2-dom-present-native-and-full-doc-owner | complete, policy-red | .tmp/slate-v2/packages/slate/src/core/public-state.ts, .tmp/slate-v2/packages/slate/test/commit-metadata-contract.ts, .tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts, src/editable/mutation-controller.ts, src/editable/model-input-strategy.ts, test/model-input-strategy-contract.ts, test/provider-hooks-contract.tsx, test/react-editor-contract.tsx, scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; focused node and Vitest contracts green; bun --filter slate typecheck, bun --filter slate-react typecheck, bun lint:fix, and bun check green; profile /tmp/slate-v2-profile-5000-dom-present-safe-full-doc-target-cut.out; clean compare /tmp/slate-v2-clean-5000-dom-present-safe-target-cut.out | Full-document commits now publish next live runtime ids instead of null, cutting no-shell 5000 -> 1 replacement runtime-node checks/notifies from 10k/10k to 2/2 without breaking projection refresh; browser handle exposes getElementByPath so the beforeinput benchmark uses Slate's path map instead of root CSS scans; model-owned insertText can use the synced collapsed selection as explicit target when unmarked | Clean 5000 no-island still fails ready 473.22 / 465.10 ms, promote/type 88.47 / 88.80 ms, native-beforeinput 483.63 / 488.35 ms, replace 579.85 / 596.03 ms, fragment 748.41 / 781.11 ms; profile says full-doc selector fanout is no longer the bottleneck, so the remaining full-doc cost is mounted DOM/React act flush; native-beforeinput lane is still model-owned event-path plus setup, not a proven native browser insertion lane | phase-2-beforeinput-lane-split |
| phase-2-beforeinput-lane-split | complete, policy-red | .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; added setup-free startBlockModelBeforeInputTypeMs and middleBlockModelBeforeInputTypeMs; kept the older mixed *NativeBeforeInputTypeMs lanes for continuity; benchmark cleanup drains delayed caret repair for 500 ms outside measured samples; profiled 200-block benchmark green; profiled 5000-block benchmark green; clean 5000-block benchmark green; bun lint:fix and bun check green | The event-path evidence is now honest: shell model-beforeinput is setup-free, while the older native lane remains promotion/setup-inclusive and not a true browser-native insertion proof. Clean 5000: shell model-beforeinput 35.17 / 33.87 ms, shell mixed native 55.36 / 48.02 ms; no-island model-beforeinput 321.83 / 277.62 ms, no-island mixed native 543.64 / 473.69 ms | No-shell direct typing/select/type are green, but no-shell event-input, promote, ready, and full-document lanes are red. Shell is the fast layer, but still cannot become universal default without browser find, screen reader, native selection, IME/mobile, clipboard, and collaboration proof | phase-2-dom-present-default-policy-replan |
| phase-2-dom-present-default-policy-replan | complete | docs/plans/2026-05-01-slate-v2-universal-large-document-performance-ralplan.md, docs/slate-v2/absolute-architecture-release-claim.md, docs/slate-v2/master-roadmap.md, docs/slate-v2/references/chunking-review.md; plan and docs now reflect the post-split benchmark evidence | Default auto is locked as two-layer policy: DOM-present is the safe baseline; shell/occlusion escalation stays explicit or proof-disabled until browser/native/a11y/collab gates pass. DOM-present no-shell is not allowed to claim startup/full-doc/event-input performance victory | Public largeDocument.enabled still exists in live source as the benchmark/internal shell shape; Phase 3 now owns sidecar range indexing before public API cleanup | phase-3-source-scoped-sidecar-range-index |
| phase-3-source-scoped-sidecar-range-index | complete | .tmp/slate-v2/packages/slate-react/src/projection-store.ts, .tmp/slate-v2/packages/slate-react/src/annotation-store.ts, .tmp/slate-v2/packages/slate-react/test/annotation-store-contract.tsx, .tmp/slate-v2/site/examples/ts/collaborative-comments.tsx, .tmp/slate-v2/playwright/integration/examples/collaborative-comments.test.ts; bun --filter slate-react test:vitest -- annotation-store-contract.test.tsx green with 9 tests; focused collaborative-comments Chromium proof green; bun lint:fix green; bun check green | Live sidecar stores already had the right owner shape, so this pass hardened proof instead of rewriting stores: projection/annotation changes wake affected runtime buckets only, document text edits rebase only impacted annotations across a larger set, and a read-only reviewer can comment through the sidecar channel while the writer owns document writes | No public annotation product API is claimed; stale-target remote collaboration and DOM/native selection behavior move to Phase 4 | phase-4-selection-bridge-and-dom-observer-discipline |
| phase-4-shell-backed-selection-derivation | complete | .tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx; bun test ./packages/slate-react/test/large-doc-and-scroll.tsx green with 17 tests; bun --filter slate-react typecheck green; bun lint:fix green; bun check green | Shell-backed root state now derives from the live model selection, not only the explicit Ctrl+A/browser-handle flag. Programmatic broad selections across shelled ranges are classified as shell-backed without expanding shells | Phase 4 still needs native selection drag, partial/full copy, paste-over-range, IME/mobile, and stale-target/rebase proof | phase-4-selection-copy-paste-and-native-proof |
| phase-4-shell-backed-copy-payload | complete | .tmp/slate-v2/packages/slate-dom/src/plugin/dom-clipboard-runtime.ts, .tmp/slate-v2/packages/slate-browser/src/playwright/index.ts, .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts; focused shell-backed copy Chromium proof green with 2 tests; bun test ./packages/slate-react/test/large-doc-and-scroll.tsx green with 17 tests; bun --filter slate-dom typecheck green; bun --filter slate-react typecheck green; bun --filter slate-browser typecheck green; bun lint:fix green; bun check green | Clipboard write now falls back to a model-backed Slate fragment payload when DOM range cloning fails because selected shell content is unmounted. Full and partial shell-backed copy events now produce text/html Slate fragment data | Real OS clipboard keyboard copy with no native DOM range was still a separate native-browser proof question until the next contract split row | phase-4-clipboard-contract-split |
| phase-4-clipboard-contract-split | complete | .tmp/slate-v2/packages/slate-browser/src/playwright/index.ts, .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts; first rerun failed because the site imported stale built slate-browser/playwright output; targeted package build bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force green; focused shell-backed copy Chromium proof green with 2 tests; existing highlighted-text real shortcut copy proof green with 1 test; bun --filter slate-browser typecheck green; bun lint:fix green; bun check green | The browser proof API now splits the contracts instead of hiding failure: clipboard.copyPayload() stays real keyboard/OS clipboard only, while clipboard.copyEventPayload() is the explicit synthetic copy-event payload helper for shell-backed selections where selected content is not fully mounted in the DOM | Shell-backed rows do not claim browser OS clipboard parity without a native DOM range; Phase 4 still needs native drag selection, IME/mobile, and stale-target/rebase proof | phase-4-native-selection-and-composition-proof |
| phase-4-native-drag-selection-proof | complete | .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts; focused native mouse drag Chromium proof green; bun lint:fix green; first bun check rerun raced formatter output and failed on one formatting diff, rerun after lint:fix green | A real mouse drag inside mounted large-document content produces non-empty DOM selected text, imports an expanded Slate model selection in the mounted block, keeps the root out of shell-backed mode, and has no illegal kernel transitions | This is native proof for mounted content, not a claim that dragging across unmounted shell ranges is native; Phase 4 still needs IME/mobile and stale-target/rebase proof | phase-4-composition-and-stale-target-proof |
| phase-4-composition-proof-refresh | complete | existing .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts rows; first mobile/chromium parallel attempt failed because two next build processes contended for the same build lock; reran sequentially; focused Chromium composition proof green with 2 tests; focused Playwright mobile composition proof green with 2 tests | Large-document mounted-content composition already had the right owner in live source: direct IME composition commits through the browser editing path, and the generated composition gauntlet records no illegal kernel transitions. Chromium is native composition; the Playwright mobile row is explicitly synthetic composition | This does not claim raw Android/iOS device proof. Phase 4 still needs stale-target/rebase proof | phase-4-stale-target-rebase-proof |
| phase-4-stale-target-rebase-proof | complete | .tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts, .tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts, .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts; first browser row exposed missing resolveRangeRef; second exposed range refs being unrefed on browser-handle reattach; focused stale-target Chromium proof green; bun --filter slate-react typecheck green; targeted bunx turbo build --filter=./packages/slate-react --force green; bun test ./packages/slate-react/test/large-doc-and-scroll.tsx green with 17 tests; bun lint:fix green; bun check green | Browser-handle range refs now survive render-time handle reattachment and are cleaned up by root unmount. A shell-backed bookmark captured in an unmounted island rebases through remote text operation replay, restores into mounted content, and copies the original selected text through the real clipboard path | Raw Android/iOS device proof is still not claimed; Phase 4 local browser owner is complete | phase-5-explicit-shell-mode |
| phase-5-active-corridor-mounting | complete | .tmp/slate-v2/packages/slate-react/src/large-document/create-island-plan.ts, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx; focused large-doc package test green with 18 tests; bun --filter slate-react typecheck green; targeted bunx turbo build --filter=./packages/slate-react --force green with the known is-hotkey external warning; focused Chromium shell activation/copy/rebase browser rows green with 4 tests; 2000-block one-iteration legacy compare green for shell explicit; 5000-block one-iteration legacy compare green/parity for shell explicit; bun lint:fix green; bun check green | activeRadius now means an actual mounted corridor: every active island mounts its full runtime id slice, and inactive islands shell only far ranges. Package proof covers radius 0 mounting the full active island, radius 1 mounting the neighboring corridor island, and focus-only shell interaction staying non-promoting | Shell mode is still explicit/aggressive, not the default. 5000-block one-iteration result has shell explicit at ready 34.96 ms, selectAll 0.36 ms, middle type 10.03 ms, middle select/type 32.37 ms versus legacy chunk-on 345.32 ms, 1.03 ms, 31.00 ms, 31.56 ms; no-island remains policy-red for ready/full-doc/event-input | phase-5-large-document-policy-api |
| phase-5-large-document-policy-api | complete | .tmp/slate-v2/packages/slate-react/src/large-document/create-island-plan.ts, .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/src/large-document/island-shell.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/site/examples/ts/large-document-runtime.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs, .tmp/slate-v2/scripts/benchmarks/browser/react/active-typing-breakdown.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-overlays.tsx, .tmp/slate-v2/docs/walkthroughs/09-performance.md, .tmp/slate-v2/docs/libraries/slate-react/editable.md; focused large-doc package test green with 19 tests; provider-hooks contract green with 14 tests; bun --filter slate-react typecheck green; targeted bunx turbo build --filter=./packages/slate-react --force green with the known is-hotkey external warning; focused Chromium shell activation/copy/rebase/composition rows green with 6 tests; 2000-block one-iteration legacy compare works through mode: 'shell'; bun lint:fix green; bun check green | Public large-document policy no longer uses misleading enabled: true. Default and largeDocument="auto" keep safe DOM-present grouping, largeDocument="off" disables automatic root grouping, and shell options require mode: 'shell'. Shell promotion also fails closed while composing | Shell mode still cannot become auto/default without browser find, screen-reader traversal, native selection across shell boundaries, raw mobile/device proof, undo/history soak, and persistent caret soak. Docs are updated for the live API, but Phase 6 still owns final docs/examples closure and stale-claim sweep | phase-6-release-docs-and-examples |
| phase-6-release-docs-and-examples | complete | .tmp/slate-v2/docs/libraries/slate-react/editable.md, .tmp/slate-v2/docs/walkthroughs/09-performance.md, .tmp/slate-v2/site/examples/ts/large-document-runtime.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx; stale sweep for enabled, old island headings, and unsupported performance-victory wording found no live large-document docs/examples drift after cleanup; provider-hooks contract green with 15 tests; large-doc package contract green with 19 tests; bun --filter slate-react typecheck green; bun lint:fix green; bun check green | Docs now describe current-state policy: omitted/auto uses safe DOM-present grouping, dom-present locks that safe mode, off disables automatic root grouping, and { mode: 'shell' } opts into aggressive shell mounting. No doc claims shell is the universal default or that unproven browser/a11y gates passed | Browser find, screen-reader traversal, cross-shell native selection, raw mobile device proof, undo/history soak, and persistent caret soak remain explicit future gates before shell can ever become automatic/default | complete |
| gpt-pro-followup-dom-present-lifecycle-replan | complete | GPT Pro follow-up answer; live source re-read for LargeDocumentOptions, root grouping, shell corridor, docs, tests, and benchmark runner; memory-backed warning that shell editing is safe only with semantic DOM | Reopened the plan at score 0.88; shell remains explicit and credible, but default DOM-present is not a performance victory. Added readiness split, root group lifecycle, stale-DOM rule, benchmark row rename, and Phase 7 lifecycle plan | Needs execution: benchmark rename, readiness traces, full-doc stale-DOM proof, staged mount lifecycle, group/corridor/budget sweep, beforeinput after lifecycle | phase-7-benchmark-surface-rename |
| phase-7-benchmark-surface-rename | complete | .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; node --check green; stale v2LargeDocument / v2NoIsland grep clean in the runner; REACT_HUGE_COMPARE_ITERATIONS=1 REACT_HUGE_COMPARE_BLOCKS=2000 REACT_HUGE_COMPARE_TYPE_OPS=10 REACT_HUGE_COMPARE_ACTIVE_RADIUS=0 REACT_HUGE_COMPARE_ISLAND_SIZE=100 bun run bench:react:huge-document:legacy-compare:local green; bun lint:fix green; bun check green | Benchmark output now has v2Off, v2DefaultOmitted, v2AutoExplicit, v2DomPresent, v2ShellExplicitRadius0, and v2ShellExplicitRadius1, plus per-surface trace fields for mode, grouping, shell, group size, corridor, staged mounting, readiness, mounted/pending/stale counts, and background chunks. deltaMeanMsBySurface now compares every v2 surface separately | Trace fields are scaffolding for the lifecycle phase. One-iteration 2000-block smoke showed default/DOM-present full-doc lanes red: v2DomPresent replace 111.64 ms and fragment 108.88 ms versus legacy chunk-on 43.89 ms and 41.66 ms | phase-7-dom-present-full-doc-lifecycle |
| phase-7-dom-present-stale-dom-proof | complete, perf-red | .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; focused stale-DOM test green; full large-doc package test green with 20 tests; profiled 2000-block compare written to /tmp/slate-v2-profile-2000-phase7-full-doc.out; clean 2000-block compare green; bun lint:fix green; bun check green | DOM-present full-document replacement now has a replayable correctness proof: old far text is absent from DOM immediately after visible commit, new active text is present, and shell nodes are not involved. The benchmark trace now reports interactiveReadyAt and nativeSurfaceCompleteAt as equal for synchronous DOM-present/off rows, while shell leaves nativeSurfaceCompleteAt: null. The benchmark also has model-only full-doc lanes: replaceFullDocumentWithTextModelCommitMs and insertFragmentFullDocumentModelCommitMs | Correctness is green, but lifecycle performance is still red. Clean 2000-block smoke: legacy chunk-on ready 130.21 ms, replace 47.28 ms, fragment 44.03 ms; v2DomPresent ready/native surface 187.11 ms, replace visible 96.50 ms, fragment visible 118.21 ms, model replace 11.66 ms, model fragment 11.48 ms. The gap is mounted DOM/React visible flush, not model commit | phase-7-dom-present-full-doc-lifecycle-perf-cut |
| phase-7-dom-present-staged-group-lifecycle | complete, needs-5000-gate | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; focused DOM-present tests green; full large-doc package test green with 22 tests; provider DOM-present tests green; node --check green; 2000-block one-iteration compare green; bun lint:fix green; bun check green | Default/DOM-present root groups now stage far groups: interactive-ready mounts the active group, pending groups expose inert hidden placeholders with no stale text, selected pending groups materialize urgently, and background mounting fills native surface later. The root runtime receives mounted ranges for DOM-present staging, so broad selections are model-backed until DOM exists. Benchmark trace now includes nativeSurfaceCompleteMs as a separate lane instead of pretending staged ready means full native DOM. | 2000-block smoke improved the red owner: legacy chunk-on ready 143.21 ms, replace 45.57 ms, fragment 89.78 ms; v2DomPresent interactive ready 89.90 ms, native surface complete 211.48 ms, replace visible 36.21 ms, fragment visible 47.67 ms. This is promising but not release proof: one iteration only, 5000-block medians not run, native beforeinput remains red/noisy (v2DomPresent middle native beforeinput 111.94 ms at 2000), and group size/corridor/budget are still fixed first-cut values. | phase-7-5000-default-gate |
| phase-7-root-group-size-250 | complete, selection-red | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; split-selection diagnostic artifact .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-split-selection-g250.json; focused DOM-present tests green; full large-doc package test green with 22 tests; provider DOM-present tests green; bun --filter slate-react typecheck, node --check, bun lint:fix, and bun check green | DOM-present default root group size moved from 1000 to 250, and the benchmark now has opt-in split-selection lanes plus disposeDelayMs so selection activation can be separated from post-selection typing. This cut interactive ready and full-doc visible commit hard: normal 5000 one-iteration v2DomPresent ready 40.08 ms, replace 10.20 ms, fragment 18.95 ms versus legacy chunk-on 300.66 ms, 122.80 ms, 115.13 ms. | The 5000 gate is still not closed. v2DomPresent middle select+type remains red at 88.80 ms versus legacy chunk-on 39.27 ms; split diagnostic shows the owner is pending-group materialization/selection (middleBlockSelectMs 66.16 ms, while middleBlockTypeAfterSelectMs is 22.74 ms). nativeSurfaceComplete is now slower and must stay separately reported (1085.07 ms for v2DomPresent, 1311.16 ms for default omitted). | phase-7-selection-materialization-cost-cut |
| phase-7-root-group-size-100-batched-background | complete, event-input-red | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; split-selection artifact .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-split-selection-g100.json; normal artifact .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark.json; focused DOM-present tests green; provider DOM-present tests green; bun --filter slate-react typecheck green; node --check green | DOM-present root group size moved from 250 to 100, and background native-surface mounting now batches 4 pending groups per tick. Normal 5000 one-iteration smoke: legacy chunk-on ready 352.22 ms, middle type 33.96 ms, middle select/type 66.67 ms, replace 183.13 ms, fragment 115.18 ms; v2DomPresent ready 23.86 ms, native surface complete 827.68 ms, middle type 9.04 ms, middle select/type 38.35 ms, replace 25.56 ms, fragment 7.58 ms; v2DefaultOmitted ready 34.62 ms, native surface complete 1050.22 ms, middle type 8.69 ms, middle select/type 49.51 ms, replace 8.57 ms, fragment 8.38 ms. | This closes the selection/materialization owner for smoke only, not release medians. Split diagnostic confirms the cut: v2DomPresent middle select 15.59 ms, middle type-after-select 23.58 ms, middle select/type 25.89 ms versus legacy chunk-on 1.34 ms, 32.75 ms, 38.31 ms. The active red owner is event input: normal v2DomPresent middle model-beforeinput 36.61 ms and native-beforeinput 73.69 ms; default omitted native-beforeinput is 99.43 ms. | phase-7-beforeinput-event-path-after-materialization |
| phase-7-beforeinput-lane-truth | complete, needs-medians | .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; artifact .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-model-beforeinput-renamed.json; 100-block smoke green; profiled 5000-block diagnostic green; clean 5000-block diagnostic green; node --check green | Removed the fake *NativeBeforeInputTypeMs labels from the jsdom runner. The old lane dispatched @, which is intentionally not native-eligible, so it measured model-owned beforeinput plus selection/setup. The runner now reports *ModelBeforeInputTypeMs and *SelectThenModelBeforeInputTypeMs; real native browser insertion remains an explicit missing proof lane. Clean 5000 one-iteration after the rename: legacy chunk-on ready 345.40 ms, middle type 48.61 ms, middle select/type 50.24 ms, replace 119.51 ms, fragment 112.98 ms; v2DomPresent ready 29.26 ms, native surface complete 866.41 ms, middle type 8.70 ms, middle select/type 65.36 ms, model-beforeinput 36.09 ms, select-then-model-beforeinput 55.78 ms, replace 7.96 ms, fragment 7.99 ms; v2AutoExplicit ready 22.81 ms, middle type 8.68 ms, middle select/type 51.04 ms, model-beforeinput 36.66 ms, select-then-model-beforeinput 58.50 ms, replace 7.76 ms, fragment 10.25 ms. | This does not close default performance. It deletes bad evidence. One-iteration values are noisy: v2DomPresent direct middle select/type and promote are still red in this clean run, while prepared model-owned beforeinput is near/under legacy typing. Next proof must be 5000-block medians with honest lane names and a separate real browser-native input proof. | phase-7-5000-median-confirmation-after-event-path |
| phase-7-5000-median-confirmation-after-event-path | complete, native-proof-and-selection-red | .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-median-model-beforeinput-renamed.json; focused large-doc/provider tests, slate-react typecheck, node --check, bun lint:fix, and bun check green before the median run; REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_TYPE_OPS=10 REACT_HUGE_COMPARE_ACTIVE_RADIUS=0 REACT_HUGE_COMPARE_ISLAND_SIZE=100 bun run bench:react:huge-document:legacy-compare:local green | 5000-block medians confirm the DOM-present/default architecture is materially better than the one-iteration fog suggested. Legacy chunk-on median: ready 295.57 ms, selectAll 0.69 ms, middle type 33.37 ms, middle select/type 31.68 ms, promote/type 34.39 ms, replace 115.05 ms, fragment 111.26 ms. v2DomPresent median: ready 24.63 ms, native surface complete 999.66 ms, selectAll 0.19 ms, middle type 8.51 ms, middle select/type 38.31 ms, promote/type 47.71 ms, model-beforeinput 23.95 ms, select-then-model-beforeinput 54.26 ms, replace visible 8.24 ms, fragment visible 7.78 ms. v2AutoExplicit median: ready 23.95 ms, middle type 9.07 ms, middle select/type 42.10 ms, model-beforeinput 31.26 ms, replace visible 7.71 ms, fragment visible 15.93 ms. | Default DOM-present now passes the important ready/type/selectAll/visible-full-doc lanes against legacy chunk-on, but it is not done. Remaining red owners: selection/setup-inclusive lanes, promote-then-type, select-then-model-beforeinput, and no true browser-native insertion proof. nativeSurfaceComplete must keep being reported separately and bounded; do not hide the roughly one-second full native DOM warmup behind interactive ready. | phase-7-real-native-input-proof-and-selection-setup-cost |
| phase-7-real-native-input-proof | complete, selection-red | .tmp/slate-v2/packages/slate-browser/src/playwright/index.ts, .tmp/slate-v2/site/examples/ts/large-document-runtime.tsx, .tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.ts, .tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts, .tmp/slate-v2/packages/slate-react/src/editable/runtime-keyboard-events.ts, .tmp/slate-v2/packages/slate-react/src/editable/runtime-event-engine.ts, .tmp/slate-v2/packages/slate-react/src/editable/native-input-strategy.ts, .tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts, .tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts, .tmp/slate-v2/packages/slate-dom/src/plugin/with-dom.ts, .tmp/slate-v2/packages/slate-dom/test/bridge.ts, .tmp/slate-v2/packages/slate-react/test/kernel-authority-audit-contract.ts; bun test ./packages/slate-dom/test/bridge.ts, bun test ./packages/slate-react/test/model-input-strategy-contract.ts, bun test ./packages/slate-react/test/large-doc-and-scroll.tsx, package typechecks for slate-dom/slate-react/slate-browser, focused Chromium Playwright native typing row, audit contract row, bun lint:fix, and bun check green | Added a separate DOM-present large-document route and Chromium proof that uses a real click plus page.keyboard.type, then asserts model text, model selection, DOM caret, and a native-allowed input trace. The path to native input was blocked by three real issues: large-document keydown intercepted all printable keys instead of only shell-backed selection, empty marks {} denied native input, and selection-only ops marked the DOM node map dirty. After those cuts, native DOM-to-model repair still produced an invalid model selection because it selected and inserted in separate updates; repair now inserts at the explicit pre-native point and then sets the post-insert selection. | Real native insertion proof is now green for the active DOM-present group. The Phase 7 default claim is still pending because selection/setup-inclusive lanes, promote-then-type, and select-then-model-beforeinput are still slower than the target medians; native-surface completion remains separate and roughly one second at 5000 blocks. | phase-7-selection-setup-promote-cost |
| phase-7-selection-setup-promote-cost | complete, stress-red | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; artifacts .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-split-selection-median.json, .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-split-selection-group50-median.json, and .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-group50-stress.json; focused large-doc/provider tests green; bun --filter slate-react typecheck green; node --check green; bun lint:fix green; bun check green | Measured the remaining selection/setup owner with split-selection medians, then cut DOM-present root group size from 100 to 50 and kept background throughput at eight 50-block groups per tick. 5000-block medians now pass the default product lanes: legacy chunk-on ready 307.75 ms, middle type 31.51 ms, middle select/type 32.33 ms, promote/type 32.25 ms, replace 114.35 ms, fragment 114.17 ms; v2DefaultOmitted ready 23.41 ms, middle type 8.74 ms, middle select/type 39.73 ms, promote/type 38.78 ms, model-beforeinput 40.52 ms, select-then-model-beforeinput 48.81 ms, replace 7.83 ms, fragment 7.04 ms; v2DomPresent ready 20.65 ms, middle type 8.66 ms, middle select/type 38.99 ms, promote/type 33.97 ms, model-beforeinput 28.39 ms, replace 8.15 ms, fragment 7.13 ms. | The 5000 gate improved, but the 10000 stress gate is red. With group size 50, 10000-block medians show default ready/full-doc still crush legacy (v2DefaultOmitted ready 42.44 ms, replace 13.03 ms, fragment 20.84 ms vs legacy chunk-on 613.79 ms, 262.21 ms, 255.53 ms), but selection-inclusive lanes scale badly: v2DefaultOmitted middle select/type 84.35 ms and promote/type 115.28 ms vs legacy 37.90 ms and 35.86 ms; v2DomPresent middle select/type 79.21 ms and promote/type 71.00 ms. A dynamic 100-block stress-size smoke was rejected because it made the surface noisy and worsened other lanes. | phase-7-10000-selection-stress-cost |
| phase-7-10000-pending-placeholder-coalescing | complete, stress-red | .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx, .tmp/slate-v2/packages/slate-react/test/large-doc-and-scroll.tsx, .tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx, .tmp/slate-v2/scripts/benchmarks/browser/react/huge-document-legacy-compare.mjs; artifacts .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-split-group50-coalesced-smoke.json, .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-5000-split-group50-coalesced-median.json, and .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-group50-coalesced-stress.json; focused large-doc/provider tests green; bun --filter slate-react typecheck green; node --check green; bun lint:fix green; bun check green | Coalesced contiguous pending DOM-present groups into one hidden placeholder range instead of rendering one hidden placeholder per pending group. This keeps stale DOM absent and mounted ranges unchanged while cutting root child reconciliation pressure. 5000-block coalesced medians keep the default rows green: legacy chunk-on ready 295.89 ms, middle type 35.26 ms, select/type 31.08 ms, promote/type 32.74 ms; v2DefaultOmitted ready 19.44 ms, middle type 8.92 ms, select/type 31.55 ms, promote/type 35.93 ms; v2AutoExplicit ready 19.26 ms, middle type 9.10 ms, select/type 32.00 ms, promote/type 33.66 ms. | The 10000 stress owner improved but remains red. Coalesced 10000 medians: legacy chunk-on ready 598.29 ms, middle type 40.83 ms, select/type 34.70 ms, promote/type 35.75 ms, replace 257.71 ms, fragment 254.46 ms; v2DefaultOmitted ready 33.30 ms, middle type 21.66 ms, select/type 69.03 ms, promote/type 72.29 ms, model-beforeinput 38.04 ms, replace 14.22 ms, fragment 12.76 ms; v2DomPresent ready 43.45 ms, middle type 20.28 ms, select/type 75.74 ms, promote/type 85.46 ms. Next owner is not raw typing; it is the selection-inclusive render/selection repair path at 10000 blocks. | phase-7-10000-selection-stress-cost |
| phase-7-10000-eager-active-group-persist-removal | rejected, reverted | .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-split-group50-coalesced-no-eager-persist-smoke.json; focused large-doc/provider tests green before and after revert; bun --filter slate-react typecheck green; node --check green; bun lint:fix green; bun check green | Tested removing the eager effect that persisted the active root group into mounted state, because the profile suggested a possible post-selection React flush. The 10000-block split smoke did not validate the theory: v2DefaultOmitted select/type was 73.94 ms and promote/type regressed to 115.57 ms; v2DomPresent select/type was 65.55 ms and promote/type 107.39 ms. The patch was reverted. | Keep coalesced placeholders and eager active-group persistence. Do not chase mounted-state persistence next; the remaining owner is deeper selection-inclusive render/selection repair cost at 10000 blocks. | phase-7-10000-selection-stress-cost |
| phase-7-10000-local-tuning-rejections | complete, follow-up-owner | .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-split-group50-coalesced-profile-after-revert.json, .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-split-group25-smoke.json, .tmp/slate-v2/tmp/slate-react-huge-document-legacy-compare-benchmark-10000-split-group50-background-delay0-smoke.json; focused large-doc/provider tests green; bun --filter slate-react typecheck green; node --check green; bun lint:fix green; bun check green | Profile confirmed the remaining immediate far-selection cost is materializing/rendering the selected DOM-present group plus the following text update. Two cheap local levers were rejected: 25-block groups made the surface noisy and worsened important rows (v2DefaultOmitted select/type 199.69 ms, direct middle type 20.66 ms; v2DomPresent promote/type 112.54 ms), while immediate background mounting was catastrophic (v2DefaultOmitted selectAll 1450.40 ms, middle type 1497.83 ms, select/type 1841.38 ms). Both patches were reverted. | Current default claim can close for the 5000-block release gate, but do not claim the 10000-block immediate far-selection stress row is fixed. Follow-up needs deeper architecture around pending group interaction/materialization, not constant tuning. | phase-7-default-claim-gate |
Latest status is closed for the current default-performance gate, with a recorded follow-up for 10000-block immediate far-selection stress.
The previous shell-policy execution is complete:
The default-performance lane is complete for the current release claim because these Phase 7 gates now pass or have a recorded owner:
v2NoIsland (Phase 7.0 complete);interactiveReady and nativeSurfaceComplete are measured separately;Do not upgrade this into a 10000-block immediate far-selection victory. That is the next research/perf owner.