Back to Plate

Slate v2 command behavior pipeline architecture

docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md

53.0.8114.5 KB
Original Source

Slate v2 command behavior pipeline architecture

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:

  • none

Completion threshold:

  • Execution is done only when all accepted implementation phases are complete: API consistency sweep, strict boolean command cleanup, projected/content-root selection hardening, paste/drop command semantics, scenario-matrix coverage, closure verification, and final autoreview.
  • Closure is legal only when focused package/browser tests pass, 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:

  • Planning artifact check: 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.
  • Slate v2 API/source grounding: live reads from .tmp/slate-v2/packages/slate and .tmp/slate-v2/packages/slate-react recorded in the source, API, runtime, test, and verification rows below.
  • Implementation proof is recorded from .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:

  • Keep raw Slate unopinionated: no public Editor.* command namespace, no public editor.commands.on, no Editable onCommand, and no Plate product command/profile surface in raw Slate.
  • Keep raw Slate unopinionated. Plate owns product toolbars, schema profiles, and polished authoring kits.
  • Do not invent a public Editor.* namespace. Public examples must be instance-first and extension-first.
  • Do not copy Portable Text API names unless a later pass proves they beat Slate-native wording.

Boundaries:

  • Allowed execution scope: .tmp/slate-v2 Slate v2 source/tests/docs plus this plan ledger.
  • Issue/reference discovery remains ledger-first. No live GitHub sweep was needed for this execution slice.

Blocked condition:

  • Do not use blocked while any research, review, ledger, source-grounding, score-hardening, or plan-hardening move remains runnable.
  • Blocked only if a required source/ledger artifact is missing and no narrower local fallback can answer the current pass, or the user stops the lane before the next pass.

Slate Plan lane state:

  • slate_plan_lane_status: accepted-execution-complete
  • current_pass: closure-verification
  • current_pass_status: complete
  • next_pass: none
  • next_action: final handoff
  • final_handoff_status: execution handoff complete

Accepted execution addendum:

  • 2026-05-29: user accepted the plan direction and asked to add a full API consistency sweep before starting implementation.
  • Execution starts with a narrow but complete audit across public Slate substrate and private 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.
  • First execution slice starts in .tmp/slate-v2 with source inspection and focused tests around the smallest surface that proves or improves consistency.

Current verdict:

  • verdict: accepted execution complete
  • confidence: closure planning score 0.925; no dimension below 0.85; execution is verified by focused tests, browser rows, package typechecks, bun check, bun check:full, focused retries-off reruns for full-suite flakes, and clean final autoreview
  • keep / cut / revise call: keep editor.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.
  • reason: current source already has internal command registry, transform middleware, commit metadata, 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:

  • Do not call update_goal(status: complete) while any required checklist item remains unchecked. If an item does not apply, check it and add N/A: <reason>.
  • Do not call 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.
  • Do not create hook state for this goal. This file plus the active goal are the durable state.

Start Gates:

GateAppliesEvidence
Skill analysis before editsyesslate-plan prompt loaded in user message; planning mode only
Active goal checked or createdyesget_goal returned none; create_goal created this lane goal
Source of truth read before editsyesplan, 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 workN/Aimplementation 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 claimsyespublic export, internal export, extension, command-registry, transform-middleware, update/afterCommit, editable command, clipboard reads recorded below

Work Checklist:

  • Objective includes lane outcome, full pass schedule, one-pass-per- activation policy, completion threshold, verification surface, constraints, boundaries, and blocked condition.
  • One-pass-per-activation policy respected, or marked N/A with reason.
  • Live source grounding recorded for every current implementation claim, or marked N/A with reason.
  • Issue ledger / ClawSweeper pass applied or skipped with concrete evidence.
  • Research and ecosystem synthesis complete for every external system used as evidence, or marked N/A with reason.
  • Intent/boundary record and decision brief complete.
  • Scorecard recorded with evidence; total score >= 0.92 and no dimension below 0.85 before closure.
  • Applicable implementation-skill review matrix applied or skipped with concrete reason.
  • Slate maintainer objection ledger complete for every breaking/paradigm change, or marked N/A with reason.
  • Verification workspace gate recorded for every Slate v2 source, runtime, browser, package, public API, or issue-fix claim.
  • TDD used for behavior/proof changes with a sane test surface, or marked N/A with reason.
  • Browser proof captured for browser-surface claims, or marked N/A with reason.

Completion Gates:

GateAppliesRequired actionEvidence
Named verification thresholdcompleteRun the command, proof, source audit, or artifact check named in this planClosure 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 claimcompleteRecord live .tmp/slate-v2 command/proof or mark as planning-only with reasonExecution 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 changedcomplete for current passCurrent manual v2 sync ledger updated; PR description, coverage matrix, and fork dossier explicitly unchanged because no claim text changeddocs/slate-issues/gitcrawl-v2-sync-ledger.md 2026-05-29 planning sync
Autoreview for uncommitted implementation changescompleteLoad .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 patchFinal 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 handoffcompleteEmit final handoff or keep the plan open with the next passFinal response presents execution result, hard cuts, verification, flakes, and residual risks
Goal plan completeyesRun node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.mdClosure command passes in this pass

Phase / pass table:

PhaseStatusEvidenceNext
Current-state read and initial scorecompletelive source reads in .tmp/slate-v2; research and Evidence Kit control-plane read; initial scorecard and provisional target rows filledrelated issue discovery
Related issue discoverycompletereused 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 clustersissue-ledger pass
Issue-ledger passcompleteadded 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 sectionintent/boundary pass
Intent/boundary and decision briefcompleteintent, outcome, in-scope, non-goals, decision boundaries, unresolved user-decision points, principles, drivers, options, chosen direction, rejected alternatives, consequences, and follow-ups are filledresearch refresh
Research, ecosystem strategy, live-source refreshcompletesynthesized 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 buspressure passes
Performance/DX/migration/regression/simplicity pressure passescompletepressure 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 defaultobjection ledger
Slate maintainer objection ledgercompleteanswered 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 gateshigh-risk pass
High-risk deliberate modecompletekilled 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 benchmarksecosystem maintainer pass
Ecosystem maintainer passcompleteReact 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 APIsrevision pass
Revision passcompletefinal 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 expansionissue sync accounting
Issue sync accountingcompletereused 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 shapeclosure score and final gates
Closure score and final gatescompleteclosure 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 preparedfinal handoff

Scorecard:

DimensionWeightScoreEvidence
React 19.2 runtime performance0.200.86Positive: 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 DX0.200.94Public 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 backbone0.150.93Commit 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 strategy0.200.94Portable 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 completeness0.150.96Portable 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 minimalism0.100.93Product 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:

  • target shape: Slate core stays transaction-first: the 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.
  • source evidence: public root exports 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).
  • rejected drift: do not expose 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).
  • migration posture: existing transform/clipboard authors should stay on the current public substrate. Native input, paste/drop, keyboard, history, and app-level editable intent are not a new public command surface in this plan. Ordinary data changes remain direct editor.update((tx) => ...).

Public API target:

