docs/plans/2026-04-19-slate-absolute-api-replan.md
Reopen packages/slate despite the stale tranche-3 closeout read and drive it
toward the best honest public API:
packages/slate is closed and tranche 4 is
nextslate public surfaces stay primary,
which become advanced runtime tools, and which get cut or demoted”slate core API direction is settled enough to become the live claimbun test ./packages/slate/test/snapshot-contract.ts --bail 1move_node row is gonebun test ./packages/slate/test/snapshot-contract.ts --bail 1bun test ./packages/slate/test is green again55-row cluster is now cut at one explicit owner:
/Users/zbeyens/git/slate-v2/packages/slate/test/fixture-claim-overrides.tsbunx turbo build --filter=./packages/slate greenbunx turbo typecheck --filter=./packages/slate greenbun run lint:fix greenbun run lint greenBaseEditor does not expose editor.operations
as a normal fieldEditor.getOperations(editor) / editor.getOperations() are now the
canonical operations read surfaceEditor.withTransaction(editor, tx => ...) now exposes live draft state
through the transaction argument:
tx.childrentx.selectiontx.markstx.operationsdocs/slate-v2/**slate core redesign
settleseditor.children directly for live
draft reads in:
transforms-text/delete-text.tsutils/get-default-insert-location.tsEditor.getChildren(editor)Editor.getChildren(editor) over editor.children for live draft
readsinsertText no longer pays snapshot-read tax in its null-selection guard;
it uses an explicit public-selection helper insteadtx.apply(op) instead of
editor.apply(op) where the transaction already exists:
interfaces/transforms/general.tseditor/insert-break.tstransforms-node/move-nodes.tstransforms-text/delete-text.tstx.apply(op) is no longer decorativeeditor.apply(op)applyOperation(editor, op) helper now routes helper/transform
code through the active transaction writer when one existseditor.apply(op) directly:
interfaces/transforms/text.tstransforms-selection/select.tstransforms-selection/deselect.tstransforms-selection/set-selection.tstransforms-node/remove-nodes.tstransforms-node/set-nodes.tstransforms-node/split-nodes.tstransforms-node/insert-nodes.tstransforms-node/merge-nodes.tstx.apply(op) is now backed by the base core writer instead of delegating
through an overridden editor.applyeditor.apply and still hit
the core transaction path directlyEditor.apply(editor, op) is now the explicit public single-op writer
over that same transaction patheditor.apply(op) no longer owns the public single-op path by
defaultpackages/slate/test/surface-contract.tspackages/slate/test/transaction-contract.tseditor.onChange()editor.onChange() is now classified as a compatibility callback over the
snapshot-store path, not the primary commit ownerpackages/slate/test/snapshot-contract.tsinterfaces/Editor/** fixtures now prefer explicit read APIs over
ambient property mirrorseditor.children: 0editor.selection: 2editor.marks: 4editor.children: 9editor.selection: 4editor.marks: 1src/core/public-state.tssrc/interfaces/editor.tstest/accessor-transaction.test.tstest/interfaces-contract.tstest/snapshot-contract.tstest/surface-contract.tstest/transaction-contract.tseditor.marks property writes are now explicitly proved in:
packages/slate/test/snapshot-contract.tsBaseEditor no longer declares editor.children,
editor.selection, editor.marks, or editor.operations as normal fieldsgetChildren, getSelection, getSnapshot,
getOperations, and transaction APIs insteadeditor.apply(op), reset-style setChildren, and broad live
accessors<Slate onChange> belongs to the React adapter surface, not core editor
statepackages/slate/test/fixture-claim-overrides.ts/Users/zbeyens/git/slate-v2/Users/zbeyens/git/slate-v2/packages/slate/**/Users/zbeyens/git/slate-v2/scripts/benchmarks/**/Users/zbeyens/git/slate-v2/package.json/Users/zbeyens/git/plate-2/docs/slate-v2/**/Users/zbeyens/git/plate-2/docs/plans/**slate proof or perf owner forces it:
slate-historyslate-hyperscriptslate-domslate-reactpackages/slatepackages/slate
is doneslate API direction is still
undecidedmove_node just to satisfy a stale snapshot row when the live docs already
define that cleanup as explicit-onlyeditor.children, editor.selection, editor.marks, and
editor.operations are not normal live fields on the current BaseEditorNone blocking this slate core lane.
Current hard-cut ledger:
editor.childreneditor.selectioneditor.markseditor.operationseditor.apply(op) as an ordinary app write pathsetChildren<Slate onChange> classified as React adapter output, not a core editor
fieldCustomTypes cut unless later code work proves reopening it earns its
costbun run bench:core:normalization:compare:localbun run bench:core:observation:compare:localbun run bench:core:huge-document:compare:localcd .tmp/slate-v2 && bun test ./packages/slate/test/snapshot-contract.ts --bail 1cd .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 run bench:slate:6038:localcd .tmp/slate-v2 && bun run bench:core:normalization:compare:localcd .tmp/slate-v2 && bun run bench:core:observation:compare:localcd .tmp/slate-v2 && bun run bench:core:huge-document:compare:localslate core API direction as settled enough to unblock tranche 4slate core design questions without new contrary evidencereplaneditor.operations is not a normal current BaseEditor fieldEditor.getOperations(editor) is the explicit operations read surfacewithTransaction(tx => ...) is now an explicit draft-access APIEditor.apply(editor, op) is now the explicit public single-op writersubscribe(...) is now the primary post-commit API ahead of
editor.onChange()editor.operations no longer owns the internal queue directlyeditor.apply(op) is no longer the public default write patheditor.onChange() is no longer treated as the primary commit APIeditor.children / editor.selection / editor.marks are no longer
normal current BaseEditor fieldseditor.apply(op), reset-style
setChildren, broad live accessors, and docs/examples that teach them as
normal DXeditor.onChange() should be discussed only as React adapter output when
referring to current live shapeslate core API-direction decisions remain in this lanepackages/slate churn under a completed lanecontinue calls against this same owner without a new scope or new
contrary evidence should return:
replanpackages/slate work here would be invented churn, not progress44continue was received again against the same completed owner
with no new scope, evidence, or blocker changereplanpackages/slate/src/core/normalize-node.ts so adjacent-text
canonicalization is explicit-only instead of ordinary-op default behaviorpackages/slate/test/snapshot-contract.tspackages/slate/test/fixture-claim-overrides.tspackages/slate/test/index.spec.ts to skip that explicit-cut family
from one ownerBaseEditor does not expose editor.operations as
a normal field. Files touched at the time:
packages/slate/src/core/public-state.tspackages/slate/src/core/apply.tspackages/slate/src/interfaces/editor.tspackages/slate/src/create-editor.tspackages/slate/src/editor/without-normalizing.tsEditor.withTransaction(editor, tx => ...)tx.apply(op)tx.apply(op) into transaction-owned source paths in:
packages/slate/src/interfaces/transforms/general.tspackages/slate/src/editor/insert-break.tspackages/slate/src/transforms-node/move-nodes.tspackages/slate/src/transforms-text/delete-text.tsapplyOperation(editor, op) as the internal writer helper in:
packages/slate/src/core/public-state.tspackages/slate/src/interfaces/transforms/text.tspackages/slate/src/transforms-selection/select.tspackages/slate/src/transforms-selection/deselect.tspackages/slate/src/transforms-selection/set-selection.tspackages/slate/src/transforms-node/remove-nodes.tspackages/slate/src/transforms-node/set-nodes.tspackages/slate/src/transforms-node/split-nodes.tspackages/slate/src/transforms-node/insert-nodes.tspackages/slate/src/transforms-node/merge-nodes.tstx.apply(op) so it now bypasses an overridden editor.apply and
writes through the base core writer in:
packages/slate/src/core/public-state.tspackages/slate/src/create-editor.tsEditor.apply(editor, op) as the explicit public single-op writer in:
packages/slate/src/interfaces/editor.tseditor.apply(op)
and reuses the transaction path in:
packages/slate/test/surface-contract.tspackages/slate/test/transaction-contract.tseditor.onChange() in:
packages/slate/src/core/public-state.tseditor.onChange() as a compatibility callback with current proof
in:
packages/slate/test/snapshot-contract.tsinterfaces/Editor/** fixture reads off ambient mutable
properties and onto explicit read APIs in:
packages/slate/test/interfaces/Editor/**packages/slate/test/index.spec.tspackages/slate/test/legacy-fixture-utils.tspackages/slate/test/transforms/mergeNodes/path/non-selectable-ancestor.tseditor.marks property writes were proved as
compatibility pressure in:
packages/slate/test/snapshot-contract.tseditor.children in:
packages/slate/src/transforms-text/delete-text.tspackages/slate/src/utils/get-default-insert-location.tseditor.children in:
packages/slate/test/normalization-contract.tspackages/slate/test/extension-contract.tsinsertText off raw editor.selection without paying snapshot tax by
adding an explicit public-selection helper in:
packages/slate/src/core/public-state.tspackages/slate/src/interfaces/transforms/text.tspackages/slate/src/core/public-state.tsCURRENT_SELECTION / CURRENT_MARKS no longer fall through stale public
state when the live value is actually nulldocs/slate-v2/** stack to the perfect-redesign read:
overview.mdmaster-roadmap.mdrelease-readiness-decision.mdreplacement-gates-scoreboard.mdtrue-slate-rc-proof-ledger.mdfresh-branch-migration-plan.mdrelease-file-review-ledger.mdreferences/architecture-contract.mdreferences/slate-batch-engine.mdcommands/launch-next-ralph-batch.mdcommands/reinterview-remaining-scope.mdcommands/replan-remaining-work.mdledgers/README.mdledgers/slate-legacy-draft-contract-corpus.mdledgers/slate-editor-api.mdledgers/slate-transforms-api.mdslate-tranche-3-execution.mddocs/solutions/developer-experience/2026-04-19-slate-explicit-normalization-cuts-should-live-in-one-fixture-override-registry.md