docs/plans/2026-04-02-editor-behavior-major-execution.md
| Phase | Status | Notes |
|---|---|---|
| Ground approved PRD + test spec | complete | .omx plan is the execution contract |
| Mine existing learnings + current test seams | complete | blockquote/container lessons loaded |
| Batch 1 red tests | complete | quote/list structural ownership locked |
| Batch 1 implementation | complete | liftBlock seam + blockquote rewiring landed |
| Batch 2 tab ownership cleanup | complete | plain and quoted paragraph Tab stay editor-owned through indent; reverse Tab exhausts paragraph indent before quote lift |
| Existing-feature markdown-native parity batch | complete | table round-trip, nested quote coverage, heading coverage, ordered-list restart coverage, image/title fixes, and affinity policy landed |
| Full existing-feature matrix expansion | complete | broader existing-feature gate was reopened, then closed after the block-editor-native, styling/layout, media/caption, and collaboration lanes were either covered or explicitly deferred |
| Verification + review | complete | package tests/build/typecheck/lint and browser checks green; apps/www full type lane remains separately noisy |
This is a historical execution note.
Current gate truth lives in docs/editor-behavior/markdown-parity-matrix.md, which currently says the active major-release gate is closed and the remaining lanes are release-prep or later-release follow-up.
exit:
exit inserts a sibling paragraph after the containerEnter, quote start-Backspace, and quote Shift+Tab need to lift the current block out of one quote level insteadBackspace@start needed one more guard after the container rewrite:
Backspace@start needed a real plugin rule instead of generic Slate merge behavior:
Backspace already removed list first, then quoteBackspace already stayed inside the current cellBackspace behavior were already correct once directly tested* bullets on serializeEnter behavior in withToggle.ts with almost no direct coverageunwrapNodes({ split: true }) already gives the correct one-level quote split behavior when wrapped in a focused transform.critical-patterns.md lookup does not exist in this repo. Use real local docs/solutions/**, docs/editor-behavior/**, and docs/plans/** artifacts instead.Enter / Backspace / Tab arbitration@platejs/core@platejs/basic-nodes@platejs/indent@platejs/list@platejs/code-block@platejs/table@platejs/markdownapps/www type lane still shows unrelated platejs export drift when included, but that is outside the current diff and not a blocker for this package-scoped seam.Enter on a top-level quoted paragraph exits the quoteEnter on a nested quoted paragraph exits one quote level and lands at the parent quote depthralph, major-task, planning-with-files, tdd, and learnings-researcher instructions..omx PRD and test-spec artifacts.docs/plans/4898-blockquote-markdown-first.mddocs/solutions/logic-errors/2026-04-01-markdown-blockquotes-must-round-trip-as-container-blocks.mddocs/solutions/ui-bugs/2026-04-02-blockquote-transforms-must-keep-selection-inside-the-new-quote.mddocs/solutions/ui-bugs/2026-04-02-blockquote-autoformat-must-wrap-nested-quotes.mdeditor.tf.liftBlock(...) and rewired quote Enter/Backspace to use one-level lift semantics.Tab ownership to follow Typora-style editor-owned paragraph indent instead of letting plain Tab fall out of the editor.Shift+Tab behavior through BlockquotePlugin and updated public docs + changesets for the new lift action.Tab ownership so indent only claims it when a paragraph indent exists; otherwise quoted paragraphs fall through to quote lift.pnpm build, and pnpm lint:fix.http://localhost:3001/blocks/editor-ai.Tab stay editor-owned and add paragraph indent on http://localhost:3001/blocks/editor-ai.Backspace@start on empty quoted paragraphs and narrowed blockquote delete matching so only first/only empty quoted paragraphs lift; empty non-first quoted paragraphs now delete in place inside the quote.Backspace@start with a real BaseH1Plugin seam and wired headings to delete.start: 'reset' so one ⌫ resets the heading before any merge.⌫, empty whole code-block ⌫, and table-cell ⌫; all three behaviors were already correct, so this slice tightened the matrix instead of changing package code.packages/markdown.* bullets.packages/markdown/src/lib/commonmarkSurface.spec.ts.packages/markdown/src/lib/defaultRules.spec.ts.packages/markdown/src/lib/commonmarkSurface.spec.ts.AffinityPlugin evidence for links and marks so the remaining affinity row is framed as a profile decision instead of fake missing tests.BaseBoldPlugin and BaseItalicPlugin directional by default and added direct AffinityPlugin coverage for those defaults.node.title instead of being mirrored from the caption, then updated package/app tests to match the new canonical output.commonmarkSurface.spec.ts and defaultRules.spec.ts into the final release-blocking matrix for plain links, images, marks, hard breaks, nested blockquotes, and image attribute precedence.packages/table/src/lib/withApplyTable.spec.tsx.Enter behavior coverage in packages/toggle/src/react/withToggle.spec.tsx for open and closed toggles.Enter inside a cell and making the multi-paragraph cell markdown policy explicit: serialize as , deserialize back to one paragraph with an inline break instead of pretending plain markdown tables can round-trip block children.packages/basic-styles.↵, non-empty ↵, and start ⌫ in packages/core.packages/markdown/src/lib/mediaSurface.spec.ts.packages/media/src/lib/BaseMediaPluginContracts.spec.ts and expanded caption movement coverage in packages/caption/src/lib/withCaption.spec.tsx.propsToAttributes so MDX attribute value expressions are preserved instead of being JSON-stringified during media serialization.packages/markdown/src/lib/columnSurface.spec.ts and removed the duplicate audit column rows from the editing spec.