SurfaceProposed shapeUser-facing DXCompatibility / migrationEvidenceVerdict
Core writesKeep editor.update((tx, { afterCommit }) => { tx.text.insert('x'); afterCommit(...) }) as the public effect boundaryOne write boundary, tx mutation is obvious, side effects are visibly post-commitNo migration needed for current v2 call sitespublic-state.ts:2208, 2499, 4551keep
Public Editor.* namespaceDo not add Editor.registerCommand, Editor.insertText, or Editor.commands to public docs/examplesAvoids old Slate static-namespace gravity and wrong examplesInternal Editor type/name remains; public authoring uses instance/update/extensionsindex.ts:1; internal/index.ts:1cut
Imperative editor.commands.onDo not add this public shapeLooks easy but bypasses extension lifecycle, ordering, teardown, and React Strict Mode pressureNo migration path; not part of accepted public APIcurrent registry is internal and extension lifecycle already owns slotsreject
Extension transform middlewareKeep as tx-level mutation middleware for transform-shaped workSlate-close: transforms.insertText({ text, next, tx }) style already matches transform namesExisting extension authors use transforms; no rename in pass 1interfaces/editor.ts:928; editor-extension.ts:447; transform-middleware.ts:11keep
Semantic native input commandsKeep a stable internal taxonomy in slate-react; expose no public extension slot in this planPublic docs stay on current Slate substrate while native event internals get one vocabularyCurrent internal EditableCommand taxonomy is private runtime behavior, not a public contracteditable-command-types.ts; mutation-controller.ts:610; editing-kernel.ts search output; projected-command and browser command testskeep internal
Clipboard/pasteKeep clipboard.insertData on the existing extension surface, but hard-cut command/middleware returns to strict booleanPaste/drop stays on existing extension surface; true handles, false declines/falls throughBreaking return-shape cleanup accepted for v2; no { handled } objectinterfaces/editor.ts:1446; clipboard-input-strategy.ts:51; phase 1 patchkeep strict boolean

Internal runtime target:

LayerCurrent ownerTarget mechanismAvoidsEvidenceVerdict
Command registrypackages/slate/src/core/command-registry.tsStay internal; continue powering transform middleware and static compatibility pathsPublic bus sprawl and out-of-band mutationcommand-registry.ts:32, 36, 69 and internal/index.ts:1keep internal
Transform command bridgepackages/slate/src/core/transform-middleware.ts and extension registrationKeep deterministic next/default semantics; strengthen docs/tests around handled/forward behaviorTransform overrides that mutate DOM events or duplicate default logictransform-middleware.ts:112; editor-extension.ts:456keep
Editable command taxonomypackages/slate-react/src/editable/editable-command-types.ts, editing-kernel.ts, mutation-controller.tsKeep stable internal taxonomy; normalize scenario matrix before execution closureNative input, keydown, paste/drop, and history taking divergent code pathsmutation-controller.ts:610, 661, 668, 678, 699; projected-command-contract.test.ts; browser beforeinput rowskeep internal
Effect boundaryeditor.update context and extension onCommitKeep effects post-commit; forbid hidden side effects inside transforms except scheduling afterCommitTransform middleware causing analytics/sync/UI effects before commit truth existspublic-state.ts:2217, 4560; interfaces/editor.ts:1473, 1491keep

Hook / component / render DX target:

SurfaceCall-site shapeComposition rulePerformance ruleEvidenceVerdict
Raw Slate exampleShow defineEditorExtension({ transforms: { insertText(...) {} } }) and editor.update firstNo helper extraction unless reusedNo render subscription for command policycurrent public extension slotskeep
React editable input policyRuntime-owned internal taxonomy only; no Editable onCommand and no public input-policy exampleEvent runtime owns native input; apps keep using current Slate substrateHandler runs during event/update, not render/effect watcherEditableDOMRootProps exposes no onCommand; mutation controller owns editable commandskeep internal
Product toolbar applicabilityKeep in Plate/examples, not raw Slate coreUse schema/state selectors from extension stateSelector reads must be narrowPortable Text toolbar selector evidence is product-DX onlydefer to Plate

Plate migration-backbone target:

PressureSlate substrate targetPlate adaptation routeNon-goalEvidenceVerdict
Rich behavior pluginsExtension-owned transforms plus stable internal native-input taxonomy; no raw Slate product command slotPlate maps plugins to Slate extension slots and keeps product shortcuts/toolbars outside coreDo not preserve current Plate public APIs in raw Slateextension state/tx/transforms/clipboard slotsrevise
Toolbar allowed/applicable stateSchema/state selectors, not command bus statePlate asks Slate state/schema and product profile selectorsDo not ship a Slate toolbar packagePortable Text selector evidence; current state groupsdefer

slate-yjs migration-backbone target:

PressureSlate substrate targetCollaboration routeNon-goalEvidenceVerdict
Deterministic command replayCommands annotate commits through metadata/tags; operations remain the replay truthyjs adapter observes operations/commits, not UI eventsDo not make remote replay depend on native event namesEditorCommitCommand, update metadata, operationskeep
EffectsafterCommit/onCommit after deterministic commitRemote send/sync runs after local commit is materializedNo hidden transform-side remote effectspublic-state.ts:4551keep

Intent / boundary record:

  • intent: define the smallest Slate-native behavior pipeline that makes native input, paste/drop, keyboard editing, history, and app-editable intent deterministic without turning raw Slate into a product command framework.
  • outcome: user-review-ready architecture decision: public mutation stays 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.
  • in-scope: input/beforeinput guard/handle/forward semantics; insert text, insert break, delete backward/forward/word/line/block/fragment, paste/drop, move/extend selection, history undo/redo, and native format intent; transform middleware; clipboard ingress; commit metadata; after-commit effects; scenario matrices and proof gates.
  • non-goals: public 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.
  • decision boundaries:
    • mutation belongs in tx.*, not event handlers or effects.
    • effects belong in afterCommit/onCommit, not hidden inside transforms.
    • extension lifecycle owns public interception through existing extension slots; imperative command registration stays internal.
    • native input policy belongs to the slate-react event runtime; core Slate stays transaction and extension substrate.
    • product applicability, toolbar enablement, schema profiles, and polished shortcuts belong to Plate.
    • collaboration replay depends on operations/commits; command names are local intent metadata only.
  • unresolved user-decision points:
    • whether clipboard boolean handlers stay indefinitely or get a compatibility adapter to a shared handled-result shape.
    • which scenario families become release gates before execution is accepted.

Decision brief:

  • principles:
    • Slate stays unopinionated and transaction-first.
    • Public API is instance/extension-first, not Editor.*.
    • Native browser behavior gets explicit policy only where behavior is currently duplicated or fragile.
    • Every behavior boundary needs scenario tests before execution closure.
    • Effects are post-commit, never mutation truth.
  • top drivers:
    • live source already rejects extension commands and exposes command registration only internally.
    • input/IME/clipboard/delete issue rows all point to the same fragile boundary: native event handling plus model mutation plus selection repair.
    • React runtime pressure requires event/update-time work, not render/effect watchers or broad subscriptions.
    • Plate needs a substrate it can compose, not raw Slate absorbing product command/tooling policy.
    • slate-yjs needs operations/commits as truth; command labels cannot become replay protocol.
  • viable options:
    • Option A: expose Editor.registerCommand / Editor.commands.
    • Option B: expose editor.commands.on(...) as a public middleware bus.
    • Option C: keep only transforms and clipboard, document them harder, and leave native input taxonomy internal.
    • Option D: keep A/B cut, keep C as the public substrate, and harden private slate-react native-input taxonomy plus scenario matrix.
  • chosen option: Option D-internal-only is the current plan direction. It matches live source, preserves raw Slate minimalism, gives native input bugs one owned runtime boundary, and keeps product command DX out of core.
  • rejected alternatives:
    • Reject Option A because it reopens static-namespace API gravity and exposes an internal registry as public architecture.
    • Reject Option B as the default because lifecycle, ordering, teardown, React Strict Mode, and extension composition are worse than extension-owned slots.
    • Reject Option C as the final answer because it leaves input, delete, paste/drop, selection movement, and history taxonomy split across runtime internals without a coherent private contract.
    • Reject Portable Text vocabulary copy-paste because the boundary model is useful but the names and CMS/value/runtime assumptions are foreign to Slate.
  • consequences:
    • the final public API is smaller than a command bus and harder to misuse.
    • examples must show editor.update, tx, extension slots, and afterCommit directly instead of hiding the real API behind helpers.
    • implementation must add scenario tests before execution closure.
    • issue claims stay conservative until behavior proof exists.
  • follow-ups: none before issue sync; the next pass is no-claim accounting.

Issue accounting:

