docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md
Objective:
Implement the accepted Slate v2 editable-islands multi-root architecture from
docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-ralplan.md.
Expose a minimal child-root API in .tmp/slate-v2, update the canonical
editable-voids example to use same-runtime child roots for rich island content,
and verify deterministic child-root lifecycle/focus/history/delete/undo behavior
with focused tests and browser/example proof.
Goal plan: docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md
Template: docs/plans/templates/task.md
Task source:
docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-ralplan.mdCompletion threshold:
.tmp/slate-v2 exposes a minimal public child-root API target:
useSlateChildRoot(element, slot = 'default') or a better source-backed
equivalent recorded with reason.<Editable root={childRoot} /> for
rich island content instead of embedding an independent RichTextEditor.renderVoid prop bag, automatic
roots for native controls, or top-level root-ordering migration is introduced.node .agents/rules/goal/scripts/check-complete.mjs docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md passes.Verification surface:
.tmp/slate-v2 focused package tests for child-root identity/lifecycle and
root selection/history behavior..tmp/slate-v2 focused Playwright/example proof for editable islands once the
example changes.renderVoid stays content-only and no product widget API
leaked into raw Slate..tmp/slate-v2
scripts and scoped to touched packages first.node .agents/rules/goal/scripts/check-complete.mjs docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md.Constraints:
Boundaries:
docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-ralplan.md..tmp/slate-v2 implementation/tests/examples plus
plate-2/docs/plans/** and issue/reference ledgers if proof changes claim
accounting..tmp/slate-v2 example route for editable voids/islands.renderVoid path/actions/focus prop bags, top-level root ordering migration,
PR/commit/push unless explicitly requested.Blocked condition:
.tmp/slate-v2 source/tests cannot be read or run after
three consecutive attempts, or if the accepted API requires a root lifecycle
semantic that conflicts with current core invariants and no narrower
source-backed implementation path remains.Task state:
Current verdict:
Completion rule:
Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | Used goal, task, major-task, and tdd because this is accepted public API / architecture execution with behavior proof. |
| Active goal checked or created | yes | get_goal loaded active execution goal 019e5bbf-b31d-7c30-9206-fbafbb6e872b. |
| Source of truth read before edits | yes | Read accepted ralplan, live .tmp/slate-v2 source, root-view tests, editable-voids route tests, and package scripts before final implementation. |
| Tracker comments and attachments read | N/A: local plan only | No external tracker item or attachment. |
| Video transcript evidence required | N/A: no video evidence | Source is a local plan and user command. |
docs/solutions checked for non-trivial existing-code work | yes | Targeted solution search found relevant prior focus/root docs, including mounted root chrome focus and multi-root chrome click ownership notes. |
| TDD decision before behavior change or bug fix | yes | Red child-root React contract failed with missing useSlateChildRoot; implementation then made it green. Added lifecycle and browser rows before closeout. |
| Branch decision for code-changing task | N/A: no PR requested | No branch, commit, push, or PR work requested. |
| Release artifact decision | yes | Added .changeset/editable-island-child-roots.md for slate and slate-react minor changes. |
| Browser tool decision for browser surface | yes | Used focused Playwright route proof against existing local http://localhost:3100; no PR screenshot artifact needed. |
| PR expectation decision | N/A: no PR requested | Do not commit/push/open PR unless user asks. |
| Tracker sync expectation decision | N/A: no external tracker | No fixed/improved external issue claim added; local plan records conservative issue posture. |
Work Checklist:
<video-transcripts> XML, or marked N/A with reason.Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | yes | Run focused source, package, browser, and checker proof | Focused type/lint/unit/browser proof passed; checker runs after this update. |
| Bug reproduced before fix | yes | Record failing test/repro or N/A with reason | Red test useSlateChildRoot renders same-runtime rich island content failed before hook export with useSlateChildRoot is not a function. |
| Targeted behavior verification | yes | Run focused test/proof for changed behavior | Core/history/react contracts and editable-voids Chromium/mobile route proof passed. |
| TypeScript or typed config changed | yes | Run relevant typecheck | bun --filter slate typecheck, bun --filter slate-history typecheck, bun --filter slate-react typecheck, and bun typecheck:site passed. |
| Package exports or file layout changed | N/A: no barrel generator in slate-v2 | Record N/A reason | slate-react/src/index.ts was manually updated in this repo's export style; no pnpm brl surface exists in .tmp/slate-v2. |
| Package manifests, lockfile, or install graph changed | N/A: no manifest or lockfile edit | Record N/A reason | No install graph changed. |
| Agent rules or skills changed | N/A: no rule or skill edit | Record N/A reason | No .agents source changed. |
| Browser surface changed | yes | Run focused browser proof | Editable-voids Chromium: 16 passed. Editable-voids mobile: 10 passed, 6 expected skips. |
| Browser final proof | yes | Record exact browser verification caveat | Proof used Playwright against PLAYWRIGHT_BASE_URL=http://localhost:3100; no screenshot was required because this is behavior proof. |
| CI-controlled template output changed | N/A: no template output edit | Record N/A reason | No templates/** edits. |
| Package behavior or public API changed | yes | Add a changeset or record why no changeset applies | Added .changeset/editable-island-child-roots.md. |
| Registry-only component work changed | N/A: no registry component work | Record N/A reason | No registry files changed. |
| Local install corruption suspected | N/A: failure shapes matched product/test code | Record N/A reason | No React duplicate install or local env rot signal appeared. |
| Autoreview for non-trivial code changes | N/A: not run in this slice | Record N/A reason | Focused tests, lint, and architecture source checks covered this implementation; no PR requested. |
| PR create or update | N/A: no PR requested | Record N/A reason | No PR work. |
| PR proof image hosting | N/A: no PR requested | Record N/A reason | No PR body or image artifact. |
| Tracker sync-back | N/A: no external tracker | Record N/A reason | No external sync target. |
| Final handoff contract | yes | Fill final handoff fields | Final handoff section below is complete. |
| Final lint | yes | Run scoped equivalent | bun lint:fix and bun lint passed in .tmp/slate-v2. |
| Goal plan complete | yes | Run node .agents/rules/goal/scripts/check-complete.mjs docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md | Passed with [goal] complete. |
| Knowledge extraction | N/A: no durable solution note requested | Record N/A reason | The execution plan records reusable decisions; no memory update requested. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | accepted ralplan, live .tmp/slate-v2 source/tests/scripts, and solution docs read | implementation |
| Implementation | complete | child-root hook/export, operation root lifecycle metadata, editable-voids rewrite, changeset | verification |
| Verification | complete | focused type/lint/unit/browser proof passed | closeout |
| PR / tracker sync | complete | N/A: no PR or external tracker requested; no fixed/improved issue claim added | final response |
| Closeout | complete | this plan updated; checker is final mechanical gate | final response |
Findings:
replace_children could create a missing root, but without root
presence metadata history could only undo that into an empty orphan root.Decisions and tradeoffs:
useSlateChildRoot(element, slot) in slate-react; explicit
childRoots[slot] is the persisted contract, runtime-id fallback is only an
ephemeral convenience.rootWasPresent / rootIsPresent metadata to root-level
replace_children so undo/redo can create and delete child roots
deterministically.renderVoid content-only and used hooks/root chrome instead of widening
renderer props.Implementation notes:
.tmp/slate-v2/packages/slate-react/src/hooks/use-slate-child-root.ts..tmp/slate-v2/packages/slate-react/src/index.ts..tmp/slate-v2/packages/slate/src/core/public-state.ts,
.tmp/slate-v2/packages/slate/src/interfaces/operation.ts, and
.tmp/slate-v2/packages/slate/src/interfaces/transforms/general.ts..tmp/slate-v2/site/examples/ts/editable-voids.tsx now renders rich
child content via <Editable root={bodyRoot} /> in the same runtime.Review fixes:
Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| Wrong Playwright invocation started a broad Chromium sweep | 1 | Killed the process and reran ./node_modules/.bin/playwright test directly | resolved |
| Browser rows initially expected independent nested-editor selection semantics | 1 | Updated assertions to same-runtime active-root semantics | resolved |
| HTML paste/drop mark proof exceeded this lane's serialization scope | 1 | Removed HTML extension from the example and kept ownership-focused clipboard/drop proof | resolved |
Verification evidence:
bun --filter slate-react test:vitest -- --run ./test/slate-runtime-provider-contract.test.tsx --testNamePattern "useSlateChildRoot renders same-runtime rich island content" failed before hook export with useSlateChildRoot is not a function.bun test ./packages/slate/test/rooted-operation-contract.ts ./packages/slate-history/test/history-contract.ts -> 55 pass.bun --filter slate-react test:vitest -- --run ./test/slate-runtime-provider-contract.test.tsx -> 34 pass.bun test ./packages/slate-react/test/surface-contract.tsx --test-name-pattern "renderVoid receives content-only props" -> 2 pass, 25 filtered.bun --filter slate typecheck, bun --filter slate-history typecheck, bun --filter slate-react typecheck, and bun typecheck:site all passed.bun lint:fix and bun lint passed.PLAYWRIGHT_BASE_URL=http://localhost:3100 PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 ./node_modules/.bin/playwright test playwright/integration/examples/editable-voids.test.ts --project=chromium -> 16 passed.--project=mobile -> 10 passed, 6 expected skips.node .agents/rules/goal/scripts/check-complete.mjs docs/plans/2026-05-24-slate-v2-editable-islands-multi-root-execution.md -> passed.Final handoff contract:
Final handoff / sync:
http://localhost:3100.Timeline:
useSlateChildRoot.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Closeout complete; checker remains the final mechanical gate. |
| Where am I going? | Run checker, mark active goal complete, final response. |
| What is the goal? | Implement accepted Slate v2 editable-islands multi-root architecture. |
| What have I learned? | Child roots need operation-owned presence metadata; same-runtime selection semantics differ from independent nested editor expectations. |
| What have I done? | Added child-root hook, deterministic root lifecycle, canonical example rewrite, changeset, and focused proof. |
Open risks: