docs/plans/2026-04-19-slate-core-perf-coverage-plan.md
Before leaving .tmp/slate-v2/packages/slate, land a real core-performance
benchmark package that covers the hot slate engine families instead of one
lonely #6038 lane.
The bar is not:
slate-react already winsThe bar is:
slate core familyslate-react with a trustworthy core baseline instead of vibesToday the live repo only has one runnable package-local perf command:
bun run bench:slate:6038:localThat lane is useful, but too narrow:
Editor.withTransaction(...) vs
Transforms.applyBatch(...) inside current slateThe draft repo already solved most of the benchmark shape problem:
scripts/benchmarks/But that setup drifted:
repo-compare.mjs assumes non-Yarn means pnpmslate-v2 is Bun-ownedslate is Yarn PnPSo the first honest task is not “add more benchmarks”. It is “restore benchmark infrastructure that can run across current Bun and legacy Yarn without lying”.
.tmp/slate-v2/scripts/benchmarks/shared/stats.mjsshared/repo-compare.mjsbench:slate:6038:local alive and added:
bench:core:transaction:localbench:core:normalization:localbench:core:query-ref-observation:localbench:core:node-transforms:localbench:core:text-selection:localbench:core:editor-store:localbench:core:refs-projection:localbench:core:normalization:compare:localbench:core:observation:compare:localbench:core:huge-document:compare:localThe benchmark package is live, and the regression picture is now split cleanly:
First compare read:
+274.61ms+6144.98ms+3184.18mseditor.children.length after each write: +2329.16msEditor.nodes(...) after each write: +2431.04msEditor.positions(...) after each write: +2519.43ms+4269.43ms+4216.04msLatest compare read after the core text/normalize pivots:
-7.01ms-86.58ms+32.15mseditor.children.length after each write: +22.07msEditor.nodes(...) after each write: +16.44msEditor.positions(...) after each write: +59.98ms+22.32ms+20.97msThat means:
cd .tmp/slate-v2 && bun test ./packages/slate/testcd .tmp/slate-v2 && bunx turbo build --filter=./packages/slatecd .tmp/slate-v2 && bunx turbo typecheck --filter=./packages/slatecd .tmp/slate-v2 && bun run lint:fixcd .tmp/slate-v2 && bun run lintcd .tmp/slate-v2 && bun test ./packages/slate/test/snapshot-contract.ts --bail 1continue checkpoint:
continue invocations should keep returning replanstatus: completed overrides the pasted stale promptreplanreplan verdictpublic-state.ts cut that removed the redundant
previous-snapshot clone and whole-tree change diffing because it failed to
move the red lanes enough and tripped the standalone snapshot ownercore/apply.tseditor/normalize.tspackages/slate perf work if the bounded remaining gap is not
acceptablebun test ./packages/slate/testbun run bench:core:normalization:compare:localbun run bench:core:observation:compare:localbun run bench:core:huge-document:compare:localIn scope:
.tmp/slate-v2/scripts/benchmarks/**.tmp/slate-v2/package.jsonplate-2slate lanes onlyOut of scope:
slate-history perf beyond keeping its future compare lane unblockedslate-dom and slate-react runtime/browser perf lanes“Full transforms/api coverage” does not mean one benchmark per exported function.
That would be stupid and noisy.
For packages/slate, full coverage means every performance-relevant family has
an owner lane:
Pure value helpers like Path.equals(...) or Range.includes(...) do not get
their own lanes unless a benchmark proves they are part of a real hot path.
Architecture and proof owners:
Draft benchmark shape to reuse:
core/current/normalization.mjscore/current/query-ref-observation.mjscore/current/node-transforms.mjscore/compare/huge-document.mjscore/compare/observation.mjscore/compare/normalization.mjsshared/repo-compare.mjsshared/stats.mjsExisting live lane to preserve:
scripts/benchmarks/, not ad hoc files.slate-react.Purpose:
Lanes:
#6038 lane as the stable transaction smoke owner#6038 stays too issue-shapedSurfaces covered:
Editor.withTransaction(...)Transforms.applyBatch(...)editor.apply(...)Current owner input:
.tmp/slate-v2/scripts/benchmarks/slate/6038-transaction-execution.mjsPurpose:
Lanes:
core/current/normalization.mjscore/compare/normalization.mjsSurfaces covered:
Editor.normalize(...)Draft source:
.tmp/slate-v2-draft/scripts/benchmarks/core/current/normalization.mjs.tmp/slate-v2-draft/scripts/benchmarks/core/compare/normalization.mjsPurpose:
Lanes:
core/current/query-ref-observation.mjscore/compare/observation.mjsSurfaces covered:
editor.childrenEditor.nodes(...)Editor.positions(...)Editor.pathRef(...)Editor.rangeRef(...)Editor.rangeRefs(...)Draft source:
.tmp/slate-v2-draft/scripts/benchmarks/core/current/query-ref-observation.mjs.tmp/slate-v2-draft/scripts/benchmarks/core/compare/observation.mjsPurpose:
Lanes:
core/current/node-transforms.mjsSurfaces that must be represented before this family is called complete:
insertFragmentinsertNodessetNodesmoveNodessplitNodesmergeNodesremoveNodeswrapNodesunwrapNodesliftNodesDecision:
Draft source:
.tmp/slate-v2-draft/scripts/benchmarks/core/current/node-transforms.mjsPurpose:
New lane:
core/current/text-selection.mjsSurfaces covered:
insertTextdeleteselectsetSelectionsetPointmovecollapseReason:
Purpose:
slate-react should lean
onNew lane:
core/current/editor-store.mjsSurfaces covered:
getChildrensetChildrengetSnapshotreplaceresetsubscribeReason:
Purpose:
New lane:
core/current/refs-projection.mjsSurfaces covered:
Editor.projectRange(...)Editor.bookmark(...)Editor.rangeRef(...)Reason:
slate-react tries to build locality on topPurpose:
Lane:
core/compare/huge-document.mjsSurfaces covered:
Draft source:
.tmp/slate-v2-draft/scripts/benchmarks/core/compare/huge-document.mjsFiles:
.tmp/slate-v2/scripts/benchmarks/README.md.tmp/slate-v2/scripts/benchmarks/shared/stats.mjs.tmp/slate-v2/scripts/benchmarks/shared/repo-compare.mjs.tmp/slate-v2/package.jsonPlan:
slate-v2#6038 so it lives inside the canonical structureshared/stats.mjsshared/repo-compare.mjs, but fix package-manager handling for:
Critical implementation decision:
Verification:
slate-v2 and legacy slateFiles:
.tmp/slate-v2/scripts/benchmarks/core/current/transaction-execution.mjs
or equivalent wrapper for #6038.tmp/slate-v2/scripts/benchmarks/core/current/normalization.mjs.tmp/slate-v2/scripts/benchmarks/core/current/query-ref-observation.mjs.tmp/slate-v2/scripts/benchmarks/core/current/node-transforms.mjsPlan:
#6038 artifact and command alive.tmp/Verification:
Files:
.tmp/slate-v2/scripts/benchmarks/core/current/text-selection.mjs.tmp/slate-v2/scripts/benchmarks/core/current/editor-store.mjs.tmp/slate-v2/scripts/benchmarks/core/current/refs-projection.mjsPlan:
slate public surfacetransaction-contract.tssurface-contract.tsrange-ref-contract.tsbookmark-contract.tsclipboard-contract.tstransforms-contract.tsVerification:
Files:
.tmp/slate-v2/scripts/benchmarks/core/compare/normalization.mjs.tmp/slate-v2/scripts/benchmarks/core/compare/observation.mjs.tmp/slate-v2/scripts/benchmarks/core/compare/huge-document.mjsPlan:
pnpm assumptions with current package-manager-aware plumbing.tmp/slate-normalization-compare-benchmark.json.tmp/slate-core-observation-benchmark.json.tmp/slate-core-huge-document-benchmark.jsonVerification:
/Users/zbeyens/git/slate-v2/Users/zbeyens/git/slateFiles:
.tmp/slate-v2/package.jsonPlan:
bench:slate:6038:localbench:core:normalization:localbench:core:query-ref-observation:localbench:core:node-transforms:localbench:core:text-selection:localbench:core:editor-store:localbench:core:refs-projection:localbench:core:normalization:compare:localbench:core:observation:compare:localbench:core:huge-document:compare:localVerification:
Files:
.tmp/slate-v2/scripts/benchmarks/README.md/Users/zbeyens/git/plate-2/docs/slate-v2/replacement-gates-scoreboard.md/Users/zbeyens/git/plate-2/docs/slate-v2/true-slate-rc-proof-ledger.md/Users/zbeyens/git/plate-2/docs/slate-v2/slate-tranche-3-execution.md
if the tranche record needs a perf-package addendumPlan:
Verification:
slate-react and overlay-local runtime lanespackages/slate can be leftAnything else is backwards.
Benchmark infra/package verification:
bench:core:* and bench:slate:6038:local command once.tmp/*.json artifactRegression verification:
cd .tmp/slate-v2 && bun test ./packages/slate/testcd .tmp/slate-v2 && bunx turbo build --filter=./packages/slatecd .tmp/slate-v2 && bunx turbo typecheck --filter=./packages/slatecd .tmp/slate-v2 && bun run lint:fixcd .tmp/slate-v2 && bun run lintpackages/slate perf measurement is good enough to leave only when all of
this is true:
slate-v2#6038 still runsThat bar is not met on the current tree because the compare lanes expose major regressions vs legacy.
Failure mode:
Counter:
Failure mode:
pnpm assumptions break BunCounter:
Failure mode:
Counter:
slate-reactFailure mode:
Counter:
slate-react trancheThis is worth doing before moving on.
The current repo has enough slate correctness proof to leave the package, but
not enough slate perf proof to say the core is fully measured.
So the next honest packages/slate batch is not more contract recovery.
It is this benchmark package.