Issue / clusterClaim categoryExact claimWhyProof routeV2 sync ledgerPR line
#5050 text insertion accept/rejectRelatedNo fix claim; input accept/reject belongs to beforeinput/input command policy, not a freeform handlerSame boundary as semantic insert-text guard/forward policyinsert-text scenario rows across collapsed/expanded selectionsunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#3568, #3586, #4681 native format / beforeinputRelated/materially-improved history, unchanged for this planNo new claim; native format command classification constrains the public onCommand cutThese rows pressure command taxonomy and post-DOM-beforeinput mutation disciplinenative-format and beforeinput scenario rowsunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#4613 insertData extensibilityExisting improves claim unchangedExisting improve stays scoped to typed insertData input ingress; no broader paste-rule/output-serializer claimClipboard/paste policy is part of this architecture surfaceclipboard policy scenario rows plus docs/API audit if return shape changesunchanged; 2026-05-29 manual sync section records no promotionkeep existing scoped improve only
#4569 insertData docsExisting fix claim unchangedExisting docs fix stays exact; this plan only reuses it as command-policy pressureClipboard docs may need PR reference sync only if public docs wording changesdocs proof if wording changesunchanged; 2026-05-29 manual sync section records no promotionkeep existing fix line
#5233, #3486, #4806 clipboard fixed floorExisting fix claims unchangedCustom fragment key and selected inline void clipboard claims remain exact; this plan must not dilute themPaste/drop command policy must preserve fixed clipboard behaviorclipboard regression rowsunchanged; 2026-05-29 manual sync section records no promotionkeep existing fix lines
#4888, #4802, #4104, #3926, #4623 drop/move/payload remapRelated/status unchangedNo new fixed/improved claim; paste/drop taxonomy must account for these rowsDrop/move behavior is adjacent to insertData and native command routingpaste/drop/drag scenario matrixunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#6022, #5989, #5984, #5931, #5603, #5669, #5883, #4400 and mobile/IME clusterRelated/status unchangedNo exact mobile/IME closure; raw-device/browser proof remains requiredIME/composition rows are the highest-risk pressure on command-policy designIME/composition scenario rows and raw-device proof only if claimedunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#3991, #3868, #5582, #5477, #4896, #4350, #4328, #5630 delete/selection clusterRelated/status unchangedDelete and selection rows remain preserve-only until scenario proof existsDelete/backspace/move/extend commands are part of the proposed taxonomydelete and selection rebasing scenario matrixunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#2405 command-scoped normalizationRelatedNo closure claim; command-scoped normalization is architectural pressure onlyNormalization/perf can benefit from command metadata but needs measured proofbenchmark/control-plane candidate plus normalization scenariounchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#2694, #3304, #1024 native clipboard/event rowsRelated/status unchangedNo fix claim; Shift-Delete, file drop, and MIME typing map to clipboard/native taxonomyThese rows pressure typed native command vocabulary and fallback orderingclipboard/drop scenario rowsunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only
#1770 operation composition / collaboration pressureRelatedNo closure claim; command metadata/effects cannot become operation truthCollab must replay operations/commits, not UI command namescommit metadata and collaboration proof after executionunchanged; 2026-05-29 manual sync section records no promotionrelated matrix only

