docs/plans/2026-05-27-synced-blocks-sibling-selection.md
Objective:
Fix Slate v2 Synced Blocks selection/navigation so synced content roots behave
like ordinary sibling blocks for Shift+Arrow keyboard expansion and mouse
drag/click selection across root boundaries. Complete only when focused browser
repro rows for /examples/synced-blocks fail before the fix and pass after for
cross-boundary Shift+ArrowLeft/Right/Up/Down, Cmd/Meta+Shift boundary movement
where applicable, and mouse selection across before/synced/after blocks;
affected shared behavior remains green for /examples/hidden-content-blocks
and /examples/multi-root-document; focused Slate React/DOM package tests,
owning package typecheck/lint, Chromium Playwright proof, local autoreview, and
this plan's autogoal checker pass.
Flow mode: one-shot execution
Goal plan: docs/plans/2026-05-27-synced-blocks-sibling-selection.md
Template: docs/plans/templates/task.md
Primary template: docs/plans/templates/task.md
Applied packs:
Task source:
/examples/synced-blocksCompletion threshold:
.tmp/slate-v2 reports no accepted/actionable findings.node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-synced-blocks-sibling-selection.md passes.Verification surface:
.tmp/slate-v2/playwright/integration/examples/synced-blocks.test.ts
failing-then-passing rows for Shift+Arrow and mouse selection..tmp/slate-v2/playwright/integration/examples/hidden-content-blocks.test.ts
shifted-boundary focused row..tmp/slate-v2/playwright/integration/examples/multi-root-document.test.ts
affected focused rows.packages/slate-react/test/* contract tests for view-boundary,
content-root navigation, keyboard input, selection, projected commands, or
mouse/import behavior changed by this task..tmp/slate-v2: owning package typecheck, build if needed, scoped lint/format,
and local autoreview.plate-2: this plan and final autogoal checker.Constraints:
<Editable root>, slots.contentRoot, and
slots.contentBoundary.Boundaries:
.tmp/slate-v2 source/tests..tmp/slate-v2/packages/slate-react/**,
.tmp/slate-v2/packages/slate-dom/** if DOM selection/root helpers require
it, .tmp/slate-v2/playwright/integration/examples/**,
.tmp/slate-v2/site/examples/ts/synced-blocks.tsx only if fixture coverage
genuinely needs more data, .tmp/slate-v2/.changeset/**, and this plan./examples/synced-blocks, plus regression proof on
/examples/hidden-content-blocks and /examples/multi-root-document.ViewSelection API exposure.Blocked condition:
.tmp/slate-v2 verification cannot run because of a persistent environment
blocker that survives the repo-approved retry.Task state:
Current verdict:
.tmp/slate-v2Completion rule:
update_goal(status: complete) while any required checklist item
remains unchecked. If an item does not apply, check it and add N/A: <reason>.update_goal(status: complete) until every completion threshold
above is satisfied, final handoff evidence is recorded, and
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-synced-blocks-sibling-selection.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | User invoked autogoal; loaded autogoal, changeset, and review rules during the lane. |
| Active goal checked or created | yes | get_goal confirmed active Synced Blocks sibling-selection goal for docs/plans/2026-05-27-synced-blocks-sibling-selection.md. |
| Source of truth read before edits | yes | Read latest prompt, active goal, prior unified view-boundary context, Synced Blocks example/tests, hidden-content tests, multi-root tests, and shared selection/navigation files. |
| Tracker comments and attachments read | no | N/A: no tracker item. |
| Video transcript evidence required | no | N/A: no video evidence. |
docs/solutions checked for non-trivial existing-code work | yes | Searched solution notes for content-root, view-selection, Shift+Arrow, mouse selection, multi-root, and DOM selection ownership. |
| TDD decision before behavior change or bug fix | yes | Add failing Synced Blocks Playwright rows before runtime edits; browser proof is the primary behavior guard. |
| Branch decision for code-changing task | no | N/A: no branch/PR requested and repo says no proactive git hygiene. |
| Release artifact decision | yes | Published slate-react runtime behavior changed; added .tmp/slate-v2/.changeset/synced-block-selection.md. |
| Browser tool decision for browser surface | yes | Use owning .tmp/slate-v2 Playwright Chromium rows for route proof. |
| PR expectation decision | no | N/A: no PR requested. |
| Tracker sync expectation decision | no | N/A: no tracker item. |
| Browser pack selected | yes | browser pack applied. |
| Browser route / app surface identified | yes | /examples/synced-blocks, with hidden-content and multi-root regression routes. |
| Browser tool decision recorded | yes | Playwright integration tests are the owning browser proof for .tmp/slate-v2. |
| Console/network caveat policy recorded | yes | Runtime/page errors fail targeted rows when relevant; final evidence records console/network caveat. |
| Package/API pack selected | yes | package-api pack applied. |
| Public surface or package boundary identified | yes | slate-react runtime behavior is in scope; slate-dom only if DOM selection bridge needs edits. Public API should stay stable. |
| Release artifact path selected | yes | .tmp/slate-v2/.changeset/** if package runtime behavior changes. |
changeset skill loaded when .changeset is required | yes | Loaded /Users/zbeyens/git/plate-2/.agents/skills/changeset/SKILL.md; changeset uses one package, patch bump, user-impact wording. |
| Barrel/export impact decision recorded | yes | No package export or exported file-layout change; pnpm brl N/A. |
Work Checklist:
slate-react patch changeset..tmp/slate-v2; autogoal checker runs in plate-2..tmp/slate-v2 diff..agents, .claude, .codex, skill,
hook, command, prompt, or user-action tooling changed..changeset/synced-block-selection.md added for
slate-react patch behavior delta.changeset skill loaded and prose checked.Completion Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Named verification threshold | yes | Focused Synced Blocks rows passed; full affected Chromium sweep passed 37/37; package contracts, typecheck, lint, autoreview, changeset, and plan checker are recorded. |
| Bug reproduced before fix | yes | Pre-fix browser rows failed: repeated Shift+ArrowDown stayed at [0,0] instead of moving to [1,0]; mouse drag across root boundary produced native "\n"/no view selection; Cmd+Shift reverse wrote selection to the child copy instead of shared runtime. |
| Targeted behavior verification | yes | `PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/synced-blocks.test.ts --project=chromium --grep "keeps ordinary Shift\+Arrow |
| TypeScript or typed config changed | yes | bun --filter slate-react typecheck && bun typecheck:site passed in .tmp/slate-v2. |
| Package exports or file layout changed | no | N/A: no exports or exported file layout changed; pnpm brl not needed. |
| Package manifests, lockfile, or install graph changed | no | N/A: no manifests, lockfile, or install graph changed. |
| Agent rules or skills changed | no | N/A: no agent rules or skills changed. |
| Workspace authority proof | yes | All Slate runtime/browser checks ran from .tmp/slate-v2; autogoal checker runs from plate-2. |
| Browser surface changed | yes | Synced Blocks example changed; Chromium integration proof passed on target and sibling-risk routes. |
| Browser final proof | yes | Exact browser command below passed 37/37; no screenshot needed because repo-owned Playwright is the authoritative interaction proof. |
| CI-controlled template output changed | no | N/A: no template output touched. |
| Package behavior or public API changed | yes | Runtime behavior changed without public API change; slate-react patch changeset added. |
| Registry-only component work changed | no | N/A: no registry-only component work. |
| Docs or content changed | yes | This plan only; claims are command-backed and route-backed. |
| High-risk mini gate | yes | Failure mode was stale or wrong projected selection across root views; proof covers view selection, focus, history, keyboard, mouse, typecheck, browser, and autoreview. |
| Agent-native review for agent/tooling changes | no | N/A: no agent/tooling changes. |
| Local install corruption suspected | no | N/A: no local-env-rot failure shape appeared. |
| Autoreview for non-trivial implementation changes | yes | PYTHONUNBUFFERED=1 /Users/zbeyens/git/plate-2/.agents/skills/autoreview/scripts/autoreview --mode local --no-web-search --thinking low --output /tmp/slate-v2-autoreview.txt --json-output /tmp/slate-v2-autoreview.json passed clean after accepted findings were fixed. |
| PR create or update | no | N/A: no PR requested. |
| PR proof image hosting | no | N/A: no PR body. |
| Tracker sync-back | no | N/A: no tracker item. |
| Final handoff contract | yes | Filled below with outcome, proof, review, and caveats. |
| Final lint | yes | bun biome check --fix on 15 touched files passed with no fixes on final run. |
| Goal plan complete | yes | node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-synced-blocks-sibling-selection.md to run after this update. |
| Browser interaction proof | yes | Full affected Chromium sweep passed 37/37. |
| Browser console/network check | yes | Existing runtime-error guards cover relevant Synced Blocks focus/path failures; final sweep reported no failing browser/runtime rows. |
| Browser final proof artifact | yes | Command output is the final proof artifact; no visual screenshot required for this behavior test. |
| Public API / package boundary proof | yes | Source audit: public <Editable root>, slots.contentRoot, and slots.contentBoundary stay intact; no exported barrels changed. |
| Release artifact classification | yes | Published runtime behavior delta in slate-react; not registry-only, docs-only, or agent-only. |
| Published package changeset | yes | .tmp/slate-v2/.changeset/synced-block-selection.md uses "slate-react": patch; no forbidden minor on core packages. |
| Registry changelog | no | N/A: no registry-only work. |
| No release artifact | no | N/A: release artifact exists. |
| Package typecheck/build/test | yes | 64 Slate React contract tests passed; slate-react and site typechecks passed. |
| Barrel/export generation | no | N/A: no exports or exported file layout changed. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | Read prompt, active goal, prior plans, examples, tests, and selection/navigation internals. | implementation complete |
| Implementation | complete | Shared view-selection store, owner-aware content-root navigation, mouse projected selection import, focus preservation, root-chrome cleanup, Synced Blocks fixture, tests, and changeset landed. | verification complete |
| Verification | complete | Unit/typecheck/lint/browser/autoreview gates passed. | closeout |
| PR / tracker sync | N/A | No PR or tracker requested. | final response |
| Closeout | complete | Plan updated; checker is the final remaining command. | final response |
Findings:
Decisions and tradeoffs:
<Editable root> plus
content-root slots.tabIndex = 0) because
mouse and keyboard parity requires real focusable editing surfaces.Implementation notes:
view-selection.ts keys read/write through setSlateViewSelectionStoreKey.SlateRuntimeView and useSlateRootEditor register view editors under their
runtime editor key.content-root-navigation.ts supports repeated-root owner identity, horizontal
and vertical projected extension, document-boundary Meta+Shift movement, and
disables fresh root-local projected fallback unless extending an existing
projected selection.selection-controller.ts imports cross-root mouse DOM endpoints into
projected selections before nested-focus early returns.browser-handle.ts refocuses the attached editor element after model-owned
insert/undo/redo commands so active synced copies keep focus.focus-slate-editable.ts, keyboard-input-strategy.ts, and
runtime-before-input-events.ts preserve projected selections during internal
focus repair.site/examples/ts/synced-blocks.tsx lets the block shell stay selectable and
makes toolbar chrome user-select: none.Review fixes:
allowRootLocalMovement gated on an
existing projected selection; added normal paragraph Shift+Arrow browser row.useSlateRootChrome contract.focusSlateEditable contract.Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| Synced Blocks padding-coordinate Playwright row did not hit root interaction controller | 1 | Move stale-sidecar proof to useSlateRootChrome unit contract | Removed brittle row; unit contract now covers actual branch. |
| Default autoreview hung with no structured output | 2 | Keep Codex engine but lower reasoning effort and write output files | --thinking low produced one P2, then clean after fix. |
Verification evidence:
.tmp/slate-v2/packages/slate-react: bun test:vitest test/content-root-navigation-contract.test.ts test/focus-slate-editable-contract.test.ts test/keyboard-input-strategy-contract.test.ts test/view-boundary-graph-contract.test.ts test/view-selection-contract.test.ts test/projected-command-contract.test.ts test/projected-clipboard-contract.test.ts test/use-slate-history.test.tsx test/root-interaction-resolver.test.ts test/browser-handle-contract.test.ts test/target-runtime-contract.tsx test/use-slate-root-chrome.test.tsx passed: 11 files, 64 tests..tmp/slate-v2: bun --filter slate-react typecheck && bun typecheck:site passed..tmp/slate-v2: bun biome check --fix packages/slate-react/src/editable/browser-handle.ts packages/slate-react/src/editable/content-root-navigation.ts packages/slate-react/src/editable/root-interaction-controller.ts packages/slate-react/src/editable/selection-controller.ts packages/slate-react/src/editable/keyboard-input-strategy.ts packages/slate-react/src/editable/runtime-before-input-events.ts packages/slate-react/src/hooks/focus-slate-editable.ts packages/slate-react/src/view-selection.ts packages/slate-react/src/components/slate.tsx packages/slate-react/src/hooks/use-slate-runtime.tsx packages/slate-react/src/components/editable-text-blocks.tsx packages/slate-react/test/focus-slate-editable-contract.test.ts packages/slate-react/test/use-slate-root-chrome.test.tsx site/examples/ts/synced-blocks.tsx playwright/integration/examples/synced-blocks.test.ts passed: 15 files checked, no fixes on final run..tmp/slate-v2: debug-marker scan over touched files returned no matches..tmp/slate-v2: focused Synced Blocks parity grep passed 5/5; additional word-extension row passed 1/1..tmp/slate-v2: PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bun playwright playwright/integration/examples/synced-blocks.test.ts playwright/integration/examples/hidden-content-blocks.test.ts playwright/integration/examples/multi-root-document.test.ts --project=chromium passed 37/37..tmp/slate-v2: PYTHONUNBUFFERED=1 /Users/zbeyens/git/plate-2/.agents/skills/autoreview/scripts/autoreview --mode local --no-web-search --thinking low --output /tmp/slate-v2-autoreview.txt --json-output /tmp/slate-v2-autoreview.json passed clean: no accepted/actionable findings..tmp/slate-v2: .changeset/synced-block-selection.md added for slate-react patch.Final handoff contract:
Final handoff / sync:
Timeline:
useSlateRootChrome stale-sidecar contract.focusSlateEditable contract.slate-react and site typechecks passed.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Closeout, after implementation and verification. |
| Where am I going? | Run the autogoal checker, mark goal complete, final response. |
| What is the goal? | Make Synced Blocks select/navigate like sibling blocks while preserving hidden-content and multi-root behavior. |
| What have I learned? | See Findings and Decisions. |
| What have I done? | Implemented shared projected selection ownership, owner-aware navigation, mouse import, focus preservation, root-chrome cleanup, tests, changeset, and review fixes. |
Open risks: