docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md
Objective:
Complete the accepted Slate v2 command/behavior pipeline execution in
.tmp/slate-v2: harden event/input -> guard -> command/action -> tx mutation -> afterCommit effect, strict handled/forward middleware semantics, private
native-input command taxonomy, projected/content-root selection behavior,
paste/drop command semantics, and scenario-style regression coverage without
adding a public command/input API.
Goal plan: docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md
Template: docs/plans/templates/slate-plan.md
Primary template: docs/plans/templates/slate-plan.md
Applied packs:
Completion threshold:
bun check
and bun check:full are recorded from .tmp/slate-v2, final autoreview has
no accepted/actionable findings, this plan records current evidence and open
risks, and
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md
passes.Verification surface:
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md
from plate-2 at closure..tmp/slate-v2/packages/slate
and .tmp/slate-v2/packages/slate-react recorded in the source, API, runtime,
test, and verification rows below..tmp/slate-v2: focused slate,
slate-dom, and slate-react package tests; focused Playwright browser rows
for pagination/richtext/multi-root behavior; bun check; bun check:full;
focused retries-off reruns for the two flaky full-suite rows; and final
autoreview from the Slate v2 checkout.Constraints:
Editor.* command namespace, no
public editor.commands.on, no Editable onCommand, and no Plate product
command/profile surface in raw Slate.Editor.* namespace. Public examples must be
instance-first and extension-first.Boundaries:
.tmp/slate-v2 Slate v2 source/tests/docs plus this
plan ledger.Blocked condition:
Slate Plan lane state:
Accepted execution addendum:
slate-react native editing routes. The target is not
more API surface; it is consistency: every mutation/effect/interception path
must obey the same ownership rules before deeper command-taxonomy work..tmp/slate-v2 with source inspection and
focused tests around the smallest surface that proves or improves consistency.Current verdict:
bun check, bun check:full, focused retries-off reruns for full-suite
flakes, and clean final autorevieweditor.update/tx/afterCommit, keep
extension transforms and clipboard as current public substrate, cut public
Editor.* from the proposal, reject eager editor.commands.on as the default
public shape, and keep native-input commands as a private slate-react
runtime taxonomy plus scenario contracts. This plan does not add a public
command/input API.afterCommit, and internal EditableCommand
taxonomy. Execution keeps that architecture: selection, paste/drop, and
native insert-break scenarios route through private runtime contracts without
adding a public command namespace.Completion rule:
update_goal(status: complete) while any required checklist item
remains unchecked. If an item does not apply, check it and add N/A: <reason>.update_goal(status: complete) until every Slate Plan
completion gate below is satisfied and
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | slate-plan prompt loaded in user message; planning mode only |
| Active goal checked or created | yes | get_goal returned none; create_goal created this lane goal |
| Source of truth read before edits | yes | plan, research index/log, Portable Text source summary, Evidence Kit registry/health, live .tmp/slate-v2 source reads |
docs/solutions checked for non-trivial existing-code work | N/A | implementation was governed by the accepted Slate v2 plan and live .tmp/slate-v2 source/tests; no reusable docs/solutions artifact was needed |
Live .tmp/slate-v2 grounding needed for current-state claims | yes | public export, internal export, extension, command-registry, transform-middleware, update/afterCommit, editable command, clipboard reads recorded below |
Work Checklist:
Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | complete | Run the command, proof, source audit, or artifact check named in this plan | Closure pass records planning-readiness score 0.925, closes every scheduled pass, and runs node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md |
| Slate v2 source, runtime, browser, package, public API, or issue-fix claim | complete | Record live .tmp/slate-v2 command/proof or mark as planning-only with reason | Execution proof recorded from .tmp/slate-v2: focused package tests, browser rows, bun check, bun check:full, focused flake reruns, and final autoreview |
| Issue ledger or PR reference changed | complete for current pass | Current manual v2 sync ledger updated; PR description, coverage matrix, and fork dossier explicitly unchanged because no claim text changed | docs/slate-issues/gitcrawl-v2-sync-ledger.md 2026-05-29 planning sync |
| Autoreview for uncommitted implementation changes | complete | Load .agents/skills/autoreview/SKILL.md; run the helper from the git checkout that owns the implementation diff (.tmp/slate-v2 for Slate v2 patches) until no accepted/actionable findings, or record N/A for planning-only/no local patch | Final run from .tmp/slate-v2: /Users/zbeyens/git/plate-2/.agents/skills/autoreview/scripts/autoreview --mode local --engine claude --no-tools --no-web-search --prompt <intentional hard-cut context> -> clean, no accepted/actionable findings |
| Final user-review handoff | complete | Emit final handoff or keep the plan open with the next pass | Final response presents execution result, hard cuts, verification, flakes, and residual risks |
| Goal plan complete | yes | Run node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md | Closure command passes in this pass |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Current-state read and initial score | complete | live source reads in .tmp/slate-v2; research and Evidence Kit control-plane read; initial scorecard and provisional target rows filled | related issue discovery |
| Related issue discovery | complete | reused existing ledger/cache evidence: fork dossier sections for input policy, clipboard insertData, mobile/IME macro sync, PM-10/PM-09, public onCommand cut, and Plate-fit API hard cuts; read live/current ledger rows and test-candidate map clusters | issue-ledger pass |
| Issue-ledger pass | complete | added docs/slate-issues/gitcrawl-v2-sync-ledger.md 2026-05-29 planning sync; confirmed no Fixes, no Improves, no PR description change, no coverage matrix change, and no new fork dossier section | intent/boundary pass |
| Intent/boundary and decision brief | complete | intent, outcome, in-scope, non-goals, decision boundaries, unresolved user-decision points, principles, drivers, options, chosen direction, rejected alternatives, consequences, and follow-ups are filled | research refresh |
| Research, ecosystem strategy, live-source refresh | complete | synthesized Portable Text, Lexical, ProseMirror, Tiptap, React 19.2, Plate, slate-yjs, and local Slate v2 source against Option D; no research source forced a public command bus | pressure passes |
| Performance/DX/migration/regression/simplicity pressure passes | complete | pressure pass read Evidence Kit registry/health/current benchmark rows, huge-doc perf docs, command taxonomy tests, extension middleware tests, and browser command rows; Option D survived only as internal-first taxonomy plus scenario gates, not public API by default | objection ledger |
| Slate maintainer objection ledger | complete | answered maintainer objections for public Editor.*, default editor.commands.on, internal-first native-input taxonomy, clipboard handled-result adapter, Plate product boundary, slate-yjs operations/commit boundary, and scenario/benchmark gates | high-risk pass |
| High-risk deliberate mode | complete | killed public API drift again; kept internal-first taxonomy only with strict limits: no public command/input extension API, no clipboard break, no app-command funnel, no remote command replay, no Plate product surface, and no hot-path work without benchmarks | ecosystem maintainer pass |
| Ecosystem maintainer pass | complete | React runtime owner keeps command policy out of render/subscriptions; Slate core owner keeps public API instance/extension-first; slate-react owner keeps native input runtime-owned; Plate owner keeps product command/profile DX outside raw Slate; slate-yjs owner keeps operations/commits as replay truth; external comparators reinforce boundary discipline, not copied APIs | revision pass |
| Revision pass | complete | final wording now consistently says private slate-react runtime taxonomy plus existing public substrate; removed public-command-bus drift; execution phases now harden internals and scenario matrix without public API expansion | issue sync accounting |
| Issue sync accounting | complete | reused docs/slate-issues/gitcrawl-v2-sync-ledger.md 2026-05-29 planning sync; confirmed no new Fixes, no new Improves, no PR description claim changes, no issue coverage matrix changes, and no new fork dossier section for the revised final shape | closure score and final gates |
| Closure score and final gates | complete | closure planning score is 0.925; no dimension below 0.85; source/API claims are live-read grounded; runtime/browser behavior is verified by accepted execution evidence; final handoff prepared | final handoff |
Scorecard:
| Dimension | Weight | Score | Evidence |
|---|---|---|---|
| React 19.2 runtime performance | 0.20 | 0.86 | Positive: the plan adds no public command/input middleware on the urgent path; editor.update/afterCommit remain outside render and current runtime profiles afterCommit separately (.tmp/slate-v2/packages/slate/src/core/public-state.ts:2208, 2499, 4551). React 19.2 research supports external-store subscriptions and non-urgent surrounding UI. Current huge-doc typing/select/select-all red remains an execution/release gate, so this is a planning-readiness score, not a runtime performance claim. |
| Slate-close unopinionated DX | 0.20 | 0.94 | Public API stays instance/extension/type oriented, not public Editor.* commands (.tmp/slate-v2/packages/slate/src/index.ts:1). The plan keeps the authoring story on editor.update, tx, transforms, clipboard, and afterCommit, and rejects editor.commands.on, Editable onCommand, PT names, and Tiptap-style command catalogs in raw Slate. |
| Plate and slate-yjs migration backbone | 0.15 | 0.93 | Commit metadata, update tags, tx, state/tx groups, onCommit, operations, and bookmarks line up with Plate product command DX and slate-yjs extension-owned binding research. Plate owns product command/profile DX above Slate; slate-yjs observes operations/commits, not UI command names. |
| Regression-proof testing strategy | 0.20 | 0.94 | Portable Text scenario style, ProseMirror DOM bridge discipline, and existing Slate v2 editable command taxonomy converge on a concrete execution matrix: delete, paste/drop, IME/composition, selection movement, history, projected/content roots, and clipboard. The plan explicitly refuses behavior claims until those rows pass. |
| Research evidence completeness | 0.15 | 0.96 | Portable Text, Lexical, ProseMirror, Tiptap, React 19.2, Plate, slate-yjs, and live Slate v2 source were synthesized against this exact command proposal. Pressure, objection, high-risk, ecosystem, revision, and issue-sync passes all converge on private runtime taxonomy plus existing public substrate. |
| shadcn-style composability and minimalism | 0.10 | 0.93 | Product command discoverability, toolbar applicability, schema profiles, and shadcn UI stay in Plate. Raw Slate keeps the minimal substrate and avoids product UI/control APIs. |
Source-backed architecture north star:
slate-react native
event runtime may classify semantic commands internally, app code writes
through editor.update or Plate-owned product commands, mutations happen
through tx.*, and effects happen through afterCommit or extension
onCommit. Native input/paste/keyboard command vocabulary stays at the
runtime boundary, not a global imperative public command bus.defineEditorExtension, createEditor,
createEditorRuntime, createEditorView, isEditor, and types, but not
public defineCommand/registerCommand/executeCommand
(.tmp/slate-v2/packages/slate/src/index.ts:1). Those command functions are
internal exports (.tmp/slate-v2/packages/slate/src/internal/index.ts:1).
Public extension output already has clipboard, onCommit, operations,
queries, state, transforms, and tx
(.tmp/slate-v2/packages/slate/src/interfaces/editor.ts:1483).Editor.registerCommand; do not make
editor.commands.on the default API; do not reintroduce extension
commands blindly while the live extension validator explicitly rejects it as
legacy (.tmp/slate-v2/packages/slate/src/core/editor-extension.ts:197,
233).editor.update((tx) => ...).Public API target:
| Surface | Proposed shape | User-facing DX | Compatibility / migration | Evidence | Verdict |
|---|---|---|---|---|---|
| Core writes | Keep editor.update((tx, { afterCommit }) => { tx.text.insert('x'); afterCommit(...) }) as the public effect boundary | One write boundary, tx mutation is obvious, side effects are visibly post-commit | No migration needed for current v2 call sites | public-state.ts:2208, 2499, 4551 | keep |
Public Editor.* namespace | Do not add Editor.registerCommand, Editor.insertText, or Editor.commands to public docs/examples | Avoids old Slate static-namespace gravity and wrong examples | Internal Editor type/name remains; public authoring uses instance/update/extensions | index.ts:1; internal/index.ts:1 | cut |
Imperative editor.commands.on | Do not add this public shape | Looks easy but bypasses extension lifecycle, ordering, teardown, and React Strict Mode pressure | No migration path; not part of accepted public API | current registry is internal and extension lifecycle already owns slots | reject |
| Extension transform middleware | Keep as tx-level mutation middleware for transform-shaped work | Slate-close: transforms.insertText({ text, next, tx }) style already matches transform names | Existing extension authors use transforms; no rename in pass 1 | interfaces/editor.ts:928; editor-extension.ts:447; transform-middleware.ts:11 | keep |
| Semantic native input commands | Keep a stable internal taxonomy in slate-react; expose no public extension slot in this plan | Public docs stay on current Slate substrate while native event internals get one vocabulary | Current internal EditableCommand taxonomy is private runtime behavior, not a public contract | editable-command-types.ts; mutation-controller.ts:610; editing-kernel.ts search output; projected-command and browser command tests | keep internal |
| Clipboard/paste | Keep clipboard.insertData on the existing extension surface, but hard-cut command/middleware returns to strict boolean | Paste/drop stays on existing extension surface; true handles, false declines/falls through | Breaking return-shape cleanup accepted for v2; no { handled } object | interfaces/editor.ts:1446; clipboard-input-strategy.ts:51; phase 1 patch | keep strict boolean |
Internal runtime target:
| Layer | Current owner | Target mechanism | Avoids | Evidence | Verdict |
|---|---|---|---|---|---|
| Command registry | packages/slate/src/core/command-registry.ts | Stay internal; continue powering transform middleware and static compatibility paths | Public bus sprawl and out-of-band mutation | command-registry.ts:32, 36, 69 and internal/index.ts:1 | keep internal |
| Transform command bridge | packages/slate/src/core/transform-middleware.ts and extension registration | Keep deterministic next/default semantics; strengthen docs/tests around handled/forward behavior | Transform overrides that mutate DOM events or duplicate default logic | transform-middleware.ts:112; editor-extension.ts:456 | keep |
| Editable command taxonomy | packages/slate-react/src/editable/editable-command-types.ts, editing-kernel.ts, mutation-controller.ts | Keep stable internal taxonomy; normalize scenario matrix before execution closure | Native input, keydown, paste/drop, and history taking divergent code paths | mutation-controller.ts:610, 661, 668, 678, 699; projected-command-contract.test.ts; browser beforeinput rows | keep internal |
| Effect boundary | editor.update context and extension onCommit | Keep effects post-commit; forbid hidden side effects inside transforms except scheduling afterCommit | Transform middleware causing analytics/sync/UI effects before commit truth exists | public-state.ts:2217, 4560; interfaces/editor.ts:1473, 1491 | keep |
Hook / component / render DX target:
| Surface | Call-site shape | Composition rule | Performance rule | Evidence | Verdict |
|---|---|---|---|---|---|
| Raw Slate example | Show defineEditorExtension({ transforms: { insertText(...) {} } }) and editor.update first | No helper extraction unless reused | No render subscription for command policy | current public extension slots | keep |
| React editable input policy | Runtime-owned internal taxonomy only; no Editable onCommand and no public input-policy example | Event runtime owns native input; apps keep using current Slate substrate | Handler runs during event/update, not render/effect watcher | EditableDOMRootProps exposes no onCommand; mutation controller owns editable commands | keep internal |
| Product toolbar applicability | Keep in Plate/examples, not raw Slate core | Use schema/state selectors from extension state | Selector reads must be narrow | Portable Text toolbar selector evidence is product-DX only | defer to Plate |
Plate migration-backbone target:
| Pressure | Slate substrate target | Plate adaptation route | Non-goal | Evidence | Verdict |
|---|---|---|---|---|---|
| Rich behavior plugins | Extension-owned transforms plus stable internal native-input taxonomy; no raw Slate product command slot | Plate maps plugins to Slate extension slots and keeps product shortcuts/toolbars outside core | Do not preserve current Plate public APIs in raw Slate | extension state/tx/transforms/clipboard slots | revise |
| Toolbar allowed/applicable state | Schema/state selectors, not command bus state | Plate asks Slate state/schema and product profile selectors | Do not ship a Slate toolbar package | Portable Text selector evidence; current state groups | defer |
slate-yjs migration-backbone target:
| Pressure | Slate substrate target | Collaboration route | Non-goal | Evidence | Verdict |
|---|---|---|---|---|---|
| Deterministic command replay | Commands annotate commits through metadata/tags; operations remain the replay truth | yjs adapter observes operations/commits, not UI events | Do not make remote replay depend on native event names | EditorCommitCommand, update metadata, operations | keep |
| Effects | afterCommit/onCommit after deterministic commit | Remote send/sync runs after local commit is materialized | No hidden transform-side remote effects | public-state.ts:4551 | keep |
Intent / boundary record:
editor.update + tx.*; side effects stay afterCommit/onCommit; internal
command registry stays internal; extension transforms and clipboard remain
the existing public substrate; semantic native-input policy is private
slate-react runtime behavior in this plan.Editor.* commands; default editor.commands.on; public
Editable onCommand; Portable Text API names; Plate toolbar/schema/profile
product APIs; current Plate public API compatibility; slate-yjs adapter
support claims; issue closure; implementation changes during planning mode.tx.*, not event handlers or effects.afterCommit/onCommit, not hidden inside transforms.slate-react event runtime; core Slate
stays transaction and extension substrate.Decision brief:
Editor.*.commands and exposes command
registration only internally.Editor.registerCommand / Editor.commands.editor.commands.on(...) as a public middleware bus.transforms and clipboard, document them harder, and
leave native input taxonomy internal.slate-react native-input taxonomy plus scenario matrix.editor.update, tx, extension slots, and
afterCommit directly instead of hiding the real API behind helpers.Issue accounting:
| Issue / cluster | Claim category | Exact claim | Why | Proof route | V2 sync ledger | PR line |
|---|---|---|---|---|---|---|
| #5050 text insertion accept/reject | Related | No fix claim; input accept/reject belongs to beforeinput/input command policy, not a freeform handler | Same boundary as semantic insert-text guard/forward policy | insert-text scenario rows across collapsed/expanded selections | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #3568, #3586, #4681 native format / beforeinput | Related/materially-improved history, unchanged for this plan | No new claim; native format command classification constrains the public onCommand cut | These rows pressure command taxonomy and post-DOM-beforeinput mutation discipline | native-format and beforeinput scenario rows | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #4613 insertData extensibility | Existing improves claim unchanged | Existing improve stays scoped to typed insertData input ingress; no broader paste-rule/output-serializer claim | Clipboard/paste policy is part of this architecture surface | clipboard policy scenario rows plus docs/API audit if return shape changes | unchanged; 2026-05-29 manual sync section records no promotion | keep existing scoped improve only |
| #4569 insertData docs | Existing fix claim unchanged | Existing docs fix stays exact; this plan only reuses it as command-policy pressure | Clipboard docs may need PR reference sync only if public docs wording changes | docs proof if wording changes | unchanged; 2026-05-29 manual sync section records no promotion | keep existing fix line |
| #5233, #3486, #4806 clipboard fixed floor | Existing fix claims unchanged | Custom fragment key and selected inline void clipboard claims remain exact; this plan must not dilute them | Paste/drop command policy must preserve fixed clipboard behavior | clipboard regression rows | unchanged; 2026-05-29 manual sync section records no promotion | keep existing fix lines |
| #4888, #4802, #4104, #3926, #4623 drop/move/payload remap | Related/status unchanged | No new fixed/improved claim; paste/drop taxonomy must account for these rows | Drop/move behavior is adjacent to insertData and native command routing | paste/drop/drag scenario matrix | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #6022, #5989, #5984, #5931, #5603, #5669, #5883, #4400 and mobile/IME cluster | Related/status unchanged | No exact mobile/IME closure; raw-device/browser proof remains required | IME/composition rows are the highest-risk pressure on command-policy design | IME/composition scenario rows and raw-device proof only if claimed | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #3991, #3868, #5582, #5477, #4896, #4350, #4328, #5630 delete/selection cluster | Related/status unchanged | Delete and selection rows remain preserve-only until scenario proof exists | Delete/backspace/move/extend commands are part of the proposed taxonomy | delete and selection rebasing scenario matrix | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #2405 command-scoped normalization | Related | No closure claim; command-scoped normalization is architectural pressure only | Normalization/perf can benefit from command metadata but needs measured proof | benchmark/control-plane candidate plus normalization scenario | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #2694, #3304, #1024 native clipboard/event rows | Related/status unchanged | No fix claim; Shift-Delete, file drop, and MIME typing map to clipboard/native taxonomy | These rows pressure typed native command vocabulary and fallback ordering | clipboard/drop scenario rows | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
| #1770 operation composition / collaboration pressure | Related | No closure claim; command metadata/effects cannot become operation truth | Collab must replay operations/commits, not UI command names | commit metadata and collaboration proof after execution | unchanged; 2026-05-29 manual sync section records no promotion | related matrix only |
Issue-ledger sync status:
onCommand cut, and Plate-fit API hard cuts.onDOMBeforeInput,
IME composition, Android native input, clipboard fragment, drop-inside-void,
history undo, void selection, and native input parity candidate rows.docs/slate-issues/gitcrawl-v2-sync-ledger.md section
2026-05-29 Command Behavior Pipeline Architecture Planning Sync.Fixes, no Improves, and no issue promotion.Ecosystem strategy synthesis:
| System | Source | Mechanism | Avoids | Steal | Reject | Slate target | Verdict |
|---|---|---|---|---|---|---|---|
| Portable Text | docs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.md:91 | Behavior event/action chain with guard, execute/forward/raise/effect, explicit ordering, and browser behavior specs | Handler soup and untested behavior plugins | Boundary model, explicit handled/forward discipline, scenario-style behavior tests | PT value format, CMS categories, XState runtime, exact API names | Slate-native runtime taxonomy over editor.update, not a PT clone | supports internal-first Option D |
| Lexical | docs/research/sources/editor-architecture/lexical-read-update-extension-runtime.md | Read/update closures, prioritized command listeners inside update context, update tags, dirty transform scheduling, extension lifecycle | Commands as the normal public mutation API; dirty work hidden in React render | Read/update discipline, lifecycle tags, command handlers inside update, dirty scheduling, extension lifecycle pressure | Class nodes, $ helpers, public dispatch-command app API as Slate's main DX | Keep editor.update and internal command registry; use tags/metadata and dirty commits; do not expose a command bus as the normal API | supports Option D |
| ProseMirror | docs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.md | Transactions own doc/selection/mark/meta changes; view owns DOM input, observer, selection import/export, composition | App commands reading DOM selection directly or post-hoc normalizing browser behavior | Transaction metadata, selection mapping/bookmarks, one DOM bridge owner | Integer positions, schema-first model, plugin complexity, React as a wrapper around a PM-style view tree | Native input policy belongs to slate-react; mutation stays in tx; DOM import/export has one owner | supports Option D |
| Tiptap | docs/research/sources/editor-architecture/tiptap-extension-command-react-dx.md; Plate source reads | Extension-packaged commands, chainable command catalog, React selector/composable UI posture | Undiscoverable product commands and toolbar glue | Product command discoverability, extension packaging, selector posture | Raw Slate editor.commands/chain() as the main write API | Put polished command/profile DX in Plate; raw Slate exposes substrate and internal runtime taxonomy only | supports Plate boundary |
| React 19.2 | docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.md | useSyncExternalStore, transitions/deferred UI, Activity, Performance Tracks | Render/effect watchers mutating editor state or broad editor rerenders | External-store subscriptions, urgent editing path, non-urgent surrounding UI, profiler evidence | Treating React scheduler as the editor invalidation engine | Runtime taxonomy must run in event/update time and feed dirty commits to selectors; effects stay after commit | supports Option D with perf gates |
| Plate | docs/research/systems/editor-behavior-architecture.md; packages/media/src/react/placeholder/PlaceholderPlugin.tsx; apps/www/src/registry/components/editor/transforms.ts | Product plugins expose editor.api, editor.tf, plugin handlers, transforms, shortcuts, and profile-like behavior decisions | Raw Slate absorbing toolbar/profile/product commands | Behavior profiles, product command catalog, plugin-provided capabilities mapped onto Slate substrate | Current Plate public API compatibility as a raw Slate requirement | Plate owns product command DX and behavior profiles over Slate tx/state/native-input substrate | boundary confirmed |
| slate-yjs / Yjs | docs/research/sources/editor-architecture/yjs-collaboration-bindings.md; live Slate source reads | Extension-owned binding state, commit-driven local export, remote import through editor.update, relative positions/bookmarks, awareness external store | Commands as collaboration replay protocol or Yjs/provider objects in Slate values | onCommit, state/tx namespaces, commit metadata, relative-position/bookmark mapping, awareness external-store hooks | External withYjs(editor) wrapper mutation, overriding apply/onChange, provider policy in raw Slate | Commands can annotate local intent; operations/commits remain replay truth; Yjs package uses extension-owned state/tx | supports Option D |
| Local Slate v2 | live .tmp/slate-v2 source reads | Public read/update, transforms, clipboard, onCommit, state/tx extension slots; internal command registry; editable command taxonomy | Reintroducing rejected extension commands or public Editable onCommand | Current substrate: extension lifecycle, tx mutation, afterCommit effects, internal taxonomy, model-owned mutation controller | Treating current internal EditableCommand as public | Keep the existing substrate; native-input taxonomy stays private in this plan | source confirms internal-only Option D |
Research pass verdict:
Editor.* or default
editor.commands.on.transforms/clipboard
substrate, keep command registry internal, and pressure whether a semantic
native-input policy belongs in public API or only in stable internals.Ecosystem maintainer pass:
| Owner lens | Accepts | Rejects | Required proof / revision | Verdict |
|---|---|---|---|---|
| React runtime owner | Event/update-time internal taxonomy; afterCommit/onCommit effects; narrow external-store notifications | render/effect watchers, broad subscriptions, public command middleware on the urgent path | keep benchmark gates on typing/select/select-all; no public command/input extension API in this plan | accept internal-only |
| Slate core owner | instance-first public API, extension transforms/clipboard, tx/state groups, internal command registry | public Editor.*, default editor.commands.on, PT/Tiptap vocabulary, generic app-command framework | revision pass must remove any wording that implies command bus destiny | accept with wording cleanup |
| slate-react native-input owner | one runtime owner for beforeinput/input/keyboard/clipboard/drag/composition; internal EditableCommand taxonomy | Editable onCommand, app handlers reading DOM selection, public hooks that race repair/composition | scenario matrix before public API; browser ownership stays in runtime | accept internal-only |
| Plate adapter owner | Slate substrate plus Plate behavior profiles, product commands, toolbar applicability, shadcn UI | raw Slate toolbar/profile package, current Plate API compatibility as a raw Slate requirement | revision pass should show Plate adapts over substrate, not through raw Slate public command bus | accept boundary |
| slate-yjs owner | operations/commits/bookmarks/metadata as replay truth; command labels as local diagnostics only | remote replay by UI command name; Yjs/provider objects in raw Slate values | final plan must say commands never become collaboration protocol | accept boundary |
| Portable Text comparator | explicit handled/forward discipline and scenario specs | PT value format, XState runtime, CMS behavior categories, exact names | steal tests/boundaries, not API | accept selectively |
| Lexical comparator | read/update lifecycle, update tags/metadata, command handlers inside update | class nodes, $ helpers, public dispatch-command as normal Slate DX | keep editor.update as public write story | accept selectively |
| ProseMirror comparator | transaction ownership and single DOM bridge owner | PM plugin complexity, integer-position model, app commands reading DOM selection | keep DOM import/export centralized in slate-react | accept selectively |
| Tiptap comparator | product command discoverability and selector posture | raw Slate editor.commands/chain() | put command catalog in Plate, not raw Slate | accept selectively |
Ecosystem maintainer pass verdict:
editor.update, tx, transforms, clipboard,
state/tx, operations, queries, onCommit, and afterCommit.Revision pass final proposed shape:
editor.update, tx.*, afterCommit, extension
transforms, clipboard, state, tx, queries, operations, and
onCommit.slate-react owns a private native-input taxonomy for
beforeinput/input/keyboard/clipboard/drag/composition/history/selection
routing. It may use handled/forward semantics internally, but that taxonomy is
not documented as a public extension API.Editor.* command namespace, no default
editor.commands.on, no Editable onCommand, no raw Slate toolbar/profile
package, no Plate command catalog in raw Slate, and no remote collaboration
replay by UI command name.boolean. clipboard.insertData keeps its extension surface but no longer
accepts command-boundary void; no { handled } object exists in command
propagation.Pressure pass verdict:
Option D-internal-first. The plan
does not accept Option D-public yet.editor.update, tx, transforms, clipboard,
and afterCommit. Documenting native-input taxonomy as public API would
create two authoring stories and confuse skeptical maintainers. Keep the
taxonomy private.next behavior, transaction-local tx,
clipboard read-only middleware, paste insert-data, beforeinput target
ranges, native format command routing, and IME toolbar behavior. The gap is a
normalized release matrix across delete, paste/drop, IME, selection movement,
history, and projected/content roots.Editor.* stays cut; default editor.commands.on stays
rejected; Editable onCommand stays cut; semantic native-input policy remains
internal-first; command/middleware propagation is strict boolean.Legacy regression proof matrix:
| Regression class | Legacy behavior | Slate v2 target | Proof route | Owner | Status |
|---|---|---|---|---|---|
| Text insertion guard | Apps can block/modify input through handlers, often with beforeinput edge bugs | Internal insert-text taxonomy can block/forward without DOM mutation hacks | model-input, input-router, browser insertText scenarios | slate-react | execution-gated |
| Delete/backspace | Legacy delete semantics split across transforms, beforeinput, keydown, void/inline boundaries | Delete command taxonomy covers backward/forward/word/line/block/fragment and beforeinput duplication | delete matrix scenario tests | slate-react + slate | execution-gated |
| Paste/drop | Legacy insertData extensibility copies internals | Paste/drop command policy composes with clipboard middleware and tx fragment insert | paste/drop scenario matrix plus clipboard benchmark | slate + slate-react | execution-gated |
| IME/composition | Browser-specific beforeinput/composition order bugs | Command pipeline preserves composition owner and post-commit effects | IME/composition scenario rows and mobile proof where available | slate-react | execution-gated |
| Selection move/extend | Keyboard/native selection can diverge from model | Move/extend selection commands preserve projected roots and hidden/content roots | selection rebasing/browser rows | slate-react | execution-gated |
Browser stress / parity strategy:
| Surface | Scenario | Browser/device | Command or proof route | Expected signal | Status |
|---|---|---|---|---|---|
| beforeinput insert text | block/forward/replace insertText at collapsed and expanded selections | Chromium first, then WebKit/Firefox rows if execution changes runtime | focused editing-kernel/model-input/browser contract | one command, one commit, no duplicate DOM repair | execution-gated |
| destructive keys | Backspace/Delete/word/line/full-block across text, void, content root, synced root | Chromium plus mobile/device if IME row changes | delete scenario matrix | deterministic selection and history | execution-gated |
| paste/drop | paste plain/html/fragment, internal drag, external drop | Chromium first; add Firefox/WebKit for native gaps | clipboard scenario matrix + benchmark mapping | handled result stops default, forward preserves fallback | execution-gated |
| IME/composition | composition insert/delete/commit ordering | Chromium/WebKit; Android raw device only if claimed | composition scenario matrix | no pre-commit side effects, stable selection | execution-gated |
Verification workspace gate:
| Claim | Workspace | Command | Result | Owner |
|---|---|---|---|---|
| Current public exports and internal command registry location | .tmp/slate-v2 | source read: packages/slate/src/index.ts, packages/slate/src/internal/index.ts | read; no runtime command | pass 1 |
Current extension slots and legacy commands rejection | .tmp/slate-v2 | source read: interfaces/editor.ts, core/editor-extension.ts | read; no runtime command | pass 1 |
| Current handled/next/afterCommit substrate | .tmp/slate-v2 | source read: core/command-registry.ts, core/transform-middleware.ts, core/public-state.ts | read; no runtime command | pass 1 |
| Current native command taxonomy and clipboard routing | .tmp/slate-v2 | source read: slate-react/src/editable/* | read; no runtime command | pass 1 |
| Research/ecosystem synthesis against Option D | plate-2 + .tmp/slate-v2 | source reads: research docs, Plate plugin/transform examples, Slate editable/source files | complete; no runtime command | research pass |
| Pressure pass benchmark/test grounding | plate-2 + .tmp/slate-v2 | source reads: Evidence Kit latest results, huge-doc perf docs, projected-command tests, extension middleware tests, browser command rows | complete; no runtime command | pressure pass |
| Slate maintainer objection grounding | .tmp/slate-v2 | source reads: public/internal exports, extension legacy rejection, EditableDOMRootProps, command registry, command result, clipboard middleware, extension setup output, editable command taxonomy, mutation controller, afterCommit/onCommit | complete; no runtime command | objection pass |
| High-risk deliberate-mode grounding | plate-2 + .tmp/slate-v2 | source/result reads: benchmark health/latest huge-doc compare, runtime event engine, input router, transform middleware, clipboard strategy, public-state afterCommit, command registry, mutation controller | complete; no runtime command | high-risk pass |
| Ecosystem maintainer grounding | plate-2 + .tmp/slate-v2 | source/research reads: React 19.2 external-store research, Plate behavior profile research and transform/plugin source, Yjs collaboration research, Lexical/ProseMirror/Tiptap/Portable Text research, Slate v2 public/internal/runtime source already recorded | complete; no runtime command | ecosystem pass |
| Revision pass wording audit | plate-2 | plan search for delayed public API framing and source-backed row rewrites | complete; no runtime command | revision pass |
| Phase 1 implementation behavior | .tmp/slate-v2 | focused tests + bun check after accepted execution | complete for command/middleware result cleanup; broader browser/scenario behavior remains phase 2+ | execution mode |
Autoreview workspace gate:
| Reviewed patch owner | Cwd | Command | Result | Notes |
|---|---|---|---|---|
| N/A | N/A | N/A | N/A | Planning-only lane; autoreview skill loaded, but no Slate v2 implementation patch exists to review. |
Applicable implementation-skill review matrix:
| Lens | Applies | Status | Findings | Plan delta |
|---|---|---|---|---|
| vercel-react-best-practices | yes | complete for planning | Command policy touches React event/runtime boundaries | No render/effect mutation; no broad subscription; native policy runs in event/update time only |
| performance-oracle | yes | complete for planning | Command, normalization, paste, and selection paths are hot | Reject public policy until complexity, allocation, and p95 impact are benchmarked |
| performance | yes | complete for planning | Evidence Kit required artifacts are present, but huge-doc typing/select/select-all stay red | Treat benchmarks as a hard gate before release-quality performance claims |
| tdd | yes | complete for planning | Private runtime taxonomy needs behavior contracts | Scenario matrix is required before execution closure |
| shadcn | partial | complete for planning | Only examples/tooling controls, not core | Product command/profile UI stays Plate-owned |
| react-useeffect | yes | complete for planning | afterCommit and extension effects are in scope | Effects are post-commit external sync only; transforms do not hide effects |
High-risk deliberate-mode pre-mortem:
| Risk | Trigger | Failure mode | Mitigation | Proof | Status |
|---|---|---|---|---|---|
| Public command API sprawl | public API / extension substrate | editor.commands.on, Editor.registerCommand, or Editable onCommand becomes a second plugin system with lifecycle leaks | Keep all three cut; keep command registry internal; public docs use existing substrate only | source export audit and extension legacy rejection | mitigated for plan |
| Hidden hot-path overhead | native input / selection / huge document | internal taxonomy adds allocations, extra dispatch, broad selector fanout, or event-time work in typing/select paths | Taxonomy stays internal and must not add public middleware dispatch; benchmark gates before release claims | Evidence Kit health plus huge-doc legacy compare red rows | open risk, gated |
| Native input regressions | beforeinput, keydown, paste, composition, drag/drop | guard/handled layer blocks DOM default but misses repair, composition state, projected-root selection, or fallback browser behavior | Runtime event engine keeps ownership; no app-facing command handler; scenario matrix by input family | runtime-event-engine, input-router, clipboard strategy, projected command/browser tests | open risk, gated |
| Collaboration ambiguity | commit/operation behavior | remote replay depends on local UI command names instead of deterministic operations | command labels are metadata only; slate-yjs observes operations/commits and maps positions/bookmarks | update/afterCommit/onCommit source plus collab readiness benchmark candidate | mitigated for plan |
| Clipboard churn | paste/drop API alignment | half-compatibility keeps void holes and makes handled semantics ambiguous | hard-cut command/middleware returns to strict boolean; no { handled } object | clipboard middleware and paste strategy source plus phase 1 tests/typecheck | mitigated by hard cut |
| Plate-in-core pressure | product command/profile DX | raw Slate grows toolbar applicability, schema profiles, or shadcn command catalog | raw Slate owns substrate; Plate owns product DX and UI | extension output substrate and Plate boundary rows | mitigated for plan |
| Internal taxonomy fossilizes | private runtime names become de facto public through docs/examples/tests | private names leak into user-facing docs and constrain later design | do not document taxonomy as public; revision pass separates internal names from user-facing wording | public API target rows and final docs gate | open risk, gated |
| App-command funnel | general app actions route through native-input taxonomy | Slate becomes a generic command framework and duplicates direct editor.update | app commands stay direct editor.update or Plate-level product commands | decision brief and maintainer ledger | mitigated for plan |
High-risk deliberate-mode verdict:
slate-react runtime handles native
beforeinput/input/keyboard/clipboard/drag/composition routing. Apps do not get
a public command hook at the DOM boundary.boolean is the
command result contract, while richer local helper objects stay out of command
propagation.Slate maintainer objection ledger:
| Change | Objection | Tradeoff | Evidence | Migration/docs/proof answer | Verdict |
|---|---|---|---|---|---|
Public Editor.* command API | "This repeats old Slate static namespace gravity and exposes internals." | Static functions are familiar to legacy Slate users but blur public/internal ownership | public root exports types/instance/extension surfaces, while defineCommand/executeCommand/registerCommand are internal exports | Keep public docs instance-first: editor.update, tx, extension slots. No migration path needed because v2 public API should not document this surface. | cut |
Default editor.commands.on | "This is a second plugin system with worse lifecycle." | Imperative registration is convenient but creates ordering, cleanup, Strict Mode, and ownership questions | extension validation rejects commands; command registry supports cleanup/order internally, but is not public lifecycle | Do not add it. Existing extension slots remain the public interception story. | reject |
Public Editable onCommand | "This is handler soup at the DOM boundary." | Easy to explain, but app handlers would race beforeinput, selection repair, composition, paste/drop, and projected roots | EditableDOMRootProps exposes onDOMBeforeInput/onKeyDown, not onCommand; runtime event engine already owns native routing | Keep cut. App-level policies should be extensions or direct editor.update; native event ownership stays in slate-react runtime. | cut |
| Internal-first native-input taxonomy | "If it is important, why not public?" | Internal-only gives less extension author power in the first slice | EditableCommand already models insert text, delete, history, insert data, move/extend selection, select-all, marks, and blocks; mutation controller routes these through editor.update and model-owned helpers | Stabilize the taxonomy and scenario matrix as runtime proof, not public API. | keep internal |
| Clipboard handled-result alignment | "Do not make paste/drop special forever." | Strict booleans are less expressive than objects, but command propagation carries no extra fields today | phase 1 hard-cuts command registry, transform bridge, clipboard middleware, DOM clipboard, examples, and docs to strict boolean | true handles, false declines, next(command?) forwards. Richer result objects are reserved for non-command helpers with extra fields. | keep strict boolean |
| Plate product command/profile boundary | "Raw Slate should not punt all command DX to Plate." | Raw Slate still needs enough substrate for non-Plate apps | extension output already exposes state, tx, transforms, clipboard, queries, operations, and onCommit | Raw Slate owns the substrate and browser behavior contract. Plate owns toolbar applicability, schema profiles, product command catalog, and shadcn-style UI. | keep boundary |
| slate-yjs operations/commit boundary | "Commands could be useful for collaboration replay." | Command labels can improve diagnostics, but replaying UI intent is unstable across clients | update context, commit metadata, operations, onCommit, and afterCommit are the deterministic boundary; editable commands include browser-local intent | Commands may annotate commits. Collaboration adapters must replay operations/commits and map positions/bookmarks, not remote UI command names. | keep |
| Public input-policy naming | "input, behaviors, or commands are all loaded terms." | Naming too early creates API debt | current source explicitly rejects extension commands; PT names are useful concepts but foreign vocabulary | Do not name a public extension API in this plan. | cut |
| Scenario/benchmark gates | "This is too much process for a private taxonomy." | Slower execution iteration | current benchmark rows have typing/select/select-all red; existing tests cover ingredients but not a normalized release matrix | Browser input behavior is regression-prone; execution waits for matrix proof. | keep |
Slate maintainer objection pass verdict:
editor.update, tx, transforms, clipboard, state/tx extension
groups, operations, queries, onCommit, and afterCommit.true handles, false declines, and
next(command?) forwards; command propagation does not accept void or
{ handled }.Hard cuts and rejected alternatives:
| Option / API | Keep / cut / reject | Why | Migration cost | Evidence | Follow-up |
|---|---|---|---|---|---|
Public Editor.registerCommand | cut | Wrong public namespace; internal registry is implementation detail | none for v2 public API | internal/index.ts:1 | none |
Default editor.commands.on | reject | Imperative lifecycle and ordering are worse than extension-owned policy | none | extension setup already owns slots | none |
PT execute/forward/raise/effect names | reject exact names | Good model, foreign vocabulary | none | Portable Text research | keep rejected unless revision pass finds a Slate-native name |
| Strict boolean command returns | keep | One propagation contract beats object ceremony and ambiguous void | small v2 break accepted | EditorCommandResult, transform bridge, clipboard maps, phase 1 patch | complete |
Plan deltas from review:
Editor.* proposal.editor.commands.on as the default public shape.Editor.*, no default editor.commands.on, keep existing
transforms/clipboard, and keep native-input policy private in
slate-react.Editor.* or a default command bus.Editor.*, default editor.commands.on, and Editable onCommand stay cut;
native-input command taxonomy stays internal-first; command and clipboard
propagation use strict boolean; Plate and slate-yjs boundaries stay outside
raw Slate.Open questions and decision-changing evidence:
| Question | Why it matters | Evidence needed | Owner | Status |
|---|---|---|---|---|
| Should command middleware return object or boolean? | Object results are ceremony unless we carry extra fields; `boolean | void` is ambiguous | source audit and user decision | slate-plan |
| Which scenario families are mandatory before execution closure? | This is the real behavior-proof gate now that public API is cut | revision and closure pass | slate-plan | delete, paste/drop, IME, selection, history, projected/content roots, clipboard |
Implementation phases with owners:
| Phase | Owner | Scope | Entry criteria | Exit criteria | Verification |
|---|---|---|---|---|---|
| 0. API consistency sweep | execution mode | audit and align editor.update/read, tx.*, extension transforms, clipboard, queries, operations, state/tx groups, onCommit/afterCommit, slate-react editable event props, internal command registry, and EditableCommand ownership | accepted final plan | every public/internal surface is classified as mutation, effect, interception, query, operation, or private native-input runtime; inconsistencies become focused patches or explicit non-goals | source audit plus focused package tests for changed surfaces |
| 1. Internal command result cleanup | execution mode | hard-cut private command/middleware boundaries to strict boolean; no public Editor.*; no public command bus | phase 0 complete | every command/middleware boundary returns true, false, or next(...); no command-boundary void; no { handled } object; contract tests pass without public API expansion | .tmp/slate-v2 focused slate tests |
| 2. React internal input taxonomy hardening | execution mode | stable internal taxonomy and scenario matrix; no public input-policy slot | phase 1 complete | beforeinput/keydown/paste/drop/history policy works through scenarios without public API churn | focused slate-react tests + browser rows |
| 3. Clipboard alignment | execution mode | paste/drop handler result semantics and command-policy docs/tests | phase 2 complete | insertData docs/tests stay strict boolean and paste/drop scenarios pass | clipboard tests + benchmark mapping |
| 4. Scenario matrix gate | execution mode | delete/paste/drop/IME/selection/history scenario families | phases 1-3 complete | scenario rows pass and become release gate candidates | focused browser/integration rows |
Fast driver gates:
| Gate | Cwd | Command / artifact | Proves | Status |
|---|---|---|---|---|
| planning artifact check | plate-2 | node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md | final plan completion only | complete in closure pass |
| Slate v2 API surface read | .tmp/slate-v2 | source read recorded above | current public/internal command shape | complete pass 1 |
| Slate v2 behavior check | .tmp/slate-v2 | focused tests + bun check + bun check:full after accepted execution | runtime/API/browser behavior | complete: phases 0-4 passed focused tests, browser rows, bun check, bun check:full, focused flake reruns, and final autoreview |
Accepted execution ledger:
| Slice | Status | Patch | Verification | Notes |
|---|---|---|---|---|
| Phase 0 slice 1: command registry public/static API consistency | complete | Split EditorStaticApi from InternalEditorStaticApi in .tmp/slate-v2/packages/slate/src/interfaces/editor.ts; kept defineCommand/registerCommand internal while removing them from the public static API type; added public-surface regression coverage in .tmp/slate-v2/packages/slate/test/public-surface-contract.ts | bun test ./packages/slate/test/public-surface-contract.ts; bun test ./packages/slate/test/transaction-contract.ts; bun --filter ./packages/slate typecheck | The initial API sweep found command registry helpers were root-value-private but still typed as part of EditorStaticApi. The fix preserves internal kernel/test use through slate/internal and prevents the public static API type from advertising a command bus. |
| Phase 0 slice 2: data-transfer command boundary | complete | Changed .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts so applyModelOwnedDataTransferInput delegates to applyEditableCommand({ kind: 'insert-data' }) and returns the handled boolean; added focused coverage in .tmp/slate-v2/packages/slate-react/test/model-input-strategy-contract.test.ts | bunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts from .tmp/slate-v2/packages/slate-react; bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; bun --filter ./packages/slate-react typecheck | The sweep found a duplicate model-owned data-transfer update path beside the private editable command taxonomy. The helper now uses the same handled command route as beforeinput/drop/paste command classification. |
| Phase 0 slice 3: explicit delete-fragment target | complete | Changed .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts so delete-fragment commands honor command.selection for normal text ranges, not only full-block deletion; added focused coverage in .tmp/slate-v2/packages/slate-react/test/projected-command-contract.test.ts | bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; bunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts from .tmp/slate-v2/packages/slate-react; bun test ./packages/slate-react/test/editing-kernel-contract.ts; bunx vitest run --config ./vitest.config.mjs ./test/dom-coverage-native-bridge-contract.test.ts from .tmp/slate-v2/packages/slate-react; bun --filter ./packages/slate-react typecheck | The sweep found a command payload field that was only partly respected. Beforeinput/delete-by-cut/delete-by-drag can classify an explicit target; the executor now mutates that target instead of falling back to whatever selection happens to be current. |
| Phase 0 fast gate | complete | Formatting-only issue in .tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts fixed; phase 0 patches verified against the repo fast gate | bun check from .tmp/slate-v2 | bun check ran lint, package/site/root typecheck, Bun tests, slate-layout tests, and slate-react Vitest suite successfully. |
| Phase 1: strict boolean command result cleanup | complete | Hard-cut .tmp/slate-v2 command/middleware propagation to strict boolean: core command registry, transform bridge, clipboard middleware, DOM clipboard handler/runtime, slate-history handler, slate-react command adapters, examples, and docs no longer use command-boundary void or { handled }; added regression coverage for false decline forwarding in .tmp/slate-v2/packages/slate/test/transaction-contract.ts | bun test ./packages/slate/test/transaction-contract.ts ./packages/slate/test/extension-methods-contract.ts ./packages/slate/test/generic-extension-namespace-contract.ts ./packages/slate/test/public-surface-contract.ts; bun test ./packages/slate-dom/test/clipboard-boundary.ts ./packages/slate-dom/test/dom-coverage.ts; bunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts ./test/projected-command-contract.test.ts ./test/slate-runtime-provider-contract.test.tsx from .tmp/slate-v2/packages/slate-react; bun --filter ./packages/slate typecheck; bun --filter ./packages/slate-dom typecheck; bun --filter ./packages/slate-react typecheck; bun typecheck:site; bun typecheck:root; bun check | Final shape is true = handled/stop, false = decline/fall through, next(command?) = forward with optional override and return downstream boolean. The public command bus remains cut. |
| Phase 2 slice 1: projected/content-root move-selection command route | complete | Refactored .tmp/slate-v2/packages/slate-react/src/editable/content-root-navigation.ts so event selection extension and command-driven selection extension share one private action helper; .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts now executes move-selection for projected/content-root selections before root-local fallback; added projected command coverage for extending an existing projected selection and promoting a model selection at a content-root edge | bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts ./test/content-root-navigation-contract.test.ts; bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts ./test/content-root-navigation-contract.test.ts ./test/keyboard-input-strategy-contract.test.ts ./test/editing-kernel-contract.ts ./test/slate-runtime-provider-contract.test.tsx; bun --filter ./packages/slate-react typecheck; bun check | Fixes the private taxonomy hole where keydown could classify move-selection but applyEditableCommand returned false. No public command API added. Browser/pointer scenario rows remain the next phase 2 slice. |
| Phase 2 slice 2: browser selection and coordinate closure | complete | Added .tmp/slate-v2/packages/slate-react/src/editable/slate-string-coordinate-placement.ts document-offset mapping for split Slate strings; mapped explicit string offsets through full-document offsets in .tmp/slate-v2/packages/slate-react/src/editable/root-interaction-controller.ts; deferred cross-root focus with focusSlateEditableAfterEventFrame; hardened the multi-root browser click row to target the actual last body paragraph box | bunx vitest run --config ./vitest.config.mjs ./test/slate-string-coordinate-placement.test.ts ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/pagination.test.ts --project=chromium --grep "places selection at wrapped line start when clicking the left paragraph margin"; PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/richtext.test.ts --project=firefox --grep "exposes input intent for start insert, number insert, and delete"; PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/multi-root-document.test.ts --project=mobile --grep "moves body caret to the clicked end padding after another root was focused"; PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/multi-root-document.test.ts --project=chromium --grep "moves body caret to the clicked end padding after another root was focused" | Closes the browser-selection slice without adding public event APIs. Coordinate placement now treats split leaves as document offsets, and root focus no longer races pointer placement after switching roots. |
| Phase 3: paste/drop command semantics | complete | Hardened .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts: empty Slate fragments with text/plain fall back to text insertion, projected paste RangeRefs are released on throwing handlers, Slate HTML fragment detection uses parsed DOM attributes instead of loose text matching, and empty projected paste preserves the view selection so the next typed replacement can apply | bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; bunx vitest run --config ./vitest.config.mjs ./test/editing-epoch-kernel-contract.test.ts ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; bunx vitest run --config ./vitest.config.mjs ./test/surface-contract.test.tsx ./test/projected-command-contract.test.ts from .tmp/slate-v2/packages/slate-react; bun test ./packages/slate/test/transaction-contract.ts ./packages/slate/test/public-surface-contract.ts | Paste/drop stays on the existing extension substrate with strict booleans. The patch fixes real fallback and cleanup defects while keeping collapsed delete-fragment as the accepted handled no-op semantics. |
| Phase 4: scenario matrix and release closure | complete | Added insert-break epoch coverage in .tmp/slate-v2/packages/slate-react/test/editing-epoch-kernel-contract.test.ts; made .tmp/slate-v2/packages/slate-react/test/surface-contract.test.tsx release-aware for sibling pending changeset patch floors; expanded .tmp/slate-v2/.changeset/paged-right-margin-selection.md to cover strict boolean command middleware and internal command registration split; kept .tmp/slate-v2/packages/slate/src/core/command-registry.ts strict boolean dispatch explicit | bun check:full from .tmp/slate-v2 exited 0; full run reported 1169 passed, 353 skipped, and 2 flaky Chromium rows; focused retries-off reruns passed: PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/richtext.test.ts --project=chromium --grep "keeps model and DOM coherent after persistent native word-delete" and PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/pagination.test.ts --project=chromium --grep "keeps middle-document typing responsive in a 1000-page virtualized document"; final autoreview from .tmp/slate-v2 returned clean | Full closure passed without expanding the public API. The two full-suite flakes were not accepted as product regressions because both focused retries-off reruns passed. |
Phase 0 API classification:
| Surface | Classification | Public? | Owner | Phase 0 decision |
|---|---|---|---|---|
slate root exports | public package values/types | yes | Slate core | no command registry values; defineCommand added to the banned root helper set |
EditorStaticApi | static compatibility API type | source-internal, not root-public | Slate core | command helpers moved to InternalEditorStaticApi; public static shape no longer advertises command middleware |
slate/internal command registry | private interception/middleware infrastructure | internal subpath only | Slate core/runtime packages | keep; supports transform middleware and internal tests only |
editor.update and tx.* | mutation boundary | yes | Slate core | keep as the only public write story |
editor.read and extension queries | query boundary | yes | Slate core | keep read-only; no mutation API added |
extension transforms | public mutation interception | yes | Slate core | keep transform-shaped middleware; no generic command bus |
extension clipboard.insertData | public paste/drop interception | yes | Slate core | phase 1 hard-cuts it to strict boolean with next() for forward; model-owned native data transfer delegates through private insert-data command |
extension operations | low-level operation interception | yes | Slate core | keep as operation-layer substrate, not user command taxonomy |
extension state and tx groups | public state/mutation extension groups | yes | Slate core | keep; product profiles belong in Plate |
afterCommit and extension onCommit | post-commit effects | yes | Slate core | keep effects post-commit; no transform-hidden effects added |
Editable event props (onDOMBeforeInput, onKeyDown, paste/drop/cut/copy/input handlers) | app event hooks and guards | yes | slate-react | keep current hooks; no onCommand and no public input-policy slot |
EditableCommand and applyEditableCommand | private native-input taxonomy and executor | no | slate-react | keep internal; fixed data-transfer routing and explicit delete target handling |
| selection/caret/DOM repair engines | private model-DOM synchronization | no | slate-react | keep outside command taxonomy; direct updates are repair/sync, not public mutation API |
| composition state | private IME state machine | no | slate-react | keep as native runtime policy; scenario phase owns deeper behavior proof |
| drag/cut cleanup routes | private compound native editing routes | no | slate-react | keep targeted cleanup local where it repairs drag/cut side effects; promote only if phase 2/3 scenario tests expose duplicated command semantics |
Phase 1 command result decision:
boolean.true means handled/stop; false means decline and continue with the same
command; next(command?) forwards with an optional command override and
returns the downstream boolean.void: missing returns should be type errors.{ handled } object: it is only justified if a boundary carries extra
fields such as repair/preventDefault/selectionPolicy/reason. Current command
propagation does not, so the object is ceremony.Final execution handoff outline:
editor.update/tx/afterCommit; keep public
extension transforms, clipboard, state, tx, queries, operations,
and onCommit; keep native-input taxonomy private to slate-react.Editor.*, no default editor.commands.on, no
Editable onCommand, no raw Slate toolbar/profile package, no Plate command
catalog in raw Slate, and no remote collaboration replay by UI command name.Fixes, no new Improves, no PR
description claim changes, no issue coverage matrix changes, and no fork
dossier update..tmp/slate-v2 focused package/browser tests passed, bun check
passed, bun check:full exited 0, focused retries-off reruns passed for both
full-suite flakes, and autoreview returned clean.Final completion gates:
| Gate | Required evidence | Status |
|---|---|---|
| score >= 0.92 and no dimension below 0.85 | scorecard rows cite evidence | complete: planning score 0.925; lowest dimension 0.86 |
| all pass rows complete or skipped with evidence | phase/pass table closed | complete |
| issue/reference sync closed | issue-ledger sync status closed | complete: 2026-05-29 manual v2 sync records no claim/reference changes |
| live source grounding complete | source-backed rows cite current owners | complete: final closure re-read public/internal exports, extension slots/rejections, editable taxonomy, afterCommit, command result, and benchmark timestamps |
| workspace verification recorded | verification workspace gate closed | complete |
| autoreview clean or N/A | .agents/skills/autoreview/SKILL.md loaded and clean from the git checkout that owns non-trivial uncommitted implementation changes (.tmp/slate-v2 for Slate v2 patches), or N/A with reason | complete: final .tmp/slate-v2 autoreview clean, no accepted/actionable findings |
| final handoff emitted or lane remains open | final response / next pass recorded | complete: final response emits execution handoff; next_pass: none |
check-complete passes | node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md | complete in closure pass |
Findings:
slate exports do not expose command registration functions;
public API is instance/extension/type oriented.next, and a
strict boolean result.commands is explicitly rejected as legacy in live source, so adding
it back would be a real API change and needs a better name or a hard reversal.transforms is the current public extension-owned command-like surface.clipboard.insertData now uses strict boolean handled semantics for the
command/middleware boundary.afterCommit is already the right effect boundary and should be kept.Fixes or
Improves claims. The correct sync artifact is the manual v2 ledger section,
not PR-body or coverage-matrix churn.next, transaction-local tx, clipboard read-only middleware,
paste insert-data, beforeinput target ranges, native format command routing,
and IME toolbar behavior. They are not yet normalized into a release matrix.Decisions and tradeoffs:
Editor command registry internal.editor.commands.on as the default public
API.editor.commands.on, but it is
much cleaner for extension lifecycle, React Strict Mode, and raw Slate
unopinionated scope.transforms/clipboard as the present public substrate, and keep semantic
native-input policy internal.editor.commands/chain, or Portable Text behavior names into
raw Slate. Steal the boundary discipline, not the public vocabulary.onCommand in
disguise?" is "no, because it is not public."Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| None yet | 0 |
External/browser findings:
Timeline:
.tmp/slate-v2 public exports, internal command registry, extension slots,
transform middleware, update/afterCommit boundary, editable command taxonomy,
Portable Text behavior research, and Evidence Kit health.docs/slate-issues/gitcrawl-v2-sync-ledger.md
and left PR description, issue coverage matrix, and fork dossier unchanged..tmp/slate-v2: command registry
helpers were moved out of EditorStaticApi into InternalEditorStaticApi;
public-surface contract coverage was added; focused slate contract tests and
package typecheck passed..tmp/slate-v2: model-owned
data-transfer input now delegates to applyEditableCommand and returns the
handled result; focused slate-react model-input/projected-command tests and
package typecheck passed..tmp/slate-v2:
delete-fragment commands now honor explicit model selections for normal
text ranges; focused projected-command, model-input, editing-kernel,
DOM-coverage native bridge, and slate-react typecheck passed..tmp/slate-v2; bun check
passed after formatting the touched test import and an existing
editing-kernel.ts indentation issue reported by Biome..tmp/slate-v2: command/middleware
propagation is strict boolean across core command registry, transform bridge,
clipboard middleware, DOM clipboard, slate-history, slate-react adapters,
examples, and docs. Focused slate/slate-dom/slate-react tests, package/site/root
typechecks, and bun check passed..tmp/slate-v2:
move-selection now executes through the private command route for
projected/content-root selections. Focused projected-command,
content-root-navigation, keyboard-input-strategy, editing-kernel, runtime
provider tests, slate-react typecheck, and bun check passed..tmp/slate-v2:
split-leaf string coordinate placement now resolves document offsets,
root-switch pointer focus is deferred through the event frame, the multi-root
browser click row targets the real paragraph box, and focused pagination,
richtext Firefox, multi-root mobile, and multi-root Chromium rows passed..tmp/slate-v2:
empty Slate fragments fall back to plain text when appropriate, projected
RangeRefs are released on handler throws, HTML fragment detection is DOM
parsed instead of text-matched, and empty projected paste preserves view
selection for the next typed replacement..tmp/slate-v2: insert-break epoch duplicate suppression, release-aware
peer floors, strict boolean command dispatch clarity, and changeset coverage
were verified by focused package tests.bun check:full from .tmp/slate-v2 exited 0 with 1169 passed,
353 skipped, and 2 flaky Chromium rows; both flaky rows passed focused
retries-off reruns..tmp/slate-v2 completed clean with no
accepted/actionable findings.Verification evidence:
.tmp/slate-v2/packages/slate/src/index.ts.tmp/slate-v2/packages/slate/src/internal/index.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate/src/core/transform-middleware.ts.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate-react/src/editable/*
No implementation command was run because this activation is planning-only.docs/slate-issues/gitcrawl-live-open-ledger.mddocs/slate-issues/gitcrawl-v2-sync-ledger.mddocs/slate-v2/ledgers/fork-issue-dossier.mddocs/slate-v2/ledgers/issue-coverage-matrix.mddocs/slate-v2/references/pr-description.mddocs/slate-issues/test-candidate-map/*.md
No implementation command was run because this activation is planning-only.docs/slate-issues/gitcrawl-v2-sync-ledger.mddocs/slate-v2/ledgers/issue-coverage-matrix.mddocs/slate-v2/references/pr-description.mddocs/slate-v2/ledgers/fork-issue-dossier.md
The only changed issue artifact is the manual v2 sync ledger; other reference
artifacts are explicitly unchanged because no fixed/improved claim changed.docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md.tmp/slate-v2/packages/slate/src/index.ts.tmp/slate-v2/packages/slate/src/internal/index.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/editable/editable-command-types.ts
No implementation command was run because this activation is planning-only.docs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.mddocs/research/sources/editor-architecture/lexical-read-update-extension-runtime.mddocs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.mddocs/research/sources/editor-architecture/tiptap-extension-command-react-dx.mddocs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.mddocs/research/sources/editor-architecture/yjs-collaboration-bindings.mddocs/research/sources/editor-architecture/slate-v2-local-proof-substrate.mddocs/research/systems/editor-behavior-architecture.mddocs/analysis/slate-v2-plate-v2-architecture-research.mdpackages/media/src/react/placeholder/PlaceholderPlugin.tsxapps/www/src/registry/components/editor/transforms.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate-react/src/editable/editable-command-types.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts
No implementation command was run because this activation is planning-only.benchmarks/editor/research/benchmark-registry.jsonbenchmarks/editor/benchmarks/results/benchmark-health-latest.jsonbenchmarks/editor/benchmarks/results/rich-text-editors-latest.jsonbenchmarks/editor/benchmarks/results/slate-v2-legacy-latest.jsonbenchmarks/editor/benchmarks/results/package-boundary-gates-latest.jsondocs/slate-v2/replacement-gates-scoreboard.mddocs/slate-v2/slate-react-perf-loop-context.md.tmp/slate-v2/packages/slate-react/test/projected-command-contract.test.ts.tmp/slate-v2/packages/slate/test/extension-methods-contract.ts.tmp/slate-v2/playwright/integration/examples/plaintext.test.ts.tmp/slate-v2/playwright/integration/examples/hovering-toolbar.test.ts
No implementation command was run because this activation is planning-only..tmp/slate-v2/packages/slate/src/index.ts.tmp/slate-v2/packages/slate/src/internal/index.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/editable/editable-command-types.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts
No implementation command was run because this activation is planning-only.benchmarks/editor/benchmarks/results/benchmark-health-latest.jsonbenchmarks/editor/benchmarks/results/slate-v2-legacy-latest.jsonbenchmarks/editor/benchmarks/results/rich-text-editors-latest.json.tmp/slate-v2/packages/slate-react/src/editable/runtime-event-engine.ts.tmp/slate-v2/packages/slate-react/src/editable/input-router.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts.tmp/slate-v2/packages/slate/src/core/transform-middleware.ts.tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts
No implementation command was run because this activation is planning-only.docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.mddocs/research/systems/editor-behavior-architecture.mdpackages/media/src/react/placeholder/PlaceholderPlugin.tsxapps/www/src/registry/components/editor/transforms.tsdocs/research/sources/editor-architecture/yjs-collaboration-bindings.mddocs/research/sources/editor-architecture/lexical-read-update-extension-runtime.mddocs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.mddocs/research/sources/editor-architecture/tiptap-extension-command-react-dx.mddocs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.md
No implementation command was run because this activation is planning-only.docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.mddocs/slate-issues/gitcrawl-v2-sync-ledger.md
No implementation command was run because this activation is planning-only.docs/slate-issues/gitcrawl-v2-sync-ledger.mddocs/slate-v2/references/pr-description.mddocs/slate-v2/ledgers/issue-coverage-matrix.mddocs/slate-v2/ledgers/fork-issue-dossier.md
No implementation command was run because this activation is planning-only..agents/skills/autoreview/SKILL.md.tmp/slate-v2/packages/slate/src/index.ts.tmp/slate-v2/packages/slate/src/internal/index.ts.tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate/src/core/public-state.ts.tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate-react/src/editable/editable-command-types.ts.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.tsbenchmarks/editor/benchmarks/results/benchmark-health-latest.jsonbenchmarks/editor/benchmarks/results/slate-v2-legacy-latest.jsonbenchmarks/editor/benchmarks/results/rich-text-editors-latest.json
No implementation command was run because this activation is planning-only..tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/test/public-surface-contract.tsbun test ./packages/slate/test/public-surface-contract.tsbun test ./packages/slate/test/transaction-contract.tsbun --filter ./packages/slate typecheck
This proved the first API consistency patch only..tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/test/model-input-strategy-contract.test.tsbunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbun --filter ./packages/slate-react typecheck
This proved data-transfer command routing only..tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/test/projected-command-contract.test.tsbunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbun test ./packages/slate-react/test/editing-kernel-contract.tsbunx vitest run --config ./vitest.config.mjs ./test/dom-coverage-native-bridge-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbun --filter ./packages/slate-react typecheck
This proved explicit delete-fragment targets only.bun check from .tmp/slate-v2bun test:bun, slate-layout tests, and slate-react Vitest tests.
Phase 0 API consistency sweep is complete..tmp/slate-v2/packages/slate/src/interfaces/editor.ts.tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate/src/core/editor-extension.ts.tmp/slate-v2/packages/slate/src/core/transform-middleware.ts.tmp/slate-v2/packages/slate-dom/src/plugin/dom-editor.ts.tmp/slate-v2/packages/slate-dom/src/plugin/dom-clipboard-runtime.ts.tmp/slate-v2/packages/slate-history/src/history-extension.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate/test/transaction-contract.ts.tmp/slate-v2/packages/slate-react/test/model-input-strategy-contract.test.tsrg -n "EditorCommandResult = \{|return \{ handled|\{ handled: false \}|\{ handled: true \}|EditorCommandResult \| void" packages/slate/src packages/slate/test packages/slate-dom/src packages/slate-dom/test packages/slate-history/src site/examples/ts docs/concepts docs/walkthroughs docs/libraries/slate-react
from .tmp/slate-v2bun test ./packages/slate/test/transaction-contract.ts ./packages/slate/test/extension-methods-contract.ts ./packages/slate/test/generic-extension-namespace-contract.ts ./packages/slate/test/public-surface-contract.tsbun test ./packages/slate-dom/test/clipboard-boundary.ts ./packages/slate-dom/test/dom-coverage.tsbunx vitest run --config ./vitest.config.mjs ./test/model-input-strategy-contract.test.ts ./test/projected-command-contract.test.ts ./test/slate-runtime-provider-contract.test.tsx
from .tmp/slate-v2/packages/slate-reactbun --filter ./packages/slate typecheckbun --filter ./packages/slate-dom typecheckbun --filter ./packages/slate-react typecheckbun typecheck:sitebun typecheck:rootbun check from .tmp/slate-v2
Result: pass. Phase 1 internal command result cleanup is complete..tmp/slate-v2/packages/slate-react/src/editable/content-root-navigation.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/test/projected-command-contract.test.tsbunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts ./test/content-root-navigation-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts ./test/content-root-navigation-contract.test.ts ./test/keyboard-input-strategy-contract.test.ts ./test/editing-kernel-contract.ts ./test/slate-runtime-provider-contract.test.tsx
from .tmp/slate-v2/packages/slate-reactbun --filter ./packages/slate-react typecheckbun check from .tmp/slate-v2
Result: pass. Phase 2 projected/content-root move-selection command route
slice is complete..tmp/slate-v2/packages/slate-react/src/editable/slate-string-coordinate-placement.ts.tmp/slate-v2/packages/slate-react/src/editable/root-interaction-controller.ts.tmp/slate-v2/playwright/integration/examples/multi-root-document.test.tsbunx vitest run --config ./vitest.config.mjs ./test/slate-string-coordinate-placement.test.ts ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-reactPLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/pagination.test.ts --project=chromium --grep "places selection at wrapped line start when clicking the left paragraph margin"
from .tmp/slate-v2PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/richtext.test.ts --project=firefox --grep "exposes input intent for start insert, number insert, and delete"
from .tmp/slate-v2PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/multi-root-document.test.ts --project=mobile --grep "moves body caret to the clicked end padding after another root was focused"
from .tmp/slate-v2PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/multi-root-document.test.ts --project=chromium --grep "moves body caret to the clicked end padding after another root was focused"
from .tmp/slate-v2
Result: pass. Phase 2 browser selection and coordinate placement closure is
complete..tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/test/projected-command-contract.test.ts.tmp/slate-v2/packages/slate-react/test/editing-epoch-kernel-contract.test.ts.tmp/slate-v2/packages/slate-react/test/surface-contract.test.tsxbun test ./packages/slate/test/transaction-contract.ts ./packages/slate/test/public-surface-contract.ts
from .tmp/slate-v2bunx vitest run --config ./vitest.config.mjs ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbunx vitest run --config ./vitest.config.mjs ./test/editing-epoch-kernel-contract.test.ts ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-reactbunx vitest run --config ./vitest.config.mjs ./test/surface-contract.test.tsx ./test/projected-command-contract.test.ts
from .tmp/slate-v2/packages/slate-react
Result: pass. Empty fragment fallback, projected RangeRef release,
structured HTML fragment parsing, and empty projected paste selection behavior
are covered..tmp/slate-v2/packages/slate/src/core/command-registry.ts.tmp/slate-v2/packages/slate-react/package.json.tmp/slate-v2/packages/slate-react/test/surface-contract.test.tsx.tmp/slate-v2/packages/slate-react/test/editing-epoch-kernel-contract.test.ts.tmp/slate-v2/.changeset/paged-right-margin-selection.mdbun check:full from .tmp/slate-v2 exited 0; summary recorded 1169
passed, 353 skipped, and 2 flaky Chromium rows.PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/richtext.test.ts --project=chromium --grep "keeps model and DOM coherent after persistent native word-delete"
from .tmp/slate-v2 -> pass.PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/pagination.test.ts --project=chromium --grep "keeps middle-document typing responsive in a 1000-page virtualized document"
from .tmp/slate-v2 -> pass./Users/zbeyens/git/plate-2/.agents/skills/autoreview/scripts/autoreview --mode local --engine claude --no-tools --no-web-search --prompt <intentional hard-cut context>
from .tmp/slate-v2 -> clean, no accepted/actionable findings.
Result: pass. Closure verification is complete.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Accepted execution closure is complete |
| Where am I going? | Final handoff and goal close |
| What is the goal? | Execute the accepted Slate v2 command behavior pipeline plan without expanding public command/input API |
| What have I learned? | Strict boolean command propagation, private native-input taxonomy, and projected selection behavior can be hardened without a public command/input API |
| What have I done? | Completed phases 0-4, focused tests, browser rows, bun check, bun check:full, focused flake reruns, final autoreview, and plan closure evidence |
Open risks:
bun check:full exited 0 but reported two flaky Chromium rows; both passed
focused retries-off reruns, so they are tracked as residual flake risk rather
than accepted product failures.