Issue-ledger sync status:

  • ClawSweeper related-issue discovery: complete by reuse. Existing completed outputs already cover this surface, so no broad live GitHub or new ClawSweeper run was needed in this pass.
  • Reused fork dossier sections: #5050 input policy, #4613/#4569 insertData, mobile/IME macro sync, All-Harvest PM-10/PM-09 input runtime sync, public onCommand cut, and Plate-fit API hard cuts.
  • generated live gitcrawl rows read: input/IME rows (#5989, #5912, #5883, #5375, #5066, #4693, #4400, #4269, #4136, #4067, #4030, #3882), clipboard/drop rows (#5233, #4888, #4613, #4569, #3611, #3304, #2694, #1024), plus current v2 sync ledger status rows for delete/selection, clipboard/drop/move, mobile/IME, focus/scroll, history, and command-surface clusters.
  • test-candidate map read: Shift-Delete, addMark during onDOMBeforeInput, IME composition, Android native input, clipboard fragment, drop-inside-void, history undo, void selection, and native input parity candidate rows.
  • Issue-ledger pass: complete. Added docs/slate-issues/gitcrawl-v2-sync-ledger.md section 2026-05-29 Command Behavior Pipeline Architecture Planning Sync.
  • manual v2 sync ledger update: complete for current pass; it records no Fixes, no Improves, and no issue promotion.
  • fork issue dossier update: unchanged; existing sections already cover the reviewed surfaces and no new per-issue claim was created.
  • issue coverage matrix update: unchanged; no new fixed/improved claim.
  • PR description sync: unchanged; no claim text changed.

Ecosystem strategy synthesis:

SystemSourceMechanismAvoidsStealRejectSlate targetVerdict
Portable Textdocs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.md:91Behavior event/action chain with guard, execute/forward/raise/effect, explicit ordering, and browser behavior specsHandler soup and untested behavior pluginsBoundary model, explicit handled/forward discipline, scenario-style behavior testsPT value format, CMS categories, XState runtime, exact API namesSlate-native runtime taxonomy over editor.update, not a PT clonesupports internal-first Option D
Lexicaldocs/research/sources/editor-architecture/lexical-read-update-extension-runtime.mdRead/update closures, prioritized command listeners inside update context, update tags, dirty transform scheduling, extension lifecycleCommands as the normal public mutation API; dirty work hidden in React renderRead/update discipline, lifecycle tags, command handlers inside update, dirty scheduling, extension lifecycle pressureClass nodes, $ helpers, public dispatch-command app API as Slate's main DXKeep editor.update and internal command registry; use tags/metadata and dirty commits; do not expose a command bus as the normal APIsupports Option D
ProseMirrordocs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.mdTransactions own doc/selection/mark/meta changes; view owns DOM input, observer, selection import/export, compositionApp commands reading DOM selection directly or post-hoc normalizing browser behaviorTransaction metadata, selection mapping/bookmarks, one DOM bridge ownerInteger positions, schema-first model, plugin complexity, React as a wrapper around a PM-style view treeNative input policy belongs to slate-react; mutation stays in tx; DOM import/export has one ownersupports Option D
Tiptapdocs/research/sources/editor-architecture/tiptap-extension-command-react-dx.md; Plate source readsExtension-packaged commands, chainable command catalog, React selector/composable UI postureUndiscoverable product commands and toolbar glueProduct command discoverability, extension packaging, selector postureRaw Slate editor.commands/chain() as the main write APIPut polished command/profile DX in Plate; raw Slate exposes substrate and internal runtime taxonomy onlysupports Plate boundary
React 19.2docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.mduseSyncExternalStore, transitions/deferred UI, Activity, Performance TracksRender/effect watchers mutating editor state or broad editor rerendersExternal-store subscriptions, urgent editing path, non-urgent surrounding UI, profiler evidenceTreating React scheduler as the editor invalidation engineRuntime taxonomy must run in event/update time and feed dirty commits to selectors; effects stay after commitsupports Option D with perf gates
Platedocs/research/systems/editor-behavior-architecture.md; packages/media/src/react/placeholder/PlaceholderPlugin.tsx; apps/www/src/registry/components/editor/transforms.tsProduct plugins expose editor.api, editor.tf, plugin handlers, transforms, shortcuts, and profile-like behavior decisionsRaw Slate absorbing toolbar/profile/product commandsBehavior profiles, product command catalog, plugin-provided capabilities mapped onto Slate substrateCurrent Plate public API compatibility as a raw Slate requirementPlate owns product command DX and behavior profiles over Slate tx/state/native-input substrateboundary confirmed
slate-yjs / Yjsdocs/research/sources/editor-architecture/yjs-collaboration-bindings.md; live Slate source readsExtension-owned binding state, commit-driven local export, remote import through editor.update, relative positions/bookmarks, awareness external storeCommands as collaboration replay protocol or Yjs/provider objects in Slate valuesonCommit, state/tx namespaces, commit metadata, relative-position/bookmark mapping, awareness external-store hooksExternal withYjs(editor) wrapper mutation, overriding apply/onChange, provider policy in raw SlateCommands can annotate local intent; operations/commits remain replay truth; Yjs package uses extension-owned state/txsupports Option D
Local Slate v2live .tmp/slate-v2 source readsPublic read/update, transforms, clipboard, onCommit, state/tx extension slots; internal command registry; editable command taxonomyReintroducing rejected extension commands or public Editable onCommandCurrent substrate: extension lifecycle, tx mutation, afterCommit effects, internal taxonomy, model-owned mutation controllerTreating current internal EditableCommand as publicKeep the existing substrate; native-input taxonomy stays private in this plansource confirms internal-only Option D

Research pass verdict:

  • No ecosystem source justifies public Editor.* or default editor.commands.on.
  • Lexical and ProseMirror both strengthen the event/update/transaction boundary, not a command-bus API.
  • Tiptap strengthens Plate's product command DX responsibility, not raw Slate's public surface.
  • React 19.2 makes the selector/commit runtime credible, but it raises the bar: command policy must not add broad subscriptions or render-time mutation.
  • slate-yjs/Yjs keeps command labels local. Operations, commits, bookmarks, and extension-owned binding state remain the collaboration contract.
  • Therefore Option D survived research: keep current 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 lensAcceptsRejectsRequired proof / revisionVerdict
React runtime ownerEvent/update-time internal taxonomy; afterCommit/onCommit effects; narrow external-store notificationsrender/effect watchers, broad subscriptions, public command middleware on the urgent pathkeep benchmark gates on typing/select/select-all; no public command/input extension API in this planaccept internal-only
Slate core ownerinstance-first public API, extension transforms/clipboard, tx/state groups, internal command registrypublic Editor.*, default editor.commands.on, PT/Tiptap vocabulary, generic app-command frameworkrevision pass must remove any wording that implies command bus destinyaccept with wording cleanup
slate-react native-input ownerone runtime owner for beforeinput/input/keyboard/clipboard/drag/composition; internal EditableCommand taxonomyEditable onCommand, app handlers reading DOM selection, public hooks that race repair/compositionscenario matrix before public API; browser ownership stays in runtimeaccept internal-only
Plate adapter ownerSlate substrate plus Plate behavior profiles, product commands, toolbar applicability, shadcn UIraw Slate toolbar/profile package, current Plate API compatibility as a raw Slate requirementrevision pass should show Plate adapts over substrate, not through raw Slate public command busaccept boundary
slate-yjs owneroperations/commits/bookmarks/metadata as replay truth; command labels as local diagnostics onlyremote replay by UI command name; Yjs/provider objects in raw Slate valuesfinal plan must say commands never become collaboration protocolaccept boundary
Portable Text comparatorexplicit handled/forward discipline and scenario specsPT value format, XState runtime, CMS behavior categories, exact namessteal tests/boundaries, not APIaccept selectively
Lexical comparatorread/update lifecycle, update tags/metadata, command handlers inside updateclass nodes, $ helpers, public dispatch-command as normal Slate DXkeep editor.update as public write storyaccept selectively
ProseMirror comparatortransaction ownership and single DOM bridge ownerPM plugin complexity, integer-position model, app commands reading DOM selectionkeep DOM import/export centralized in slate-reactaccept selectively
Tiptap comparatorproduct command discoverability and selector postureraw Slate editor.commands/chain()put command catalog in Plate, not raw Slateaccept selectively

Ecosystem maintainer pass verdict:

  • Every owner rejects a public command bus as the default.
  • The only accepted raw Slate shape is internal native-input taxonomy plus the existing public substrate: editor.update, tx, transforms, clipboard, state/tx, operations, queries, onCommit, and afterCommit.
  • React and slate-react owners keep native input private to the runtime; the plan includes no public command/input extension API.
  • Plate and Tiptap evidence say product command catalogs belong above Slate.
  • slate-yjs/Yjs evidence says command labels can annotate local intent, but operation/commit replay remains the collaboration contract.
  • Revision pass cleaned the remaining wording so the plan no longer reads like a delayed public command API.

Revision pass final proposed shape:

  • Final public API shape: editor.update, tx.*, afterCommit, extension transforms, clipboard, state, tx, queries, operations, and onCommit.
  • Final internal shape: 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.
  • Final cuts: no public 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.
  • Final result-shape call: command/middleware propagation uses strict boolean. clipboard.insertData keeps its extension surface but no longer accepts command-boundary void; no { handled } object exists in command propagation.
  • Final execution prerequisite: scenario matrix first. The execution plan must harden delete, paste/drop, IME/composition, selection movement, history, projected/content roots, and clipboard behavior before any release-quality claim.
  • Final benchmark prerequisite: public performance claims still require the registered Evidence Kit gates. Current huge-doc typing/select/select-all red stays an accepted execution/release gate, not a planning closure claim.

Pressure pass verdict:

  • decision: Option D survives, but only as Option D-internal-first. The plan does not accept Option D-public yet.
  • performance: Evidence Kit health has no missing required artifacts and the active benchmark registry covers React huge document, rerender breadth, overlays, active typing, browser trace, rich-text replay, core transforms, selection, refs/projection, clipboard payload, collaboration readiness, and history compare. That is enough coverage to pressure the plan, not enough to bless new public API. Current huge-document evidence still has typing, select, and select-all red against legacy chunking. Any public command policy that adds selector fanout, allocations, or event-time overhead is rejected until focused benchmarks prove otherwise.
  • DX: Raw Slate should teach 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.
  • migration: Plate should build product command/profile/tooling DX on top of Slate substrate. slate-yjs should observe operations and commits. Command labels can annotate local intent, but they cannot become collaboration replay protocol.
  • regression: Slate v2 already has relevant proof rows: projected insert/delete commands, transform middleware 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.
  • simplicity: adding a public command layer is needless API surface. The clean plan is to harden internal taxonomy and scenario tests while keeping public substrate unchanged.
  • result: public 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 classLegacy behaviorSlate v2 targetProof routeOwnerStatus
Text insertion guardApps can block/modify input through handlers, often with beforeinput edge bugsInternal insert-text taxonomy can block/forward without DOM mutation hacksmodel-input, input-router, browser insertText scenariosslate-reactexecution-gated
Delete/backspaceLegacy delete semantics split across transforms, beforeinput, keydown, void/inline boundariesDelete command taxonomy covers backward/forward/word/line/block/fragment and beforeinput duplicationdelete matrix scenario testsslate-react + slateexecution-gated
Paste/dropLegacy insertData extensibility copies internalsPaste/drop command policy composes with clipboard middleware and tx fragment insertpaste/drop scenario matrix plus clipboard benchmarkslate + slate-reactexecution-gated
IME/compositionBrowser-specific beforeinput/composition order bugsCommand pipeline preserves composition owner and post-commit effectsIME/composition scenario rows and mobile proof where availableslate-reactexecution-gated
Selection move/extendKeyboard/native selection can diverge from modelMove/extend selection commands preserve projected roots and hidden/content rootsselection rebasing/browser rowsslate-reactexecution-gated

Browser stress / parity strategy:

SurfaceScenarioBrowser/deviceCommand or proof routeExpected signalStatus
beforeinput insert textblock/forward/replace insertText at collapsed and expanded selectionsChromium first, then WebKit/Firefox rows if execution changes runtimefocused editing-kernel/model-input/browser contractone command, one commit, no duplicate DOM repairexecution-gated
destructive keysBackspace/Delete/word/line/full-block across text, void, content root, synced rootChromium plus mobile/device if IME row changesdelete scenario matrixdeterministic selection and historyexecution-gated
paste/droppaste plain/html/fragment, internal drag, external dropChromium first; add Firefox/WebKit for native gapsclipboard scenario matrix + benchmark mappinghandled result stops default, forward preserves fallbackexecution-gated
IME/compositioncomposition insert/delete/commit orderingChromium/WebKit; Android raw device only if claimedcomposition scenario matrixno pre-commit side effects, stable selectionexecution-gated

Verification workspace gate:

ClaimWorkspaceCommandResultOwner
Current public exports and internal command registry location.tmp/slate-v2source read: packages/slate/src/index.ts, packages/slate/src/internal/index.tsread; no runtime commandpass 1
Current extension slots and legacy commands rejection.tmp/slate-v2source read: interfaces/editor.ts, core/editor-extension.tsread; no runtime commandpass 1
Current handled/next/afterCommit substrate.tmp/slate-v2source read: core/command-registry.ts, core/transform-middleware.ts, core/public-state.tsread; no runtime commandpass 1
Current native command taxonomy and clipboard routing.tmp/slate-v2source read: slate-react/src/editable/*read; no runtime commandpass 1
Research/ecosystem synthesis against Option Dplate-2 + .tmp/slate-v2source reads: research docs, Plate plugin/transform examples, Slate editable/source filescomplete; no runtime commandresearch pass
Pressure pass benchmark/test groundingplate-2 + .tmp/slate-v2source reads: Evidence Kit latest results, huge-doc perf docs, projected-command tests, extension middleware tests, browser command rowscomplete; no runtime commandpressure pass
Slate maintainer objection grounding.tmp/slate-v2source reads: public/internal exports, extension legacy rejection, EditableDOMRootProps, command registry, command result, clipboard middleware, extension setup output, editable command taxonomy, mutation controller, afterCommit/onCommitcomplete; no runtime commandobjection pass
High-risk deliberate-mode groundingplate-2 + .tmp/slate-v2source/result reads: benchmark health/latest huge-doc compare, runtime event engine, input router, transform middleware, clipboard strategy, public-state afterCommit, command registry, mutation controllercomplete; no runtime commandhigh-risk pass
Ecosystem maintainer groundingplate-2 + .tmp/slate-v2source/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 recordedcomplete; no runtime commandecosystem pass
Revision pass wording auditplate-2plan search for delayed public API framing and source-backed row rewritescomplete; no runtime commandrevision pass
Phase 1 implementation behavior.tmp/slate-v2focused tests + bun check after accepted executioncomplete for command/middleware result cleanup; broader browser/scenario behavior remains phase 2+execution mode

Autoreview workspace gate:

Reviewed patch ownerCwdCommandResultNotes
N/AN/AN/AN/APlanning-only lane; autoreview skill loaded, but no Slate v2 implementation patch exists to review.

Applicable implementation-skill review matrix:

LensAppliesStatusFindingsPlan delta
vercel-react-best-practicesyescomplete for planningCommand policy touches React event/runtime boundariesNo render/effect mutation; no broad subscription; native policy runs in event/update time only
performance-oracleyescomplete for planningCommand, normalization, paste, and selection paths are hotReject public policy until complexity, allocation, and p95 impact are benchmarked
performanceyescomplete for planningEvidence Kit required artifacts are present, but huge-doc typing/select/select-all stay redTreat benchmarks as a hard gate before release-quality performance claims
tddyescomplete for planningPrivate runtime taxonomy needs behavior contractsScenario matrix is required before execution closure
shadcnpartialcomplete for planningOnly examples/tooling controls, not coreProduct command/profile UI stays Plate-owned
react-useeffectyescomplete for planningafterCommit and extension effects are in scopeEffects are post-commit external sync only; transforms do not hide effects

High-risk deliberate-mode pre-mortem:

RiskTriggerFailure modeMitigationProofStatus
Public command API sprawlpublic API / extension substrateeditor.commands.on, Editor.registerCommand, or Editable onCommand becomes a second plugin system with lifecycle leaksKeep all three cut; keep command registry internal; public docs use existing substrate onlysource export audit and extension legacy rejectionmitigated for plan
Hidden hot-path overheadnative input / selection / huge documentinternal taxonomy adds allocations, extra dispatch, broad selector fanout, or event-time work in typing/select pathsTaxonomy stays internal and must not add public middleware dispatch; benchmark gates before release claimsEvidence Kit health plus huge-doc legacy compare red rowsopen risk, gated
Native input regressionsbeforeinput, keydown, paste, composition, drag/dropguard/handled layer blocks DOM default but misses repair, composition state, projected-root selection, or fallback browser behaviorRuntime event engine keeps ownership; no app-facing command handler; scenario matrix by input familyruntime-event-engine, input-router, clipboard strategy, projected command/browser testsopen risk, gated
Collaboration ambiguitycommit/operation behaviorremote replay depends on local UI command names instead of deterministic operationscommand labels are metadata only; slate-yjs observes operations/commits and maps positions/bookmarksupdate/afterCommit/onCommit source plus collab readiness benchmark candidatemitigated for plan
Clipboard churnpaste/drop API alignmenthalf-compatibility keeps void holes and makes handled semantics ambiguoushard-cut command/middleware returns to strict boolean; no { handled } objectclipboard middleware and paste strategy source plus phase 1 tests/typecheckmitigated by hard cut
Plate-in-core pressureproduct command/profile DXraw Slate grows toolbar applicability, schema profiles, or shadcn command catalograw Slate owns substrate; Plate owns product DX and UIextension output substrate and Plate boundary rowsmitigated for plan
Internal taxonomy fossilizesprivate runtime names become de facto public through docs/examples/testsprivate names leak into user-facing docs and constrain later designdo not document taxonomy as public; revision pass separates internal names from user-facing wordingpublic API target rows and final docs gateopen risk, gated
App-command funnelgeneral app actions route through native-input taxonomySlate becomes a generic command framework and duplicates direct editor.updateapp commands stay direct editor.update or Plate-level product commandsdecision brief and maintainer ledgermitigated for plan

High-risk deliberate-mode verdict:

  • The internal-first taxonomy survives, but the bar is stricter: it is a runtime implementation boundary, not a public API promise.
  • A command/input extension API is rejected for this plan. Separate future work would need to prove it improves real extension authoring without worsening typing, selection, paste/drop, IME, projected roots, or collaboration boundaries.
  • The biggest active risk is performance, not API aesthetics. Current 5000-block compare rows still show v2 losing typing/select/select-all lanes against legacy chunking. That makes any new event-time dispatch suspect until measured.
  • Browser ownership remains single-owner: slate-react runtime handles native beforeinput/input/keyboard/clipboard/drag/composition routing. Apps do not get a public command hook at the DOM boundary.
  • Clipboard alignment is worth the small v2 break: strict boolean is the command result contract, while richer local helper objects stay out of command propagation.
  • The next pass must review this result from ecosystem owner viewpoints, not reopen the command-bus idea from scratch.

Slate maintainer objection ledger:

ChangeObjectionTradeoffEvidenceMigration/docs/proof answerVerdict
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 ownershippublic root exports types/instance/extension surfaces, while defineCommand/executeCommand/registerCommand are internal exportsKeep 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 questionsextension validation rejects commands; command registry supports cleanup/order internally, but is not public lifecycleDo 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 rootsEditableDOMRootProps exposes onDOMBeforeInput/onKeyDown, not onCommand; runtime event engine already owns native routingKeep 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 sliceEditableCommand 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 helpersStabilize 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 todayphase 1 hard-cuts command registry, transform bridge, clipboard middleware, DOM clipboard, examples, and docs to strict booleantrue 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 appsextension output already exposes state, tx, transforms, clipboard, queries, operations, and onCommitRaw 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 clientsupdate context, commit metadata, operations, onCommit, and afterCommit are the deterministic boundary; editable commands include browser-local intentCommands 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 debtcurrent source explicitly rejects extension commands; PT names are useful concepts but foreign vocabularyDo not name a public extension API in this plan.cut
Scenario/benchmark gates"This is too much process for a private taxonomy."Slower execution iterationcurrent benchmark rows have typing/select/select-all red; existing tests cover ingredients but not a normalized release matrixBrowser input behavior is regression-prone; execution waits for matrix proof.keep

Slate maintainer objection pass verdict:

  • The skeptical maintainer answer is blunt: do not ship a public command/input API yet. The plan is stronger when it treats native-input commands as a runtime taxonomy and proof matrix, not a new user-facing abstraction.
  • The only public surfaces that survive this pass are existing Slate-native ones: editor.update, tx, transforms, clipboard, state/tx extension groups, operations, queries, onCommit, and afterCommit.
  • A native-input extension API is not part of this plan. A separate proposal would need naming/lifecycle acceptance, scenario matrix coverage, and benchmark proof that it does not worsen hot paths.
  • Clipboard alignment is strict boolean. true handles, false declines, and next(command?) forwards; command propagation does not accept void or { handled }.
  • Plate and slate-yjs boundaries are clean: Plate owns product command/profile DX; slate-yjs owns operation/commit observation. Raw Slate must not become a toolbar framework or a collaboration protocol.

Hard cuts and rejected alternatives:

Option / APIKeep / cut / rejectWhyMigration costEvidenceFollow-up
Public Editor.registerCommandcutWrong public namespace; internal registry is implementation detailnone for v2 public APIinternal/index.ts:1none
Default editor.commands.onrejectImperative lifecycle and ordering are worse than extension-owned policynoneextension setup already owns slotsnone
PT execute/forward/raise/effect namesreject exact namesGood model, foreign vocabularynonePortable Text researchkeep rejected unless revision pass finds a Slate-native name
Strict boolean command returnskeepOne propagation contract beats object ceremony and ambiguous voidsmall v2 break acceptedEditorCommandResult, transform bridge, clipboard maps, phase 1 patchcomplete

Plan deltas from review:

  • Pass 1 filled the template objective, constraints, source-backed verdict, initial scorecard, provisional public/internal API target, proof matrix, benchmark control-plane status, and next pass owner.
  • Dropped the earlier accidental public Editor.* proposal.
  • Rejected editor.commands.on as the default public shape.
  • Strengthened the plan around native-input taxonomy and scenario tests.
  • Pass 2 completed related issue discovery without a new live GitHub sweep: existing ClawSweeper/fork-dossier output already covered input/native command, clipboard/drop, delete/selection, IME/composition, history, and command-surface pressure.
  • Pass 2 changed no issue claim status. Existing fixed/improved claims remain exact; the command/behavior pipeline plan is related pressure only until execution proof exists.
  • Issue-ledger pass added a current manual sync section and explicitly kept PR description, issue coverage matrix, and fork dossier unchanged because no claim text changed.
  • Intent/decision pass hardened the boundary: Option D wins for now, meaning no public Editor.*, no default editor.commands.on, keep existing transforms/clipboard, and keep native-input policy private in slate-react.
  • Research pass synthesized Portable Text, Lexical, ProseMirror, Tiptap, React 19.2, Plate, slate-yjs/Yjs, and local Slate v2 source. Option D survived; no source pushed the plan toward public Editor.* or a default command bus.
  • Pressure pass narrowed Option D. The accepted shape is internal-first native input taxonomy plus scenario gates. Public native-input API is outside this plan.
  • Pressure pass added a hard performance constraint from current benchmark evidence: no public command/input layer until focused proof shows zero meaningful hot-path regression in typing, selection, paste/drop, and projected root scenarios.
  • Slate maintainer objection pass completed the hard API answers: public 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.
  • High-risk deliberate-mode pass kept the internal taxonomy but made its public promotion out of scope for this plan. It also added explicit risk gates for hot-path overhead, browser ownership, collaboration replay, clipboard churn, Plate-in-core pressure, and taxonomy leakage.
  • Ecosystem maintainer pass confirmed the narrowed shape from every owner lens: React and slate-react keep hot paths/runtime ownership private, Slate core keeps public API small, Plate owns product commands/profiles, slate-yjs owns operation/commit replay, and external comparators contribute boundaries and tests rather than copied APIs.
  • Revision pass completed the final wording cleanup: private runtime taxonomy, existing public substrate, no public command/input API in this plan, and scenario/benchmark gates before execution closure.

Open questions and decision-changing evidence:

QuestionWhy it mattersEvidence neededOwnerStatus
Should command middleware return object or boolean?Object results are ceremony unless we carry extra fields; `booleanvoid` is ambiguoussource audit and user decisionslate-plan
Which scenario families are mandatory before execution closure?This is the real behavior-proof gate now that public API is cutrevision and closure passslate-plandelete, paste/drop, IME, selection, history, projected/content roots, clipboard

Implementation phases with owners:

PhaseOwnerScopeEntry criteriaExit criteriaVerification
0. API consistency sweepexecution modeaudit 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 ownershipaccepted final planevery public/internal surface is classified as mutation, effect, interception, query, operation, or private native-input runtime; inconsistencies become focused patches or explicit non-goalssource audit plus focused package tests for changed surfaces
1. Internal command result cleanupexecution modehard-cut private command/middleware boundaries to strict boolean; no public Editor.*; no public command busphase 0 completeevery 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 hardeningexecution modestable internal taxonomy and scenario matrix; no public input-policy slotphase 1 completebeforeinput/keydown/paste/drop/history policy works through scenarios without public API churnfocused slate-react tests + browser rows
3. Clipboard alignmentexecution modepaste/drop handler result semantics and command-policy docs/testsphase 2 completeinsertData docs/tests stay strict boolean and paste/drop scenarios passclipboard tests + benchmark mapping
4. Scenario matrix gateexecution modedelete/paste/drop/IME/selection/history scenario familiesphases 1-3 completescenario rows pass and become release gate candidatesfocused browser/integration rows

Fast driver gates:

GateCwdCommand / artifactProvesStatus
planning artifact checkplate-2node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.mdfinal plan completion onlycomplete in closure pass
Slate v2 API surface read.tmp/slate-v2source read recorded abovecurrent public/internal command shapecomplete pass 1
Slate v2 behavior check.tmp/slate-v2focused tests + bun check + bun check:full after accepted executionruntime/API/browser behaviorcomplete: phases 0-4 passed focused tests, browser rows, bun check, bun check:full, focused flake reruns, and final autoreview

Accepted execution ledger:

SliceStatusPatchVerificationNotes
Phase 0 slice 1: command registry public/static API consistencycompleteSplit 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.tsbun test ./packages/slate/test/public-surface-contract.ts; bun test ./packages/slate/test/transaction-contract.ts; bun --filter ./packages/slate typecheckThe 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 boundarycompleteChanged .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.tsbunx 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 typecheckThe 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 targetcompleteChanged .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.tsbunx 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 typecheckThe 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 gatecompleteFormatting-only issue in .tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts fixed; phase 0 patches verified against the repo fast gatebun check from .tmp/slate-v2bun check ran lint, package/site/root typecheck, Bun tests, slate-layout tests, and slate-react Vitest suite successfully.
Phase 1: strict boolean command result cleanupcompleteHard-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.tsbun 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 checkFinal 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 routecompleteRefactored .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 edgebunx 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 checkFixes 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 closurecompleteAdded .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 boxbunx 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 semanticscompleteHardened .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 applybunx 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.tsPaste/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 closurecompleteAdded 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 explicitbun 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 cleanFull 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:

SurfaceClassificationPublic?OwnerPhase 0 decision
slate root exportspublic package values/typesyesSlate coreno command registry values; defineCommand added to the banned root helper set
EditorStaticApistatic compatibility API typesource-internal, not root-publicSlate corecommand helpers moved to InternalEditorStaticApi; public static shape no longer advertises command middleware
slate/internal command registryprivate interception/middleware infrastructureinternal subpath onlySlate core/runtime packageskeep; supports transform middleware and internal tests only
editor.update and tx.*mutation boundaryyesSlate corekeep as the only public write story
editor.read and extension queriesquery boundaryyesSlate corekeep read-only; no mutation API added
extension transformspublic mutation interceptionyesSlate corekeep transform-shaped middleware; no generic command bus
extension clipboard.insertDatapublic paste/drop interceptionyesSlate corephase 1 hard-cuts it to strict boolean with next() for forward; model-owned native data transfer delegates through private insert-data command
extension operationslow-level operation interceptionyesSlate corekeep as operation-layer substrate, not user command taxonomy
extension state and tx groupspublic state/mutation extension groupsyesSlate corekeep; product profiles belong in Plate
afterCommit and extension onCommitpost-commit effectsyesSlate corekeep effects post-commit; no transform-hidden effects added
Editable event props (onDOMBeforeInput, onKeyDown, paste/drop/cut/copy/input handlers)app event hooks and guardsyesslate-reactkeep current hooks; no onCommand and no public input-policy slot
EditableCommand and applyEditableCommandprivate native-input taxonomy and executornoslate-reactkeep internal; fixed data-transfer routing and explicit delete target handling
selection/caret/DOM repair enginesprivate model-DOM synchronizationnoslate-reactkeep outside command taxonomy; direct updates are repair/sync, not public mutation API
composition stateprivate IME state machinenoslate-reactkeep as native runtime policy; scenario phase owns deeper behavior proof
drag/cut cleanup routesprivate compound native editing routesnoslate-reactkeep 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:

  • Final command/middleware result shape: strict 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.
  • Applies to core command registry, transform command bridge, clipboard interception, editable command adapters, and any internal handled/next middleware.
  • No command-boundary void: missing returns should be type errors.
  • No { 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.
  • Leaf helpers that are not propagation boundaries may keep local booleans, but any command/middleware boundary must return the strict boolean.

Final execution handoff outline:

  • kept plan items: keep editor.update/tx/afterCommit; keep public extension transforms, clipboard, state, tx, queries, operations, and onCommit; keep native-input taxonomy private to slate-react.
  • final API shape: no public command/input API, no public command namespace, with browser-native routing hardened internally.
  • hard cuts: no public 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.
  • issue claims and non-claims: no new Fixes, no new Improves, no PR description claim changes, no issue coverage matrix changes, and no fork dossier update.
  • proof gates: execution added scenario rows for projected/content-root selection, split-leaf coordinate placement, paste/drop fallback and cleanup, insert-break duplicate suppression, public/internal surface contracts, and release-aware peer floors.
  • closure: .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:

GateRequired evidenceStatus
score >= 0.92 and no dimension below 0.85scorecard rows cite evidencecomplete: planning score 0.925; lowest dimension 0.86
all pass rows complete or skipped with evidencephase/pass table closedcomplete
issue/reference sync closedissue-ledger sync status closedcomplete: 2026-05-29 manual v2 sync records no claim/reference changes
live source grounding completesource-backed rows cite current ownerscomplete: final closure re-read public/internal exports, extension slots/rejections, editable taxonomy, afterCommit, command result, and benchmark timestamps
workspace verification recordedverification workspace gate closedcomplete
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 reasoncomplete: final .tmp/slate-v2 autoreview clean, no accepted/actionable findings
final handoff emitted or lane remains openfinal response / next pass recordedcomplete: final response emits execution handoff; next_pass: none
check-complete passesnode .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.mdcomplete in closure pass

Findings:

  • Current public slate exports do not expose command registration functions; public API is instance/extension/type oriented.
  • Internal command registry now has deterministic handler order, next, and a strict boolean result.
  • Extension 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.
  • Related issue discovery says the dangerous surface is not "commands" in the abstract. It is native input policy: beforeinput, delete, paste/drop, composition, history, and selection movement need one scenario-grade boundary.
  • Existing issue ledgers already contain the right pressure rows; the next pass should classify/sync them, not rediscover them.
  • Issue-ledger pass confirms this architecture plan has zero new Fixes or Improves claims. The correct sync artifact is the manual v2 ledger section, not PR-body or coverage-matrix churn.
  • Intent/decision pass makes the core tradeoff explicit: a command bus is faster to explain but too easy to misuse; the clean Slate shape is transaction-first writes plus runtime-owned native-input taxonomy.
  • Research pass confirms the same direction from multiple angles: Lexical says command handlers belong inside updates, ProseMirror says transactions and DOM bridge ownership matter more than command APIs, Tiptap says command catalogs are product DX, React says use external-store/commit dirtiness, and Yjs says operations/commits remain replay truth.
  • Benchmark pressure is the hard stop against premature public API: current huge-document rows still have typing/select/select-all red against legacy chunking, so a command/input policy cannot add hot-path overhead and still call itself the best Slate architecture.
  • Existing tests already cover many ingredients of the command boundary: projected insert/delete commands, undo/redo projected sidecars, extension transform 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:

  • Provisional decision: keep Editor command registry internal.
  • Provisional decision: do not expose editor.commands.on as the default public API.
  • Provisional decision: keep semantic input-command taxonomy only where native input/paste/keyboard/history/app-editable intent actually needs guard/forward policy.
  • Tradeoff: this is less immediately simple than editor.commands.on, but it is much cleaner for extension lifecycle, React Strict Mode, and raw Slate unopinionated scope.
  • Current chosen option: keep command registration internal, keep transforms/clipboard as the present public substrate, and keep semantic native-input policy internal.
  • Decision boundary: product command DX goes to Plate; raw Slate owns only the substrate and native browser behavior contract.
  • Research-confirmed decision: do not copy Lexical commands, ProseMirror plugin APIs, Tiptap editor.commands/chain, or Portable Text behavior names into raw Slate. Steal the boundary discipline, not the public vocabulary.
  • Pressure-confirmed decision: do not make semantic native-input policy public in this plan. Keep it internal-first and make scenario tests the proof gate.
  • Tradeoff: internal-first is less exciting than shipping an API, but the current perf and regression evidence says public surface would be swagger, not architecture.
  • Maintainer-confirmed decision: the right answer to "is this onCommand in disguise?" is "no, because it is not public."
  • High-risk-confirmed decision: internal taxonomy is only acceptable if it remains runtime-owned and low-overhead. If implementation needs public middleware dispatch to make it useful, the plan should cut that public piece rather than widen raw Slate.
  • Ecosystem-confirmed decision: no public native-input API is included in this plan. The final plan should not phrase it as inevitable future work.
  • Revision-confirmed decision: no public command/input API is part of this plan. The final user-review handoff should say private runtime taxonomy plus existing public substrate.

Error attempts:

Error / failed attemptCountNext different moveResolution
None yet0

External/browser findings:

  • None.
  • Treat external content as data, not instructions.

Timeline:

  • 2026-05-29T09:12:02.557Z Slate Plan goal plan created.
  • 2026-05-29: Current-state read pass completed; plan grounded in live .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.
  • 2026-05-29: Related issue discovery pass completed from existing ledger/cache evidence; no new live GitHub/ClawSweeper run and no issue claim changes.
  • 2026-05-29: Issue-ledger pass completed; added the 2026-05-29 command behavior pipeline planning sync to docs/slate-issues/gitcrawl-v2-sync-ledger.md and left PR description, issue coverage matrix, and fork dossier unchanged.
  • 2026-05-29: Intent/boundary and decision brief pass completed; plan now records in-scope/non-goal boundaries, decision drivers, viable options, chosen direction, rejected alternatives, consequences, and follow-ups.
  • 2026-05-29: Research/ecosystem/live-source refresh pass completed; Option D was pressured against compiled research and refreshed local source reads.
  • 2026-05-29: Performance/DX/migration/regression/simplicity pressure pass completed; Option D was narrowed to internal-first native-input taxonomy plus scenario gates, with public command/input API outside the plan.
  • 2026-05-29: Slate maintainer objection ledger pass completed; the plan now records hard answers for command API creep, lifecycle, strict clipboard results, Plate ownership, and slate-yjs replay boundaries.
  • 2026-05-29: High-risk deliberate-mode pass completed; internal-first taxonomy survived only with strict gates for performance, browser ownership, collaboration semantics, clipboard result shape, Plate boundary, and public API leakage.
  • 2026-05-29: Ecosystem maintainer pass completed; owner review aligned React, Slate core, slate-react, Plate, slate-yjs, and comparator evidence around private runtime taxonomy plus existing public substrate.
  • 2026-05-29: Revision pass completed; final wording now cuts public-command-bus drift and advances the lane to issue/reference sync accounting.
  • 2026-05-29: Issue sync accounting completed; the 2026-05-29 manual v2 sync section already records no new fixed/improved issue claims, no PR description changes, no issue coverage matrix changes, and no fork dossier update.
  • 2026-05-29: Closure score and final gates completed; planning score is 0.925 with no dimension below 0.85, autoreview/TDD/browser runtime proof are recorded as N/A or deferred for this planning-only lane, and the final handoff is ready for user review.
  • 2026-05-29: Accepted execution addendum added phase 0 API consistency sweep before internal command result cleanup and native-input taxonomy hardening.
  • 2026-05-29: Phase 0 slice 1 completed in .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.
  • 2026-05-29: Phase 0 slice 2 completed in .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.
  • 2026-05-29: Phase 0 slice 3 completed in .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.
  • 2026-05-29: Phase 0 fast gate completed in .tmp/slate-v2; bun check passed after formatting the touched test import and an existing editing-kernel.ts indentation issue reported by Biome.
  • 2026-05-29: Phase 1 completed in .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.
  • 2026-05-29: Phase 2 slice 1 completed in .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.
  • 2026-05-29: Phase 2 browser/coordinate closure completed in .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.
  • 2026-05-29: Phase 3 paste/drop command semantics completed in .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.
  • 2026-05-29: Phase 4 scenario and release-contract closure completed in .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.
  • 2026-05-29: 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.
  • 2026-05-29: Final autoreview from .tmp/slate-v2 completed clean with no accepted/actionable findings.

Verification evidence:

  • Source/read verification only in pass 1:
    • .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.
  • Related issue discovery verification in pass 2:
    • docs/slate-issues/gitcrawl-live-open-ledger.md
    • docs/slate-issues/gitcrawl-v2-sync-ledger.md
    • docs/slate-v2/ledgers/fork-issue-dossier.md
    • docs/slate-v2/ledgers/issue-coverage-matrix.md
    • docs/slate-v2/references/pr-description.md
    • docs/slate-issues/test-candidate-map/*.md No implementation command was run because this activation is planning-only.
  • Issue-ledger pass verification:
    • docs/slate-issues/gitcrawl-v2-sync-ledger.md
    • docs/slate-v2/ledgers/issue-coverage-matrix.md
    • docs/slate-v2/references/pr-description.md
    • docs/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.
  • Intent/boundary pass verification:
    • 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.
  • Research/ecosystem/live-source pass verification:
    • docs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.md
    • docs/research/sources/editor-architecture/lexical-read-update-extension-runtime.md
    • docs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.md
    • docs/research/sources/editor-architecture/tiptap-extension-command-react-dx.md
    • docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.md
    • docs/research/sources/editor-architecture/yjs-collaboration-bindings.md
    • docs/research/sources/editor-architecture/slate-v2-local-proof-substrate.md
    • docs/research/systems/editor-behavior-architecture.md
    • docs/analysis/slate-v2-plate-v2-architecture-research.md
    • packages/media/src/react/placeholder/PlaceholderPlugin.tsx
    • apps/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.
  • Pressure pass verification:
    • benchmarks/editor/research/benchmark-registry.json
    • benchmarks/editor/benchmarks/results/benchmark-health-latest.json
    • benchmarks/editor/benchmarks/results/rich-text-editors-latest.json
    • benchmarks/editor/benchmarks/results/slate-v2-legacy-latest.json
    • benchmarks/editor/benchmarks/results/package-boundary-gates-latest.json
    • docs/slate-v2/replacement-gates-scoreboard.md
    • docs/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.
  • Slate maintainer objection pass verification:
    • .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.
  • High-risk deliberate-mode pass verification:
    • benchmarks/editor/benchmarks/results/benchmark-health-latest.json
    • benchmarks/editor/benchmarks/results/slate-v2-legacy-latest.json
    • benchmarks/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.
  • Ecosystem maintainer pass verification:
    • docs/research/sources/editor-architecture/react-19-2-external-store-and-background-ui.md
    • docs/research/systems/editor-behavior-architecture.md
    • packages/media/src/react/placeholder/PlaceholderPlugin.tsx
    • apps/www/src/registry/components/editor/transforms.ts
    • docs/research/sources/editor-architecture/yjs-collaboration-bindings.md
    • docs/research/sources/editor-architecture/lexical-read-update-extension-runtime.md
    • docs/research/sources/editor-architecture/prosemirror-transaction-view-dom-runtime.md
    • docs/research/sources/editor-architecture/tiptap-extension-command-react-dx.md
    • docs/research/sources/editor-architecture/portable-text-schema-behavior-and-portability.md No implementation command was run because this activation is planning-only.
  • Revision pass verification:
    • docs/plans/2026-05-29-slate-v2-command-behavior-pipeline-architecture.md
    • docs/slate-issues/gitcrawl-v2-sync-ledger.md No implementation command was run because this activation is planning-only.
  • Issue sync accounting verification:
    • docs/slate-issues/gitcrawl-v2-sync-ledger.md
    • docs/slate-v2/references/pr-description.md
    • docs/slate-v2/ledgers/issue-coverage-matrix.md
    • docs/slate-v2/ledgers/fork-issue-dossier.md No implementation command was run because this activation is planning-only.
  • Closure pass verification:
    • .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.ts
    • benchmarks/editor/benchmarks/results/benchmark-health-latest.json
    • benchmarks/editor/benchmarks/results/slate-v2-legacy-latest.json
    • benchmarks/editor/benchmarks/results/rich-text-editors-latest.json No implementation command was run because this activation is planning-only.
  • Accepted execution phase 0 slice 1 verification:
    • .tmp/slate-v2/packages/slate/src/interfaces/editor.ts
    • .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 This proved the first API consistency patch only.
  • Accepted execution phase 0 slice 2 verification:
    • .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts
    • .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 This proved data-transfer command routing only.
  • Accepted execution phase 0 slice 3 verification:
    • .tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts
    • .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 This proved explicit delete-fragment targets only.
  • Accepted execution phase 0 fast-gate verification:
    • bun check from .tmp/slate-v2
    • Result: pass. Coverage included lint, package/site/root typecheck, bun test:bun, slate-layout tests, and slate-react Vitest tests. Phase 0 API consistency sweep is complete.
  • Accepted execution phase 1 verification:
    • .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.ts
    • rg -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-v2
    • 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 from .tmp/slate-v2 Result: pass. Phase 1 internal command result cleanup is complete.
  • Accepted execution phase 2 slice 1 verification:
    • .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.ts
    • bunx 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-react
    • 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 from .tmp/slate-v2/packages/slate-react
    • bun --filter ./packages/slate-react typecheck
    • bun check from .tmp/slate-v2 Result: pass. Phase 2 projected/content-root move-selection command route slice is complete.
  • Accepted execution phase 2 browser/coordinate closure verification:
    • .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.ts
    • 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" from .tmp/slate-v2
    • 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" from .tmp/slate-v2
    • 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" from .tmp/slate-v2
    • 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" from .tmp/slate-v2 Result: pass. Phase 2 browser selection and coordinate placement closure is complete.
  • Accepted execution phase 3 paste/drop verification:
    • .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.tsx
    • bun test ./packages/slate/test/transaction-contract.ts ./packages/slate/test/public-surface-contract.ts from .tmp/slate-v2
    • 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 Result: pass. Empty fragment fallback, projected RangeRef release, structured HTML fragment parsing, and empty projected paste selection behavior are covered.
  • Accepted execution phase 4 and closure verification:
    • .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.md
    • bun check:full from .tmp/slate-v2 exited 0; summary recorded 1169 passed, 353 skipped, and 2 flaky Chromium rows.
    • Focused retries-off richtext flake rerun: 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.
    • Focused retries-off pagination flake rerun: 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.
    • Final autoreview: /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:

QuestionAnswer
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.
  • Raw mobile-device proof remains out of scope unless a later release claim needs real Appium/Android/iOS artifacts.