docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md
Date: 2026-04-29 Status: done Review mode: slate-review Current pass: Execution Phase 11 complete
The docs rewrite is directionally good, but the current organization is not yet Slate-maintainer clean.
The repeated sentence class the user called out is a real smell:
Use
tx.marks.add(...)insideeditor.update(...)in normal application code. The primitive mark helper is reference material for code that intentionally works at the bridge layer.
That sentence is not wrong. It is just in the wrong place. Repeating it inside individual primitive method entries makes the reference page feel defensive and implementation-shaped. A Slate maintainer would teach the write model once, then let the API reference be exact.
The best reorg is:
Do not patch the repeated sentences one by one. That would leave the same ownership problem in prettier prose.
Reopened on 2026-04-29 after the API/runtime/docs hard-cut review. The new findings are not cosmetic:
editor make schema policy look like mutable
editor methodstoggleList, setBlock, toggleBlock, and toggleAlignment leaked
product-level formatting into raw Slate core<Slate onChange> currently reads like a full editor-change callback while
source only calls it for childrenChangedSlate and EditableTextBlocks both expose projection/decorator store props
when provider ownership should be the normal public APIEditor.* query helpers are a compatibility compromise; the
no-compat target should route editor-state reads through state / txThe right move is a hard cut, not another wording pass.
Current score: 0.93
| Dimension | Weight | Score | Evidence |
|---|---|---|---|
| React 19.2 runtime performance | 0.20 | 0.93 | Final target removes duplicated projection props from hot React surfaces, makes projectionStore runtime-owned, and replaces app-level useEditorSelector(editor => editor.read(...)) examples with useEditorState. Evidence: SlateProps projection inputs in .tmp/slate-v2/packages/slate-react/src/components/slate.tsx:37-45, duplicate EditableTextBlocksProps inputs in .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:252-282, and existing target-scoped hook guidance in .tmp/slate-v2/docs/concepts/09-rendering.md:147 and :177. |
| Slate-close unopinionated DX | 0.20 | 0.93 | Final target keeps pure node/path/range helpers, but hard-cuts public Editor.* editor-state queries, instance schema predicates, instance query aliases, and opinionated block/list helpers from normal raw core. Evidence: instance predicates and aliases at .tmp/slate-v2/packages/slate/src/interfaces/editor.ts:237-325, static editor query helpers at :101-234 in current docs, opinionated helpers at :360-383, and toggleList in .tmp/slate-v2/packages/slate/src/editor/block-format.ts:120-162. |
| Plate and slate-yjs migration-backbone shape | 0.15 | 0.92 | Final target keeps substrate-only migration: extension state/tx, schema/spec policy, commits, operations, tags, and local runtime ids, with no current adapter compatibility requirement. Operation replay belongs under tx.operations.replay(...), not a public editor.applyOperations(...) escape hatch. Evidence: extension state/tx in .tmp/slate-v2/docs/concepts/08-plugins.md:38-44, commit/operation replay in .tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:53-70, and migration contracts in .tmp/slate-v2/packages/slate/test/migration-backbone-contract.ts:33-195. |
| Regression-proof testing strategy | 0.20 | 0.92 | Final gates cover stale docs, public nav, adapter promises, banned core helpers, callback contracts, duplicate React projection props, and browser-proof family mapping. Evidence: browser contract families in .tmp/slate-v2/packages/slate-browser/src/core/first-party-browser-contracts.ts:24-130 and Pass 8/9 grep gates in this plan. |
| Research evidence completeness | 0.15 | 0.92 | Plan cites live v2 source, live v2 docs, legacy Slate docs, compiled Slate docs research, solution docs style, unit contracts, migration contracts, and browser contracts. No decorative Lexical/ProseMirror/Tiptap refresh is needed because this closure is scoped to Slate API/docs hard cuts. |
| shadcn-style composability and hook/component minimalism | 0.10 | 0.93 | Final target leaves app authors with fewer props and clearer hooks: provider-owned sources, runtime-owned projection store, mount/render/event-only Editable, useEditorState for shell reads, and target-scoped hooks for rendered document nodes. |
Score threshold is met again. The reopened plan has no remaining "maybe" public
API language: projectionStore is runtime-owned and not a normal public
<Slate> or <Editable> prop.
Current docs evidence:
.tmp/slate-v2/docs/api/nodes/editor.md:264 and :314 repeat
transaction-path warnings inside individual mark methods..tmp/slate-v2/docs/api/nodes/editor.md:517-532 repeats
Inside editor.update(...) across primitive insert methods..tmp/slate-v2/docs/api/transforms.md:1-8 already has the right page-level
owner statement for the normal write surface..tmp/slate-v2/docs/api/transforms.md:34-143 currently lists tx.nodes,
tx.selection, tx.text, and editor.applyOperations as distinct
families; the no-compat target folds replay into tx.operations.replay..tmp/slate-v2/docs/concepts/04-transforms.md:1-19 teaches the write model as
narrative concept prose, and :188-209 cleanly separates normalization and
operation replay..tmp/slate-v2/docs/Summary.md:21, :34, and :74 show the remaining IA
smell: Editor Methods labels plus public Docs Proof Map.Legacy Slate evidence:
../slate/docs/Summary.md:17-35 keeps Concepts and API labels as
Transforms, not Editor Methods.../slate/docs/api/transforms.md:1-10 starts with a short purpose and table
of contents.../slate/docs/api/transforms.md:39-118 keeps transform entries terse and
exact.../slate/docs/walkthroughs/05-executing-commands.md:1-9 starts from the
user problem and extracts the command abstraction before the reference layer.Live Slate v2 source evidence:
.tmp/slate-v2/packages/slate/src/interfaces/editor.ts:193-206 defines the
update transaction groups: marks, nodes, selection, and text..tmp/slate-v2/packages/slate/src/interfaces/editor.ts:208-234 currently
exposes applyOperations, read, getSelection, and getSnapshot on
BaseEditor; the no-compat target keeps coherent boundaries and cuts the
public replay escape hatch..tmp/slate-v2/packages/slate/src/interfaces/editor.ts:814-829 currently
exposes primitive writers as API reference material; the no-compat target
moves public writes to tx..tmp/slate-v2/packages/slate/src/interfaces/editor.ts:1394-1403 exposes
Editor.subscribe and Editor.update.Contract evidence:
.tmp/slate-v2/packages/slate/test/read-update-contract.ts:11-48 proves the
coherent read/update boundary and commit tags..tmp/slate-v2/packages/slate/test/read-update-contract.ts:50-129 proves
writes and replay are rejected inside plain reads..tmp/slate-v2/packages/slate/test/write-boundary-contract.ts:27-55 proves
primitive writes outside editor.update are rejected..tmp/slate-v2/packages/slate/test/write-boundary-contract.ts:57-77
currently proves applyOperations as the explicit replay writer; the target
should move that proof to tx.operations.replay(...)..tmp/slate-v2/packages/slate/test/write-boundary-contract.ts:79-114 proves
implicit writes route through editor.update and tx methods.Keep Slate docs calm:
Legacy Slate did this well with Summary.md separating Walkthroughs,
Concepts, API, Libraries, and General. The v2 docs should keep that shape.
Verdict: keep.
The docs reorg is not a runtime change. It must not alter the React guidance that rendered document content uses target-scoped hooks and avoids broad editor subscriptions. Acceptance criteria:
concepts/09-rendering.md still says broad editor subscriptions are for
shell UI, not rendered document nodes.api/nodes/element.md and libraries/slate-react/editable.md still teach
renderVoid as visible-content-only and useElementSelected(target) as an
opt-in hook.Verdict: revise and keep.
The current docs are accurate but too defensive. Slate-close DX means one write-model explanation, not repeated warnings under every primitive method. Acceptance criteria:
Summary.md uses Transforms for concepts/04-transforms.md and
api/transforms.md.concepts/04-transforms.md owns the write model and says primitive editor
write helpers are runtime internals.api/transforms.md is the terse tx reference and includes
tx.operations.*.api/nodes/editor.md documents lifecycle and boundary methods, not public
Editor.* queries or primitive write helpers.addMark, removeMark, insertBreak,
insertSoftBreak, insertNode, or insertText.Verdict: keep.
The reorg must not make raw Slate docs look like Plate docs. Acceptance criteria:
editor.api, editor.tf, PlatePlugin, current Plate adapter, or
current slate-yjs adapter promise enters raw Slate docs.state and tx backbone language.Verdict: keep with proof-map relocation.
The proof map is valuable for agents and maintainers, but it reads like internal release scaffolding in the public Summary. Acceptance criteria:
Docs Proof Map from public Summary.md.docs/general/docs-proof-map.md in place.docs/general/contributing.md or a maintainer
subsection so proof remains findable.Verdict: strengthen gates.
The previous fast gates were directionally right, but path globs must be cwd-stable. Acceptance criteria:
.tmp/slate-v2 with docs as the search root.--glob '!docs/general/changelog.md'.Verdict: enough evidence for a docs reorg.
The compiled Slate docs research plus live legacy Slate docs are sufficient for this scope. No fresh Lexical/ProseMirror/Tiptap refresh is needed because the decision is about Slate IA, not editor architecture. Acceptance criteria:
Verdict: cut, do not add.
Do not add a new public Architecture page and do not split api/editor-runtime
unless the editor API page remains unreadable after reordering. Acceptance
criteria:
api/nodes/editor.md as the reference owner for primitive editor
methods.api/transforms.md as the transaction method reference.concepts/04-transforms.md as the mental-model owner.Preferred nav:
## Concepts
- [Interfaces](concepts/01-interfaces.md)
- [Nodes](concepts/02-nodes.md)
- [Locations](concepts/03-locations.md)
- [Transforms](concepts/04-transforms.md)
- [Operations](concepts/05-operations.md)
- [Commands](concepts/06-commands.md)
- [Editor](concepts/07-editor.md)
## API
- [Transforms](api/transforms.md)
- [Node Types](api/nodes/README.md)
- [Editor](api/nodes/editor.md)
Changes:
Editor Methods back to Transforms.Docs Proof Map from the public General section.general/contributing.md or a maintainer-only note,
not the reader-facing table of contents.This page should own the write model once.
It should say:
editor.update(...).tx.tx.nodes, tx.text, tx.selection, and tx.marks are the normal app
authoring surface.tx.operations.replay(...) is the operation replay path.It should not become a full API catalog. Keep it narrative and example-led, like legacy Slate's transforms concept page.
This page should be the exact transaction API reference.
It should list:
tx.nodes.*tx.text.*tx.selection.*tx.marks.*tx.operations.*, including replayIt can have one top-level note:
Use transaction methods inside
editor.update(...)for normal document changes. Runtime internals may use lower-level helpers, but public document changes and operation replay stay inside the transaction boundary.
Then stop repeating that warning.
Reorganize the page into reader intent:
## Editor
## Creating an editor
createEditor## Reading state with state
editor.read(state => ...)## Updating with tx
## Schema setup
state.schema /
tx.schema## Subscribing to commits
Editor.subscribe, Editor.getLastCommit## Normalization methodsThis keeps the API complete without preserving public Editor.* query clutter
or public editor-instance write escape hatches.
No runtime implementation change is implied by this review.
Docs should describe the runtime with the same boundaries the code already uses:
editor.read for coherent readseditor.update for normal writestx for transaction helperstx.operations.replay(...) for operation replayThe docs should not invent a new API to solve a prose problem.
Keep the React docs mostly as-is, but move proof language out of user docs.
User-facing React docs should say:
attributes + childrenThey should not make every component example sound like a runtime audit.
Plate needs the docs to make the substrate easy to build on:
state and txThe reorg helps Plate because it makes tx the obvious write surface without
turning raw Slate docs into Plate-style plugin documentation.
Keep the collaboration walkthrough substrate-focused:
tx.operations.replay(...)No current-version slate-yjs adapter support is required.
Verdict: keep.
This docs reorg does not change extension, plugin, operation, snapshot, or collaboration behavior. It does touch docs that describe those surfaces, so the ecosystem pass still needs to prove the plan does not drift into adapter promises.
The migration backbone is believable because raw Slate exposes extension groups, not product APIs:
state helpers live inside editor.read(...).tx helpers live inside editor.update(...).Evidence:
.tmp/slate-v2/docs/concepts/08-plugins.md:38-44 shows state and tx usage..tmp/slate-v2/docs/concepts/08-plugins.md:76-131 explains state/write groups
and raw extension slots..tmp/slate-v2/packages/slate/test/migration-backbone-contract.ts:33-132
proves extension namespaces and schema specs without adapter-shaped editor
namespaces.Acceptance:
editor.api, editor.tf, PlatePlugin, plate, yjs, or
table as direct editor namespaces.The collaboration backbone stays deterministic because adapters use commits and operations, not mutable editor fields or runtime ids:
tx.operations.replay(...) inside
editor.update(...).Evidence:
.tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:3-5
says Slate does not choose network, CRDT, persistence, or awareness policy..tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:53-70
currently defines operations as replay contract and applyOperations as
remote import; the no-compat target rewrites that public import path to
tx.operations.replay(...)..tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:96-120
defines commit observation and warns against mutable editor fields..tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:122-150
makes runtime ids local and splits Slate/adapter/React ownership..tmp/slate-v2/docs/walkthroughs/07-enabling-collaborative-editing.md:191-199
explicitly refuses a full multiplayer recipe..tmp/slate-v2/packages/slate/test/migration-backbone-contract.ts:135-195
proves operation replay, tags, and local-only runtime targets.Acceptance:
withYjs, slate-yjs, provider setup, CRDT merge recipes, or
remote-cursor implementation to raw Slate docs.tx.operations.replay(...), and local runtime ids as the raw contract.| Surface | Effect |
|---|---|
docs/concepts/08-plugins.md | Keep extension substrate language: state, tx, extension slots, product APIs above raw Slate. |
docs/walkthroughs/07-enabling-collaborative-editing.md | Keep collaboration substrate language: commits, operations, tags, tx.operations.replay(...), runtime ids local, adapter ownership. |
docs/general/docs-proof-map.md | Keep proof rows for extension namespaces, collaboration replay, and local runtime ids findable after moving the page out of public Summary. |
cd .tmp/slate-v2 && rg -n 'editor\\.api|editor\\.tf|PlatePlugin|withYjs|slate-yjs|Y\\.Doc|yjs' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/api/transforms.md docs/api/nodes/editor.md
cd .tmp/slate-v2 && rg -n 'state|tx|operations\\.replay|commit|operations|tags|runtime ids are local|does not provide a full multiplayer recipe' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/general/docs-proof-map.md
The first grep should return zero matches except intentionally allowed product layer mentions if the revision pass keeps them. The second grep must keep positive hits so the substrate remains documented.
| Risk | Proof owner |
|---|---|
| Docs promote primitive write methods as normal app API | stale-term grep over docs/api/nodes/editor.md and docs/api/transforms.md |
| Docs repeat transaction warnings per method | grep count for normal application code, bridge layer, and Inside editor.update |
| Public nav exposes internal proof artifacts | Summary.md grep for Docs Proof Map |
| Docs hide replay/browser proof from maintainers | contributor/maintainer link to proof map |
| New prose invents APIs | source-ledger grep against packages/slate/src/interfaces/editor.ts and packages/slate-react/src/index.ts |
Verdict: keep and execute in this order after closure.
The plan no longer has a real wording ambiguity. The only correct docs change is an owner-cluster rewrite:
docs/Summary.md
Editor Methods entries to Transforms.Docs Proof Map from public General nav.docs/general/contributing.md
docs/general/docs-proof-map.md.docs/concepts/04-transforms.md
editor.update((tx) => ...).tx.nodes, tx.text, tx.selection, tx.marks, and
tx.operations.docs/api/transforms.md
# Transforms API.editor.update.tx.nodes.*, tx.text.*, tx.selection.*, tx.marks.*, and
tx.operations.*.tx.operations.replay(...).docs/api/nodes/editor.md
Editor.* query helper signatures.normal application code, bridge layer, and
Inside editor.update warnings.Commands and walkthrough polish
editor.update((tx) => ...) for write examples.Proof and gates
| Decision | Final wording rule |
|---|---|
| Public term | Use Transforms in nav and headings. Explain transactions inside the page. |
| Write model owner | concepts/04-transforms.md teaches it once. |
| Exact tx reference | api/transforms.md lists transaction helper signatures. |
| Primitive editor writers | api/nodes/editor.md documents them under one advanced section. |
| Proof map | Keep the page, remove it from public Summary, link it from Contributing. |
| Ecosystem promise | Substrate only: extension state / tx, commits, operations, tags, tx.operations.replay(...), local runtime ids. |
| Explicit non-goals | No current Plate adapter, no current slate-yjs recipe, no new architecture page, no scattered warning patches. |
cd .tmp/slate-v2 && rg -n 'normal application code|bridge layer|Inside .*editor\\.update|primitive .*reference material' docs --glob '!docs/general/changelog.md'
cd .tmp/slate-v2 && rg -n 'Docs Proof Map|Editor Methods' docs/Summary.md
cd .tmp/slate-v2 && rg -n 'editor\\.api|editor\\.tf|PlatePlugin|withYjs|slate-yjs|Y\\.Doc|yjs' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/api/transforms.md docs/api/nodes/editor.md
cd .tmp/slate-v2 && rg -n 'state|tx|operations\\.replay|commit|operations|tags|runtime ids are local|does not provide a full multiplayer recipe' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/general/docs-proof-map.md
The first three gates are fail-on-output after implementation, except for a
deliberate contributor/proof-map link if it is outside Summary.md. The last
gate must keep positive hits.
No browser run is required for docs-only reorganization unless snippets become executable fixtures.
Browser-sensitive claims must still map to:
packages/slate-browser/src/core/first-party-browser-contracts.tsplaywright/stress/generated-editing.test.tsThe proof map should remain, but as maintainer/contributor support material.
| Alternative | Verdict | Reason |
|---|---|---|
| Patch every repeated warning sentence locally | drop | It treats symptoms, not ownership. |
| Delete primitive editor write methods from docs entirely | drop | They are real public/bridge surface and need reference documentation. |
Keep Docs Proof Map in public Summary | drop | It is useful, but it reads as internal release scaffolding beside user docs. Keep the page, move discovery to contributing/maintainer docs. |
| Rename everything to "Transactions" | revise | Accurate internally, but less close to legacy Slate. Keep Transforms in nav and explain v2 transaction shape inside. |
| Add a big new "Architecture" page | drop | That would make docs more plan-like. Reuse Concepts/API split. |
Split api/editor-runtime.md immediately | defer | Only split if api/nodes/editor.md remains too long after reader-intent reordering. |
| Change | Who feels pain | Strongest fair objection | Why this is not change for change's sake | Evidence | Rejected alternative | Migration answer | Docs / example answer | Regression proof | Ecosystem answer | Verdict |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ---- |
| Move repeated tx/bridge warnings into one write-model owner section. | Raw Slate user, docs reader, app author. | "Do not hide the warning; people will call low-level helpers directly." | The warning stays, but it becomes an ownership rule instead of a sermon repeated under every primitive method. Readers learn the model once, then API entries stay scannable. | Repetition is currently visible in .tmp/slate-v2/docs/api/nodes/editor.md:264, :314, and :517-532. Legacy Slate keeps transform entries terse in ../slate/docs/api/transforms.md:39-118. | Patch each repeated sentence locally. Weaker because the page still teaches by caveat spam. | Users coming from the current docs use editor.read / editor.update; runtime-only helpers are not normal public docs. | concepts/04-transforms.md owns the write model. api/transforms.md owns exact tx signatures, including tx.operations.replay. api/nodes/editor.md owns lifecycle and boundary methods. | Cwd-stable grep for normal application code, bridge layer, Inside .*editor.update, and primitive .*reference material outside the chosen owner note. | No adapter compatibility promise. Pass 5 checks that extension tx and collaboration tx.operations.replay docs keep their substrate language. | keep |
| Rename Summary.md labels from Editor Methods back to Transforms. | Slate maintainer, returning Slate user, docs reader. | "Transaction write API or Editor Methods is technically more v2-accurate." | Navigation should speak Slate's stable reader vocabulary. The page itself can explain that v2 transform helpers live on tx inside editor.update. | Legacy ../slate/docs/Summary.md:17-35 uses Transforms under Concepts and API. Current v2 .tmp/slate-v2/docs/Summary.md:21 and :34 use Editor Methods. | Rename everything to Transactions. Weaker because it is precise internally but less Slate-close and makes the docs feel like a new editor. | Readers use the same TOC word as legacy Slate, then learn the v2 transaction shape in the first paragraph of the page. | Summary.md labels become Transforms; api/transforms.md opens with transaction helper wording but keeps the Transforms API title. | rg -n 'Editor Methods' docs/Summary.md returns no matches. | No plugin/collab behavior changes. Plate and slate-yjs migration pressure benefits from less renaming churn in the public docs. | keep |
| Move Docs Proof Map out of the public General nav while keeping the page. | Docs/test author, agent maintainer, release owner. | "Agents and maintainers need this map fast; hiding it makes regression work worse." | The proof map remains a maintainer artifact, just not a normal reader learning page beside Resources and FAQ. Public docs should not look like release scaffolding. | Current v2 .tmp/slate-v2/docs/Summary.md:74 lists Docs Proof Map; legacy Slate General nav has Resources/FAQ-like reader pages, not a proof ledger. | Delete the proof map. Weaker because agents and maintainers lose source-backed regression navigation. | Users reading docs no longer see proof scaffolding in the TOC. Maintainers reach it from Contributing or a maintainer subsection. | docs/general/contributing.md links the proof map with maintainer wording; Summary.md no longer lists it. | rg -n 'Docs Proof Map' docs/Summary.md returns no matches, and a targeted grep confirms a contributor/maintainer link still exists. | No runtime ecosystem change. The proof map still protects extension/collab proof rows for later work. | keep |
| Cut primitive editor write/query methods from public docs. | Runtime bridge author, command author, test author, app author. | "Internal authors still need sharp tools." | They can exist internally, but the public API should not make bridge helpers look like application authoring surface. The rewrite is unpublished, so compatibility debt buys little. | Live source currently exposes primitive helpers and applyOperations; write-boundary tests prove why boundary discipline matters in .tmp/slate-v2/packages/slate/test/write-boundary-contract.ts:27-77. | Keep them documented under Advanced editor write methods. Weaker because it preserves a second public write surface after we already have tx. | App authors use editor.update + tx; operation replay uses tx.operations.replay; internal runtime helpers stay internal. | api/nodes/editor.md documents editor lifecycle, read, update, subscribe, and dispose, while api/transforms.md documents transaction groups. | Source-ledger grep for public Editor.* queries, instance aliases, primitive writers, and editor.applyOperations. | Extension/collab users get substrate through state / tx, not current adapter APIs or public bridge methods. | keep |
| Do not add a new public Architecture page for this cleanup. | Docs reader, app author, maintainer who wants a clean mental model. | "The read/update/runtime model is important enough for its own page." | This request is a docs organization cleanup, not a new architecture launch. A new public page would make the docs more plan-like and less like Slate. | docs/solutions/style.md says advanced/low-level details belong late, and the compiled Slate docs research says Slate's strength is walkthrough/concept/API separation. | Add api/editor-runtime.md or concepts/runtime.md immediately. Weaker because it spreads one write-model rule across another page. | Readers learn the model in concepts/04-transforms.md and exact APIs in api/transforms.md / api/nodes/editor.md. | No new public page unless api/nodes/editor.md remains unreadable after the reorder. | rg --files docs | rg 'editor-runtime | runtime' should not grow for this plan unless the revision pass explicitly reopens the split. | No plugin/collab behavior change. Keeping the docs small avoids accidental product-layer promises. | keep |
| Pass | Status | Evidence added | Plan delta | Open issues | Next owner |
|---|---|---|---|---|---|
| 1. Current-state read and initial score | complete | Read current v2 Summary, Editor API, Transforms API, Commands/Transforms concepts, legacy Summary/Transforms/Commands, research Slate doc-pattern page, docs style guide, solution docs. | Created this plan. Identified write-model ownership split and public proof-map nav smell. | Need live-source/research refresh before raising score. | Pass 2 research and live-source refresh |
| 2. Research and live-source refresh | complete | Added exact current docs lines, legacy docs lines, live Slate v2 source lines, write-boundary tests, migration-backbone tests, browser contract rows, and solution-doc evidence. | Raised score from 0.78 to 0.84; confirmed the plan should fix ownership and IA, not patch warning prose. | Need convert evidence into pressure-pass acceptance criteria. | Pass 3 pressure passes |
| 3. Pressure passes | complete | Added performance, DX, unopinionated-core, migration, regression, research, and simplicity pressure results with acceptance criteria. | Raised score from 0.84 to 0.88; strengthened cwd-stable grep gates; resolved proof-map placement direction; deferred editor-runtime split. | Need maintainer ledger rows to include full required fields. | Pass 4 Slate maintainer objection ledger |
| 4. Slate maintainer objection ledger | complete | Expanded every major docs reorg change with strongest objection, payoff, evidence, rejected alternative, migration answer, docs/example answer, regression proof, ecosystem note, and verdict. | Raised score from 0.88 to 0.90; rejected new public architecture page; kept primitive methods documented but advanced. | Need a focused ecosystem pass to verify extension/collab substrate wording stays precise and does not promise current adapters. | Pass 5 ecosystem maintainer pass |
| 5. Ecosystem maintainer pass | complete | Added Plate/plugin and slate-yjs/collab substrate answers with exact docs, test, and proof-map evidence. | Raised score from 0.90 to 0.91; added adapter-promise and substrate-presence gates. | Need revision pass to reconcile all accepted rows into final implementation checklist and close any wording ambiguity. | Pass 6 revision pass |
| 6. Revision pass | complete | Added final implementation checklist, final accepted decisions, and revision gates. | Raised score from 0.91 to 0.92; resolved wording ambiguity; prepared closure pass. | Need closure pass to verify every final gate and set completion status. | Pass 7 closure score and final gates |
| 7. Closure score and final gates | complete | Verified score threshold, dimension floors, pass schedule, objection ledger verdicts, implementation checklist, proof gates, and completion state. | Marked the review plan and completion file done before the later API/runtime review reopened it. | Reopened by Pass 8. | Pass 8 API/runtime/docs hard-cut review |
| 8. API/runtime/docs hard-cut review | complete | Added live-source evidence for schema predicates, static helpers, opinionated block/list helpers, public transaction helper leakage, React callback naming, duplicated provider/editable props, hook DX, and docs pages. | Dropped score to 0.89; added hard-cut decisions for schema reads, core formatting helpers, withTransaction, callback contracts, provider/editable props, command docs, bundled-source docs, and normalizing detail recovery. | Need closure pass to reconcile final gates and user-review handoff. | Pass 9 closure refresh |
| 9. Closure refresh and final handoff | complete | Resolved projectionStore as runtime-owned/internal, refreshed score to 0.93, reconciled final gates, and added final user-review handoff. | Marked the reopened review plan complete and ready for execution planning. | None. | User review or later execution lane |
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.Transforms nav, current v2
repeated-warning lines, current v2 proof-map nav, source-exposed primitive
editor write methods, write-boundary tests, style guidance, and compiled
Slate docs research.sed reads over the skill, active plan, completion file, continuation file,
style docs, Slate research docs, legacy Slate docs, and current Slate v2
docs; cwd-stable rg over current Slate v2 docs for repeated warning and
nav smells..tmp/slate-v2/docs still contains
Editor Methods, Docs Proof Map, and repeated editor.update warning
prose because this lane is review-only.docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.state/tx docs, raw extension
slots, collaboration commit/operation replay docs, adapter ownership docs,
local runtime id docs, proof-map rows, and migration-backbone contract tests.sed and nl -ba reads over the active plan, slate-review skill,
goal workflow skill, memory registry, current Slate v2 plugin docs,
collaboration docs, docs proof map, and migration-backbone tests; targeted
rg over plugin/collab/proof-map/docs surfaces..tmp/slate-v2/docs still contains the
user-reported docs IA/prose smells because this lane is review-only.editor.api / editor.tf compatibility, and raw
Slate provider/CRDT documentation.docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.sed over the active plan, slate-review skill, completion file, and
continuation file; targeted rg over memory registry for completion-check
semantics..tmp/slate-v2/docs still contains the
user-reported docs IA/prose smells because this lane is review-only.docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.sed over the active plan, slate-review skill, completion file, and
continuation file; rg over active plan and completion file for pending,
unresolved, and final-gate terms..tmp/slate-v2/docs still contains the
user-reported docs IA/prose smells because this lane is review-only; the plan
is ready for a later execution lane.Verdict: hard cut the confusing public surfaces. Do not solve this by adding more docs caveats.
Slate should have four read layers, not a pile of equivalent-looking methods:
| Layer | Keep? | Normal use |
|---|---|---|
Pure data helpers: Node.*, Element.*, Text.*, Path.*, Point.*, Range.* | keep | Pure structural utilities. They do not read editor runtime state. |
Coherent live reads: editor.read(state => ...) | keep | Normal imperative read boundary. |
Transaction reads/writes: editor.update(tx => ...) | keep | Normal command boundary. tx includes read helpers so commands do not juggle api and tf. |
Static Editor.* algorithms | internal only | Compatibility baggage. Public editor-state reads belong on state / tx. |
Instance aliases like editor.start, editor.node, editor.isInline | cut from normal public API | They create a second object-method read surface and make stale reads too easy. |
This means:
editor.read((state) => state.selection.get());
editor.read((state) => state.schema.isInline(element));
editor.update((tx) => {
const point = tx.points.start([]);
tx.selection.select(point);
});
No-compat target: public docs do not teach Editor.start(editor, []) at all.
Internal runtime code can keep internal query helpers, but those helpers are
not package API.
Hard cut these from the normal editor instance surface:
editor.isInline(element)editor.isVoid(element)editor.markableVoid(element)editor.isSelectable(element)editor.isElementReadOnly(element)Canonical shape:
editor.read((state) => state.schema.isInline(element));
editor.update((tx) => tx.schema.isVoid(element));
Keep schema definition on editor.schema:
editor.schema.define({
type: "mention",
inline: true,
void: "markable-inline",
});
Reason: configuring schema is editor-level setup; reading schema policy during a command is state/transaction-level. The current instance predicates make it look like every editor object method is equally safe everywhere.
Keep Element.*, Text.*, Node.*, Path.*, Point.*, and Range.*.
Moving those to editor state would be worse. They are pure data functions, not
runtime state reads.
Cut public Editor.* editor-state query helpers:
Editor.start(editor, at)Editor.end(editor, at)Editor.node(editor, at)Editor.nodes(editor, options)Editor.marks(editor)These can survive as internal implementation utilities if needed, but the
package API should route users through editor.read(state => ...) and
editor.update(tx => ...).
Best docs rule:
editor.read / editor.updatestate.* and tx.*, not public Editor.* queriesThe public editor object should be lifecycle plus coherent boundaries:
editor.read(fn)
editor.update(fn, options?)
editor.subscribe(listener)
editor.dispose()
Do not add random query or command clutter back onto the instance:
editor.start(at);
editor.node(at);
editor.isVoid(element);
editor.toggleList();
editor.withTransaction(fn);
editor.applyOperations(ops);
Remote operation replay should use the same transaction boundary:
editor.update((tx) => {
tx.operations.replay(remoteOps, { source: "remote" });
});
This keeps collaboration/backbone support without reopening a public low-level write escape hatch.
toggleList in raw Slate core is wrong. Same for setBlock,
toggleBlock, and toggleAlignment.
Hard cut from packages/slate public core:
Editor.setBlockEditor.toggleBlockEditor.toggleAlignmentEditor.toggleListRaw Slate can keep generic primitives:
tx.operations.replay(...)Lists, headings, alignment, and product formatting belong in examples,
extensions, or Plate. Core should not ship a baked-in DEFAULT_LIST_TYPES = ['numbered-list', 'bulleted-list'].
withTransactionHard cut withTransaction from the public surface.
Public code should use:
editor.update((tx) => {
tx.marks.toggle("bold");
});
If the runtime still needs a helper, rename it to an internal function such as
runTransaction and stop exporting it through public barrels. Tests that prove
runtime internals can import from an internal test helper path. App, extension,
and docs code should not see withTransaction.
Extensions should add typed state and tx namespaces, not mutate the editor
instance:
defineExtension({
key: "list",
state: {
isActive(state) {},
},
tx: {
toggle(tx, options) {},
},
});
Usage:
editor.update((tx) => {
tx.list.toggle({ type: "bulleted-list" });
});
Raw Slate ships no list namespace. Plate can ship that. The backbone is
extensible without making raw core opinionated.
The current docs/API direction is overcorrected. If onChange exists, it
should mean "the editor changed", not "only the document children changed".
Use this public React contract:
<Slate
editor={editor}
initialValue={initialValue}
onChange={(value, change) => {
if (change.valueChanged) {
save(value);
}
}}
onValueChange={(value, change) => {
save(value);
}}
onSelectionChange={(selection, change) => {
syncToolbar(selection);
}}
/>
Rules:
onChange(value, change) fires for every committed editor change, including
selection-only and marks-only changes.onValueChange(value, change) fires only when document children change.onSelectionChange(selection, change) fires only when model selection
changes.change includes commit, snapshot, operations, valueChanged,
selectionChanged, marksChanged, and tags.<Slate onCommit>. Commit-level consumers should use
editor.subscribe(...) or a named advanced hook, not a second React callback
that competes with onChange.This is closest to Slate DX and clearer than a low-level onCommit prop.
Provider owns editor-level source inputs. Editable owns DOM mount/render/event props. Projection storage is runtime plumbing, not normal public API. Do not expose the same projection plumbing at both levels.
<Slate> public props:
editorinitialValuechildrenonChangeonValueChangeonSelectionChangedecorationSourcesannotationStoresCut from public <Slate>:
projectionStoreIf tests or internal integrations need projection injection, use an internal or ugly unsafe prop that docs do not teach.
<Editable> public props:
renderElementrenderVoidrenderLeafrenderTextrenderSegmentplaceholderreadOnlyonKeyDownonDOMBeforeInputscrollSelectionIntoViewasdisableDefaultStyleslargeDocument if this stays as an explicit mount strategyCut from normal public <Editable>:
annotationStoresdecorationSourcesprojectionStoreeditorzeroWidthisInlineinputRulesisInline is schema, not an Editable render prop. inputRules is
product/plugin behavior; raw Slate React should provide the editor and DOM
event contracts, while Plate or extensions own opinionated input rules.
Add a normal app hook so users do not write this:
useEditorSelector((editor) => {
return editor.read((state) => state.marks.get()?.bold === true);
});
Preferred API:
const isBold = useEditorState((state) => state.marks.get()?.bold === true);
Options shape:
useEditorState(selector, {
deps: [query],
equalityFn: Object.is,
shouldUpdate: (change) => change.marksChanged,
});
Do not use a positional trailing deps array. It looks familiar, but it gets
messy once equality, deferred updates, and commit filtering exist.
Render behavior:
editor.readequalityFnshouldUpdate narrows which commits recomputeuseNodeSelector and
useElementSelectedKeep useEditorSelector as the low-level escape hatch. Teach useEditorState
in normal docs.
Remove:
docs/walkthroughs/08-using-the-bundled-source.mdSummary.md entry for that pageRewrite:
docs/walkthroughs/05-executing-commands.mdThe commands walkthrough should teach extracted commands with the current
read/update model, not the legacy CustomEditor object style as the main path.
It should stay progressive like legacy Slate, but the code should look like:
export const isBoldActive = (editor: Editor) =>
editor.read((state) => state.marks.get()?.bold === true);
export const toggleBold = (editor: Editor) => {
editor.update((tx) => {
tx.marks.toggle("bold");
});
};
Then the extension/plugin docs can show how product layers register richer commands. Raw Slate docs should not imply every app should mutate the editor object with a custom command namespace.
Recover useful detail from legacy docs/concepts/11-normalizing.md, but only
when it improves the current model:
Do not recover:
Transforms.* examples as the main patheditor.normalizeNode as the primary extension story| Change | Strongest objection | Answer | Evidence | Verdict |
|---|---|---|---|---|
| Cut instance schema predicates. | "Slate always had editor.isVoid; this makes extension code noisier." | Keep schema setup on editor.schema, but reads happen through state.schema / tx.schema. This removes a mutable-looking instance method family while preserving concise command code. | Current instance methods are in .tmp/slate-v2/packages/slate/src/interfaces/editor.ts:237-282; state schema already exists at .tmp/slate-v2/packages/slate/src/interfaces/editor.ts:179-188. | keep |
Cut public Editor.* editor-state queries. | "This drops a familiar Slate namespace." | Compatibility is not the goal. Pure namespaces stay public; editor-state reads move to state.* / tx.*; internal runtime helpers can exist without being exported. | Docs currently teach Editor.start / Editor.end in .tmp/slate-v2/docs/api/transforms.md:104-105; public static query helpers are listed throughout .tmp/slate-v2/docs/api/nodes/editor.md:101-234. | keep |
Cut toggleList and block-format helpers from core. | "Users need lists and headings." | They need primitives. Product formatting policy belongs in extensions/examples/Plate, not raw core. | DEFAULT_LIST_TYPES and toggleList live in .tmp/slate-v2/packages/slate/src/editor/block-format.ts:6-162. | keep |
Replace public onCommit with richer onChange context plus granular callbacks. | "Commit is the real runtime unit." | Yes, and advanced subscribers can use editor.subscribe. React app props should be Slate-friendly and ergonomic. | Current <Slate> only calls onChange(snapshot.children) when commit?.childrenChanged in .tmp/slate-v2/packages/slate-react/src/components/slate.tsx:104-110. | keep |
Cut duplicate projection props from Editable. | "Standalone EditableTextBlocks may need them." | Make that an internal/unsafe component contract. Public React API should have one provider owner for editor-level stores. | Duplicate props appear in SlateProps at .tmp/slate-v2/packages/slate-react/src/components/slate.tsx:37-45 and EditableTextBlocksProps at .tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx:252-282. | keep |
Cut inputRules from raw Editable. | "Input rules are convenient." | Convenience here is product policy. Plate/extensions can own it without bloating raw Slate React. | EditableDOMRootProps exposes inputRules at .tmp/slate-v2/packages/slate-react/src/components/editable.tsx:81-94. | keep |
Move operation replay under tx.operations.replay. | "Collaboration adapters need a direct replay entrypoint." | They need a deterministic entrypoint, not an editor-instance write escape hatch. editor.update(tx => tx.operations.replay(...)) preserves the boundary and still supports adapters. | Current docs and source expose applyOperations; previous plan already treats replay as special, but no-compat cleanup should not leave it as a public instance writer. | keep |
cd .tmp/slate-v2 && rg -n 'toggleList|toggleBlock|toggleAlignment|setBlock|withTransaction' packages/slate/src docs --glob '!docs/general/changelog.md'
cd .tmp/slate-v2 && rg -n 'Editor\\.(start|end|node|nodes|marks)|editor\\.(isInline|isVoid|markableVoid|isSelectable|isElementReadOnly|start|end|node|nodes|marks|applyOperations)' docs packages/slate/src/interfaces packages/slate/test
cd .tmp/slate-v2 && rg -n 'onCommit|onValueChange|onSelectionChange|onChange' packages/slate-react/src/components/slate.tsx docs/libraries/slate-react docs/walkthroughs
cd .tmp/slate-v2 && rg -n 'annotationStores|decorationSources|projectionStore|inputRules|isInline|zeroWidth' packages/slate-react/src/components docs/libraries/slate-react
The first two gates are fail-on-output for public docs and public interfaces after implementation, with internal implementation exceptions explicitly listed in the execution plan. The third gate must prove the final callback contract. The fourth gate must prove provider/editable prop ownership.
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.Editor.* helpers, opinionated block-format helpers,
withTransaction, <Slate> callback behavior, duplicated projection props,
Editable public props, current hook shape, commands walkthrough,
bundled-source walkthrough, current normalizing docs, and legacy normalizing
docs.sed over the active plan, slate-review skill, goal workflow skill,
completion file, continuation prompt, current/legacy docs, and relevant live
source snippets; targeted rg over .tmp/slate-v2/packages/slate/src,
.tmp/slate-v2/packages/slate-react/src, and .tmp/slate-v2/docs; bun run completion-check..tmp/slate-v2 still contains the hard-cut
surfaces because this lane is review-only. At this checkpoint,
projectionStore ownership was the only remaining closure decision; Pass 9
resolves it as runtime-owned/internal.Element / Node / Path / Range helpers
into editor state, deleting static Editor.*, keeping product list helpers
in core, retaining public onCommit, and documenting duplicate projection
props at both provider and editable levels.bun run completion-check fails intentionally
while the status is pending.docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.projectionStore decision.sed over the active plan, completion file, continuation prompt, and
relevant closure sections; targeted plan grep for Pass 8/9 hard-cut terms;
bun run completion-check..tmp/slate-v2 still contains the hard-cut
surfaces because this lane is review-only. The plan itself has no unresolved
public API maybe language.projectionStore as a normal public prop,
widening this review pass into implementation, and setting blocked when a
closure pass was runnable.User decision: compatibility does not matter for this unpublished rewrite. Therefore the plan should take the cleaner API, not the Slate-familiar compromise.
Accepted:
Editor.* editor-state query namespaceeditor.* query aliaseseditor.applyOperationsNode, Element, Text, Path, Point,
Range, and Operationeditor.read(state => ...)editor.update(tx => ...)state / tx namespaces, not editor instance
methodsRejected:
Editor.start / Editor.node for migration familiarityeditor.start / editor.node as convenience aliaseseditor.refs public proposal in this planCompletion status remains done because this amendment removes a compatibility compromise and tightens the already-accepted hard-cut direction.
.tmp/slate-v2/docs/Summary.md,
.tmp/slate-v2/docs/general/contributing.md,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.Transforms for both
concept and API pages; public Summary no longer lists Docs Proof Map;
contributor docs link the proof map.rg -n 'Docs Proof Map|Editor Methods' docs/Summary.md;
rg -n 'docs-proof-map|Docs Proof Map' docs/general/contributing.md docs/general/docs-proof-map.md;
focused sed reads over the touched docs..tmp/slate-v2/docs/concepts/04-transforms.md,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.editor.update,
tx.nodes, tx.text, tx.selection, tx.marks, and tx.operations;
operation replay is documented as tx.operations.replay(...); stale public
paths like Editor.*, withTransaction, and editor.applyOperations are
absent from the page.rg -n 'withTransaction|applyOperations|Editor\\.|editor\\.applyOperations|Inside .*editor\\.update|bridge layer|normal application code|primitive .*reference material' docs/concepts/04-transforms.md;
rg -n 'editor\\.update|tx\\.nodes|tx\\.text|tx\\.selection|tx\\.marks|tx\\.operations' docs/concepts/04-transforms.md;
focused sed read over the page.docs/api/transforms.md,
docs/api/nodes/editor.md, command walkthroughs, core APIs, React props,
and hook docs still need later phases.editor.applyOperations as a concept-page escape
hatch and teaching Editor.* query helpers for familiarity..tmp/slate-v2/docs/api/transforms.md,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.tx reference;
it documents tx.nodes, tx.text, tx.selection, tx.marks, and
tx.operations.replay(...); stale public paths like Editor.*,
withTransaction, and editor.applyOperations are absent.rg -n 'applyOperations|Editor\\.|editor\\.applyOperations|withTransaction|normal application code|bridge layer|primitive .*reference material' docs/api/transforms.md;
rg -n 'tx\\.nodes|tx\\.text|tx\\.selection|tx\\.marks|tx\\.operations|operations\\.replay' docs/api/transforms.md;
focused sed read over the page.docs/api/nodes/editor.md, command walkthroughs,
core APIs, React props, and hook docs still need later phases.editor.applyOperations and
documenting primitive bridge helpers as an advanced public reference section..tmp/slate-v2/docs/api/nodes/editor.md,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.editor.read, editor.update, editor.subscribe,
editor.extend, state.*, and tx.*; stale public paths like public
Editor.* queries, instance query aliases, withTransaction,
editor.applyOperations, and opinionated block/list helpers are absent.rg -n 'Editor\\.(start|end|node|nodes|marks)|editor\\.(start|end|node|nodes|marks|isInline|isVoid|markableVoid|isSelectable|isElementReadOnly|applyOperations)|withTransaction|toggleList|toggleBlock|toggleAlignment|setBlock|normal application code|bridge layer|primitive .*reference material' docs/api/nodes/editor.md;
rg -n 'editor\\.read|editor\\.update|editor\\.subscribe|editor\\.extend|state\\.|tx\\.' docs/api/nodes/editor.md;
focused sed read over the page.Node / Path / Range helpers into editor state..tmp/slate-v2/docs/walkthroughs/05-executing-commands.md,
.tmp/slate-v2/docs/walkthroughs/08-using-the-bundled-source.md,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.editor.read(...) and editor.update(...); it does not teach
CustomEditor, public Editor.*, Transforms.*, withTransaction, or
editor.applyOperations; bundled-source walkthrough has no docs references
and is removed.rg -n 'CustomEditor|Editor\\.|Transforms\\.|editor\\.applyOperations|withTransaction' docs/walkthroughs/05-executing-commands.md;
rg -n 'using-the-bundled-source|bundled source|Package Artifacts|08-using' docs/Summary.md docs/walkthroughs;
rg -n 'editor\\.read|editor\\.update|tx\\.marks|tx\\.nodes|tx\\.selection|tx\\.text|tx\\.operations|state\\.nodes|state\\.marks' docs/walkthroughs/05-executing-commands.md;
focused sed read over the page.CustomEditor namespace,
keeping package-artifact docs, and adding product commands to raw Slate..tmp/slate-v2/packages/slate/src/interfaces/editor.ts,
.tmp/slate-v2/packages/slate/src/create-editor.ts,
.tmp/slate-v2/packages/slate/src/core/public-state.ts,
.tmp/slate-v2/packages/slate/src/core/extension-registry.ts,
.tmp/slate-v2/packages/slate/src/core/leaf-lifecycle.ts,
.tmp/slate-v2/packages/slate/src/core/normalize-node.ts,
.tmp/slate-v2/packages/slate/src/editor/**,
.tmp/slate-v2/packages/slate/src/transforms-node/**,
.tmp/slate-v2/packages/slate/src/transforms-text/**,
.tmp/slate-v2/packages/slate/src/utils/**,
.tmp/slate-v2/packages/slate/test/**,
.tmp/slate-v2/docs/**,
docs/plans/2026-04-29-slate-v2-docs-maintainer-reorg-review-plan.md,
active goal state, and active goal state.Editor.* editor-state query helpers
are absent from the namespace, type surface, and editor barrel; instance
schema predicates and read aliases are absent from BaseEditor and runtime
editor instances; public editor.applyOperations is absent; operation replay
is available as tx.operations.replay(...); state.marks.get() preserves
the legacy mark-read behavior without restoring public Editor.marks.bun test ./packages/slate/test/public-field-hard-cut-contract.ts;
focused hard-cut contract suite over public fields, write boundary,
state/tx API, schema, extension, migration backbone, collaboration history,
apply/onChange, surface, and query contracts;
bun --filter slate typecheck;
bun test ./packages/slate/test;
stale API grep over packages/slate/src/interfaces,
packages/slate/src/create-editor.ts, packages/slate/test, and docs;
export grep over packages/slate/src/editor/index.ts and
packages/slate/src/interfaces/editor.ts.toggleList, setBlock, toggleBlock, toggleAlignment, public
withTransaction, and replay escape hatches outside tx.operations.replay.
Later phases still own React props/callbacks and hook DX..tmp/slate-v2/packages/slate/src/create-editor.ts,
.tmp/slate-v2/packages/slate/src/core/apply.ts,
.tmp/slate-v2/packages/slate/src/core/public-state.ts,
.tmp/slate-v2/packages/slate/src/editor/index.ts,
.tmp/slate-v2/packages/slate/src/editor/is-editor.ts,
.tmp/slate-v2/packages/slate/src/interfaces/editor.ts,
.tmp/slate-v2/packages/slate/src/interfaces/transforms/general.ts,
.tmp/slate-v2/packages/slate/src/editor/**,
.tmp/slate-v2/packages/slate/src/transforms-node/**,
.tmp/slate-v2/packages/slate/src/transforms-text/**,
.tmp/slate-v2/packages/slate/test/**, and
.tmp/slate-v2/docs/general/docs-proof-map.md.toggleList, setBlock, toggleBlock, or toggleAlignment; the block
formatting module is gone from the core editor barrel; public
Editor.withTransaction is gone; the runtime transaction helper is internal
as runEditorTransaction; operation replay is only under
tx.operations.replay(...).bun test ./packages/slate/test/public-field-hard-cut-contract.ts;
focused core contracts over transaction, accessors, range refs, target
runtime, query, write boundary, state/tx API, public field hard cuts, and
editor methods;
bun --filter slate typecheck;
bun test ./packages/slate/test;
fail-on-output grep for raw-core product helpers, public transaction helper
names, public replay escape hatches, stale public Editor.* state queries,
and stale editor instance aliases..tmp/slate-v2/packages/slate-react/src/components/slate.tsx,
.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx,
.tmp/slate-v2/packages/slate-react/src/index.ts,
.tmp/slate-v2/packages/slate-react/test/**,
.tmp/slate-v2/packages/slate-dom/test/**,
.tmp/slate-v2/docs/libraries/slate-react/slate.md,
.tmp/slate-v2/docs/libraries/slate-react/editable.md,
.tmp/slate-v2/docs/walkthroughs/01-installing-slate.md, and
.tmp/slate-v2/docs/walkthroughs/06-saving-to-a-database.md.<Slate onChange> fires for every commit
with a shared SlateChange object; onValueChange is document-only;
onSelectionChange is selection-only; public <Slate onCommit> and
public projectionStore props are gone; public Editable no longer accepts
editor-level projection, schema, input-rule, zero-width, or editor props;
tests render Editable under Slate.bun --filter slate-react typecheck;
bun --filter slate-dom typecheck;
focused React callback, projection, large-document, DOM-shape, surface,
generic, with-react, and slate-dom clipboard/bridge tests;
bun test ./packages/slate-react/test/*.tsx ./packages/slate-react/test/*.ts;
bun test ./packages/slate-dom/test/*.ts;
public prop/callback greps over packages/slate-react/src/components,
packages/slate-react/test, packages/slate-dom/test,
docs/libraries/slate-react, and docs/walkthroughs.store.projectionStore composition line
remains in Slate because the provider owns the runtime projection source.
Phase 9 still owns hook DX. Phase 10 still owns final docs gates.Editable editor as a convenience wrapper,
leaving saving docs on value-looking onChange, keeping onCommit as a
second React prop, and retaining direct Editable projection plumbing for
tests..tmp/slate-v2/packages/slate-react/src/hooks/use-editor-selector.tsx,
.tmp/slate-v2/packages/slate-react/src/index.ts,
.tmp/slate-v2/packages/slate-react/test/provider-hooks-contract.tsx,
.tmp/slate-v2/docs/libraries/slate-react/hooks.md, and
.tmp/slate-v2/docs/concepts/09-rendering.md.useEditorState(selector, options) runs
selectors inside editor.read; it supports result equality, deps,
deferred, and commit-scoped shouldUpdate; normal docs use
useEditorState for app-level toolbar reads; useEditorSelector remains
documented as a low-level hook for callers that intentionally need the editor
object or operation batch.bun --filter slate-react typecheck;
bun test ./packages/slate-react/test/provider-hooks-contract.tsx;
bun test ./packages/slate-react/test/*.tsx ./packages/slate-react/test/*.ts;
hook DX greps over packages/slate-react/src, docs/libraries/slate-react,
docs/concepts/09-rendering.md, and the provider hook contract tests.useEditorSelector example remains in the hooks
reference as the low-level operation-batch example by design. Phase 10 owns
final source-ledger, docs style, stale API, callback, and prop-ownership
gates.editor.read(...) inside hook selectors, and using a positional deps
array that cannot grow to equality and commit filtering..tmp/slate-v2, plus this plan, active goal state, and
active goal state.Editor Methods or Docs Proof Map; public docs
do not teach removed Editor.* state queries, mutable editor fields,
opinionated core helpers, public withTransaction, or public
editor.applyOperations; raw extension/collaboration docs still preserve
the substrate terms (state, tx, commits, operations, tags,
tx.operations.replay, local runtime ids); React docs expose the final
onChange / onValueChange / onSelectionChange callback contract;
Editable public docs and type surfaces do not expose provider-owned
projection, schema, input-rule, zero-width, or editor props; hook docs
teach useEditorState for normal app-level reads.bun lint:fix;
bun --filter slate-react typecheck;
bun --filter slate-dom typecheck;
bun test ./packages/slate-react/test/*.tsx ./packages/slate-react/test/*.ts;
bun test ./packages/slate-dom/test/*.ts;
fail-on-output greps for repeated warning prose, public proof-map/editor
method nav, stale field/method docs, adapter promises, stale public
Editor.*/core helper APIs, stale Editable public props, bundled-source
docs, and changelog/migration voice in touched docs;
positive gates for callback contract, provider-owned projection sources,
hook DX, and collaboration/plugin substrate terms.useEditorSelector
reference example, and internal projectionStore composition inside
Slate.bun check is green.bun check closeout.bun check in .tmp/slate-v2 passes after fixing slate-history
transaction replay, migrating the stale history fixture into
editor.update, adding the missing slate/internal source path for site
typecheck, and updating site examples to the hard-cut public API.bun --filter slate-history typecheck;
bun test ./packages/slate-history/test/*.ts;
bun typecheck:site;
bun lint:fix;
bun check.Added:
Summary.md, concepts/04-transforms.md,
api/transforms.md, and api/nodes/editor.md.Docs Proof Map from the public table of
contents while keeping it reachable for maintainers.Editor.* query helpers entirely and
moves operation replay under tx.operations.replay.Dropped:
toggleList, block/alignment formatting helpers, and public
withTransaction as acceptable raw core surface.onCommit as a normal React prop.inputRules, isInline, and projection store plumbing as normal
<Editable> props.projectionStore as normal <Slate> API.Editor.* editor-state query helpers as an acceptable compromise.editor.applyOperations as a replay escape hatch.Strengthened:
editor.read
internally and re-renders by selected result equality.state.* / tx.* for editor-state reads and keep pure
namespaces for pure data helpers only.Kept unchanged:
Element.*, Node.*, Path.*, Point.*,
Range.*, and Text.* stay as namespaces instead of moving into editor
state.Editor.* query namespace stays for compatibility.None. projectionStore is runtime-owned/internal. If an override survives for
tests or deep integrations, it must be visibly unsafe/internal and absent from
normal docs.
These are proposal phases only. Do not execute until the review plan closes and the user runs an execution lane.
| Phase | Owner | Output | Gate |
|---|---|---|---|
| 1. Nav and proof visibility | docs IA | Summary label cleanup; proof map unlisted or contributor-linked | Summary grep |
| 2. Write-model owner page | concept docs | concepts/04-transforms.md teaches write model once | repeated warning grep |
| 3. Transaction API reference | API docs | api/transforms.md is terse exact tx reference | source API grep |
| 4. Editor API triage | API docs | api/nodes/editor.md reorganized by reader intent; one advanced write note | repeated warning grep |
| 5. Commands/walkthrough polish | walkthrough/concepts | Commands guide reads like legacy Slate, with editor.read/update examples | stale API grep |
| 6. API hard-cut implementation | core/API docs | Remove public Editor.* editor-state queries, instance schema predicates, instance read aliases, and public editor.applyOperations; keep pure namespaces documented by intent | public-surface contract and stale API grep |
| 7. Unopinionated core cleanup | core | Remove toggleList, setBlock, toggleBlock, toggleAlignment, public withTransaction, and replay escape hatches outside tx.operations.replay from raw core public API | source/export grep and package tests |
| 8. React public contract cleanup | slate-react | Restore onChange as every committed editor change; add onValueChange / onSelectionChange; cut public onCommit; cut public projectionStore; move duplicate projection props out of Editable | React typecheck and callback contract tests |
| 9. Hook DX cleanup | slate-react docs/API | Add useEditorState for app-level coherent reads; keep useEditorSelector as low-level | hook tests and docs grep |
| 10. Final docs gates | review owner | source-ledger, style, stale API, callback, and prop-ownership greps | completion-check |
cd .tmp/slate-v2 && rg -n 'normal application code|bridge layer|Inside .*editor\\.update|primitive .*reference material' docs --glob '!docs/general/changelog.md'
cd .tmp/slate-v2 && rg -n 'Docs Proof Map|Editor Methods' docs/Summary.md
cd .tmp/slate-v2 && rg -n 'Editor\\.addMark\\(|Editor\\.removeMark\\(|editor\\.children|editor\\.selection|editor\\.operations|editor\\.marks|methods\\(' docs --glob '!docs/general/changelog.md'
cd .tmp/slate-v2 && rg -n 'editor\\.api|editor\\.tf|PlatePlugin|withYjs|slate-yjs|Y\\.Doc|yjs' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/api/transforms.md docs/api/nodes/editor.md
cd .tmp/slate-v2 && rg -n 'state|tx|operations\\.replay|commit|operations|tags|runtime ids are local|does not provide a full multiplayer recipe' docs/concepts/08-plugins.md docs/walkthroughs/07-enabling-collaborative-editing.md docs/general/docs-proof-map.md
cd .tmp/slate-v2 && rg -n 'Editor\\.(start|end|node|nodes|marks)|toggleList|toggleBlock|toggleAlignment|setBlock|withTransaction|editor\\.(isInline|isVoid|markableVoid|isSelectable|isElementReadOnly|start|end|node|nodes|marks|applyOperations)' packages/slate/src/interfaces packages/slate/src/create-editor.ts docs packages/slate/test
cd .tmp/slate-v2 && rg -n 'onCommit|onValueChange|onSelectionChange|onChange' packages/slate-react/src/components/slate.tsx docs/libraries/slate-react docs/walkthroughs
cd .tmp/slate-v2 && rg -n 'annotationStores|decorationSources|projectionStore|inputRules|isInline|zeroWidth' packages/slate-react/src/components/slate.tsx packages/slate-react/src/components/editable-text-blocks.tsx docs/libraries/slate-react
Final handoff grouped by surface:
Editor.start, Editor.node, editor.isVoid,
and editor.start; after state.* / tx.* for editor-state reads and pure
namespaces unchanged.toggleList / block helpers in raw Slate; after only
unopinionated primitives in core, product commands in extensions/Plate.withTransaction; after public
editor.update, tx.operations.replay, and private runtime transaction
helpers if needed.onChange plus onCommit; after
onChange(value, change), onValueChange, onSelectionChange, and
editor.subscribe for commit-level consumers.Slate and Editable;
after provider-owned decoration/annotation sources, runtime-owned projection
store, and mount/render/event-only Editable.editor.read inside useEditorSelector;
after useEditorState handles read/equality/filtering internally.>= 0.92.0.85.keep or resolved.active goal state says done.