docs/plans/2026-06-15-4835-markdown-linebreak-serialization.md
Objective: Complete PR #4835 markdown linebreak serialization fix; done when reproduced, package fix verified, PR opened and tracker credited.
Goal plan: docs/plans/2026-06-15-4835-markdown-linebreak-serialization.md
Template: docs/plans/templates/task.md
Primary template: docs/plans/templates/task.md
Applied packs:
Task source:
\n within a text child of a paragraph as line breakCompletion threshold:
main losing embedded paragraph hard breaks.@platejs/markdown package surface.@platejs/markdown patch changeset.pnpm check, and autoreview.node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/2026-06-15-4835-markdown-linebreak-serialization.md passes.Verification surface:
bun test packages/markdown/src/lib/commonmarkSurface.spec.ts.bun test apps/www/src/__tests__/package-integration/ai-chat-streaming/streamSerializeMd.slow.tsx.pnpm turbo typecheck --filter=./packages/markdown.pnpm lint:fix..agents/skills/autoreview/scripts/autoreview --mode local.pnpm check.gh pr view --json body and PR #4835 comment readback.Constraints:
Boundaries:
packages/markdown, focused package/app integration tests, one .changeset, this plan.Output budget strategy:
gh, sed, rg, and focused tests. Cap broad output. The first docs/solutions search was too broad and is recorded in Error attempts; later searches stay narrowed to markdown/AI streaming owner paths.Blocked condition:
Task state:
Current verdict:
Pre-solution issue challenge:
\n\n\n serializes as raw blank lines, and deserializing that markdown returns separate paragraphs.\n into break nodes before mdast conversion; reject changing AI streaming chunk outputs and avoid speculative test commentary.bun -e against current source produced raw blank-line markdown and round-tripped into two paragraphs.packages/markdown/src/lib/rules/defaultRules.ts.Completion rule:
update_goal(status: complete) while any required checklist item
remains unchecked. If an item does not apply, check it and add N/A: <reason>.update_goal(status: complete) until every completion threshold
above is satisfied, final handoff evidence is recorded, and
node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/2026-06-15-4835-markdown-linebreak-serialization.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | Read task, autogoal, autoreview, tdd, changeset, and git-commit-push-pr skills. |
| Active goal checked or created | yes | Active goal created for PR #4835 completion. |
| Source of truth read before edits | yes | Read PR #4835, PR diff, review comments API result, and linked discussion #4834. |
| Tracker comments and attachments read | yes | No PR review comments; discussion #4834 read; no attachments/video. |
| Video transcript evidence required | no | N/A: no video or screenshot evidence in the source. |
| Pre-solution issue challenge required | yes | Verdict recorded above: partially valid. |
| Reproduction verdict before implementation | yes | Source-level repro showed serialized raw blank lines round-trip into separate paragraphs. |
| Repro escalation ladder selected | yes | Source/package test is the honest surface; browser/visual proof N/A. |
| Suggested fix reviewed against durable boundary | yes | Keep markdown normalization idea; reject AI output change from original draft. |
docs/solutions checked for non-trivial existing-code work | yes | Searched narrowed markdown/streaming prior-solution paths after one broad noisy search. |
| TDD decision before behavior change or bug fix | yes | TDD used: failing markdown tests added before implementation. |
| Branch decision for code-changing task | yes | Branch codex/4835-markdown-linebreak-serialization. |
| Release artifact decision | yes | .changeset entries for @platejs/markdown and @platejs/ai. |
| Browser tool decision for browser surface | yes | N/A: package serialization, no honest browser-only surface. |
| PR expectation decision | yes | Create replacement PR from this branch; do not force-push contributor fork. |
| Tracker sync expectation decision | yes | Comment back on #4835 after replacement PR exists. |
| Output budget strategy recorded | yes | Targeted commands; broad output mistake recorded. |
| Package/API pack selected | yes | Package/API pack applies. |
| Public surface or package boundary identified | yes | Published @platejs/markdown serializer behavior and @platejs/ai streaming runtime behavior. |
| Release artifact path selected | yes | .changeset selected. |
changeset skill loaded when .changeset is required | yes | Loaded changeset skill and .agents/rules/changeset.mdc. |
| Barrel/export impact decision recorded | yes | N/A: no exports or file layout changed; pnpm brl not required. |
Work Checklist:
<video-transcripts> XML, or marked N/A with reason.valid, not reproduced, invalid,
wont-fix, partially valid, or platform limitation. Feature, docs,
support, or cleanup requests with no bug claim may mark reproduction
N/A with reason.[@Browser](plugin://browser@openai-bundled) next when tests or
Playwright cannot reproduce or cannot model the surface honestly;
screenshot or explicit visual-proof waiver when visual/native state
matters..agents/**, .claude/**,
.codex/**, skills, hooks, commands, prompts, or user-action tooling..changeset, registry changelog, or explicit no-artifact reason..changeset work loads changeset and follows its package/version/prose rules.registry-changelog pack instead of adding a package changeset.main.Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | yes | Run named proof commands | pnpm check passed; focused tests and typecheck passed. |
| Pre-solution issue challenge verdict | yes | Record challenge before implementation | Recorded above: partially valid, pivoted to narrower durable fix. |
| Repro escalation ladder | yes | Record source/browser/visual outcomes | Source-level repro complete; browser/visual N/A. |
| Bug reproduced before fix | yes | Record failing repro | bun -e repro and red commonmarkSurface.spec.ts assertions showed raw blank lines split the paragraph. |
| Targeted behavior verification | yes | Run focused tests | bun test packages/markdown/src/lib/commonmarkSurface.spec.ts; AI streaming focused suite passed. |
| TypeScript or typed config changed | yes | Run relevant typecheck | pnpm turbo typecheck --filter=./packages/markdown --filter=./packages/ai passed. |
| Package exports or file layout changed | no | pnpm brl if needed | N/A: no exports or file layout changed. |
| Package manifests, lockfile, or install graph changed | yes | Run pnpm install and checks | Added @platejs/table devDependency for existing markdown table test import; pnpm install and package/full checks passed. |
| Agent rules or skills changed | no | Sync if needed | N/A: no .agents rule or skill source changed. |
| Workspace authority proof | yes | Run proof in owning repo | All commands ran in /Users/zbeyens/git/plate. |
| Browser surface changed | no | Browser proof or waiver | N/A: serializer/runtime package behavior, not browser-only UI. |
| Browser final proof | no | Screenshot or caveat | N/A: no visual/native state. |
| CI-controlled template output changed | no | Restore or justify | N/A: no templates touched. |
| Package behavior or public API changed | yes | Add changeset | Added patch changesets for @platejs/markdown and @platejs/ai. |
| User-visible registry output changed | no | Registry changelog pack or N/A | N/A: no registry component output changed. |
| Docs or content changed | no | Docs verification or N/A | N/A: only task plan docs changed. |
| High-risk mini gate | yes | Record failure mode and proof | Risk: serializer hard-break normalization could break AI stream exactness; proof: expanded streaming matrix and autoreview clean. |
| Agent-native review for agent/tooling changes | no | Agent-native review or N/A | N/A: no agent/tooling action surfaces changed. |
| Local install corruption suspected | yes | Reinstall once and rerun | pnpm run reinstall tried after package typecheck missing @platejs/table; failure persisted, so manifest dependency fixed. |
| Autoreview for non-trivial implementation changes | yes | Run until clean | Four accepted AI-streaming findings fixed; final autoreview clean. |
| PR create or update | yes | Run check before PR and sync body | Created https://github.com/udecode/plate/pull/5026 after pnpm check passed. |
| Task-style PR body verified | yes | Verify gh pr view --json body | gh pr view 5026 --json url,state,title,body confirmed auto-release block plus task-style body. |
| PR proof image hosting | no | Hosted proof if needed | N/A: no browser proof image. |
| Tracker sync-back | yes | Comment on #4835 after PR exists | Commented https://github.com/udecode/plate/pull/4835#issuecomment-4710485850 and closed #4835 as superseded. |
| Final handoff contract | yes | Fill PR/tracker lines | Filled below with PR #5026 and tracker #4835. |
| Final lint | yes | Run pnpm lint:fix | Passed; no fixes applied. |
| Output budget discipline | yes | Record broad output and recovery | One broad search/output mistake recorded; later commands targeted/capped. |
| Goal plan complete | yes | Run autogoal checker | Running as final closeout gate. |
| Public API / package boundary proof | yes | Source-audit public API, exports, package impact | No API shape/export change; published runtime behavior changes in markdown serializer and AI streaming. |
| Release artifact classification | yes | Record artifact class | Published package runtime behavior change. |
| Published package changeset | yes | Add changesets | .changeset/markdown-text-leaf-line-breaks.md, .changeset/ai-streaming-hard-breaks.md; no forbidden minors. |
| Registry changelog | no | Registry-only flow if applies | N/A: no registry output changed. |
| No release artifact | no | Explain no artifact | N/A: published package deltas exist. |
| Package typecheck/build/test | yes | Run owning checks | pnpm turbo typecheck --filter=./packages/markdown --filter=./packages/ai; pnpm --filter @platejs/markdown test; pnpm check. |
| Barrel/export generation | no | Run pnpm brl if exports changed | N/A: no barrel impact. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | PR #4835, diff, comments, discussion #4834 read. | implementation |
| Implementation | complete | Markdown serializer normalization plus AI streaming compatibility shim implemented. | verification |
| Verification | complete | Focused suites, package typecheck, lint, autoreview, and pnpm check passed. | PR / tracker sync |
| PR / tracker sync | complete | PR #5026 created; #4835 commented and closed as superseded. | closeout |
| Closeout | complete | Plan checker is the final local gate before goal completion. | final response |
Findings:
\n serializes to markdown that deserializes as separate paragraphs.packages/markdown/src/lib/table.spec.ts imported @platejs/table; package typecheck exposed that packages/markdown/package.json lacked the devDependency.Decisions and tradeoffs:
@platejs/markdown can serialize correct markdown while @platejs/ai preserves stream chunks.Implementation notes:
normalizeParagraphLineBreaks in packages/markdown/src/lib/rules/defaultRules.ts splits embedded text \n into existing break nodes before mdast conversion.streamSerializeMd temporarily replaces embedded text-leaf newlines with a private placeholder before markdown serialization, then restores them; explicit hard-break children remain explicit markdown hard breaks.@platejs/table as a markdown devDependency because markdown package tests import table code.Review fixes:
endsWith('\\') cleanup could corrupt literal backslashes. Replaced with exact trailing-whitespace suffix cleanup.space + newline + space. Generalized suffix handling and added coverage.Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
Broad rg/source lookup produced too much output | 1 | Narrow to markdown/AI streaming owner paths and cap output | Recovered; evidence recorded without relying on noisy output. |
First AI streaming focused test command omitted ./ path prefix | 1 | Rerun with explicit relative paths | Rerun passed after implementation. |
bun stdin probe used unsupported command shape | 1 | Rerun with bun -e | Probe completed. |
Package typecheck failed on missing @platejs/table import | 2 | Run reinstall once, then fix manifest if persistent | pnpm run reinstall did not fix; added devDependency and reran checks. |
Verification evidence:
bun -e showed current source serialized Text followed...\n\n\nFollowed... as raw blank lines and deserialized into two paragraphs.bun test packages/markdown/src/lib/commonmarkSurface.spec.ts failed for embedded text-leaf hard breaks and trailing text-leaf hard break parity.bun test packages/markdown/src/lib/commonmarkSurface.spec.ts: passed, 14 tests.bun test ./apps/www/src/__tests__/package-integration/ai-chat-streaming/streamSerializeMd.slow.tsx ./apps/www/src/__tests__/package-integration/ai-chat-streaming/streamDeserializeMd.slow.tsx: passed, 18 tests.pnpm turbo typecheck --filter=./packages/markdown --filter=./packages/ai: passed.pnpm --filter @platejs/markdown test: passed, 233 tests.pnpm lint:fix: passed, no fixes applied..agents/skills/autoreview/scripts/autoreview --mode local: final pass clean.pnpm check: passed. Notes: existing eslint warning in apps/www/src/components/ui/sidebar.tsx; known multiple @platejs/core diagnostic printed during tests, but command exited 0.git diff --check: passed.Final handoff contract:
bun -e repro and red commonmarkSurface.spec.ts; browser โ N/Apnpm check; browser โ N/A@platejs/markdown, with a targeted @platejs/ai stream compatibility layer.gh pr view 5026 --json url,state,title,body confirmed task-style body and auto-release block.Task-style PR body contract:
<!-- auto-release:start --> block. If a changeset is
part of the diff and repo policy expects auto release, include that block.๐ Fixes #123 or ๐ Fixes โ N/A, then
an emoji confidence line like ๐ข 95-100% confidence.| Phase | ๐งช Tests | ๐ Browser |.Reproduced and Verified rows. Mark passing proof with ๐ข, repro or
failing proof with ๐ด, and non-applicable cells with โ N/A.**โ
Outcome**, **โ ๏ธ Caveat**,
**๐๏ธ Design**, and **๐งช Verified**.Summary / Verification PR body, an
adaptive prose body from a git helper skill, plain ## Outcome sections, or
an unrelated generated badge footer unless the caller or repo template
explicitly asks for it.gh pr view --json body output or a concise source-backed summary
of that output.Final handoff / sync:
pnpm check are non-failing and unrelated.Timeline:
pnpm check passed.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Complete |
| Where am I going? | Amend/push final plan, complete goal, final response |
| What is the goal? | Complete PR #4835 with a verified replacement PR that credits @dschoorl |
| What have I learned? | See Findings |
| What have I done? | See Timeline |
Open risks:
pnpm check passed before PR creation.