docs/plans/5043-fix-multiline-paste-crash.md
Objective: Fix issue #5043 without weakening maxLength; done when crash and empty-block overflow regressions pass, package checks and autoreview are clean, and PR ships.
Flow mode: one-shot execution
Goal plan: docs/plans/5043-fix-multiline-paste-crash.md
Template: docs/plans/templates/task.md
Primary template: docs/plans/templates/task.md
Applied packs:
Task source:
[Bug]: pasting text with multiple line-breaks crashesmaxLength does not
throw; final document text never exceeds maxLength, including fragments
with empty blocks.Timed checkpoint:
Completion threshold:
origin/main and passes
after the fix without throwing, producing exactly maxLength characters.maxLength characters.node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/5043-fix-multiline-paste-crash.md passes.Verification surface:
bun test packages/core/src/lib/plugins/length/LengthPlugin.spec.tspnpm turbo typecheck --filter=./packages/corepnpm lint:fix.agents/skills/autoreview/scripts/autoreview --mode localgh pr view and gh issue view readbackConstraints:
Boundaries:
LengthPlugin invariant on
origin/main.packages/core/src/lib/plugins/length/**, one
@platejs/core patch changeset, this plan, and only directly required test
fixtures/imports.maxLength and paste
multiline plain text; public repro is supporting evidence only.Output budget strategy:
Blocked condition:
Task state:
Current verdict:
Pre-solution issue challenge:
maxLength.maxLength: 20.maxLength.LengthPlugin trimming converge after
nested Slate operations instead of disabling all nested enforcement.maxLength is still broken.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/5043-fix-multiline-paste-crash.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Timed checkpoint parsed | no | N/A: no duration requested |
| Skill analysis before edits | yes | task, autogoal, autoreview, tdd, and changeset read; Browser skill deferred until proof |
| Active goal checked or created | yes | goal created with this plan path |
| Source of truth read before edits | yes | gh issue view 5043 --comments equivalent JSON; zero comments |
| Tracker comments and attachments read | yes | zero comments; linked reproduction commit noted; no video |
| Video transcript evidence required | no | N/A: no video or screen recording |
| Pre-solution issue challenge required | yes | valid; criterion tightened to preserve exact length |
| Reproduction verdict before implementation | yes | reproduced in prior exact review; fresh TDD red required before code |
| Repro escalation ladder selected | yes | public API test first; Browser after green; visual proof waived |
| Suggested fix reviewed against durable boundary | yes | PR #5100 guard rejected after exact 21/20 counterexample |
docs/solutions checked for non-trivial existing-code work | yes | focused search found clipboard boundary guidance but no LengthPlugin solution |
| TDD decision before behavior change or bug fix | yes | one faithful crash test red first; invariant companion coverage retained |
| Branch decision for code-changing task | yes | dedicated codex/fix-5043-multiline-paste from fresh origin/main |
| Release artifact decision | yes | one @platejs/core patch changeset |
| Browser tool decision for browser surface | yes | approved in-app Browser required after green package proof |
| PR expectation decision | yes | task workflow requires replacement PR after check |
| Tracker sync expectation decision | yes | comment issue #5043 after PR creation |
| Output budget strategy recorded | yes | exact scoped reads/searches with output caps |
| Browser pack selected | yes | materialized browser pack |
| Browser route / app surface identified | yes | local editor surface; exact route selected during narrow app search |
| Browser tool decision recorded | yes | use approved in-app Browser; no standalone browser automation |
| Console/network caveat policy recorded | yes | record paste outcome and console; unrelated network noise called out |
| Package/API pack selected | yes | materialized package-api pack for published runtime behavior |
| Public surface or package boundary identified | yes | @platejs/core LengthPlugin runtime enforcement |
| Release artifact path selected | yes | .changeset/*.md for @platejs/core: patch |
changeset skill loaded when .changeset is required | yes | loaded; one package, imperative user-impact prose, no minor |
| Barrel/export impact decision recorded | yes | N/A: no exports or file layout change planned |
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. N/A:
none touched..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. N/A: package runtime fix.main. N/A: patch changeset required.Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | yes | Run named proof | Focused 9/9, core typecheck 5/5, browser exact, autoreview clean, pnpm check exit 0 |
| Pre-solution issue challenge verdict | yes | Record verdict | Valid issue; “no crash” tightened to exact cap and no retained overflow; PR #5100 guard rejected |
| Repro escalation ladder | yes | Record each applicable level | Source test red; Playwright N/A; approved Browser red/green; visual screenshot N/A for nonvisual claim |
| Bug reproduced before fix | yes | Record failing repro | Faithful test threw exact node.text TypeError; 7 pass, 1 fail |
| Targeted behavior verification | yes | Run focused proof | Final focused suite 9 pass, 0 fail |
| TypeScript or typed config changed | yes | Run typecheck | Core 5/5 and full package typecheck 54/54 |
| Package exports or file layout changed | no | N/A | No exports or public file layout changed; pnpm brl not required |
| Package manifests, lockfile, or install graph changed | no | N/A | No manifest or lockfile changes |
| Agent rules or skills changed | no | N/A | No agent/tooling files changed |
| Workspace authority proof | yes | Verify in owner | Commands ran in repo/core; Browser ran local www route against rebuilt core dist; GitHub readback via gh |
| Browser surface changed | yes | Capture proof | Local full EditorKit exact paste passed |
| Browser final proof | yes | Record exact caveat/artifact | One DOM child, exact 20-character text, zero fresh console errors; screenshot waived because behavior is nonvisual |
| CI-controlled template output changed | no | N/A | No templates/** changes; temporary playground config restored |
| Package behavior or public API changed | yes | Add changeset | .changeset/core-fix-multiline-max-length.md patches @platejs/core |
| User-visible registry output changed | no | N/A | No final registry diff; registry changelog not applicable |
| Docs or content changed | no | N/A | Only internal goal ledger changed; no public docs/content/API examples |
| High-risk mini gate | yes | Record risk/boundary/proof | Failure modes: cross-block merge crash, 21/20 leak, retained blank blocks; owner is LengthPlugin; tests + full EditorKit Browser prove boundary |
| Agent-native review for agent/tooling changes | no | N/A | No agent/tooling changes |
| Local install corruption suspected | no | N/A | No install-corruption signal; artifact-facing stale dist fixed by core build |
| Autoreview for non-trivial implementation changes | yes | Run until clean | First P2 accepted/fixed; final local autoreview clean with zero findings |
| PR create or update | yes | Run check and create | pnpm check exit 0 before PR; https://github.com/udecode/plate/pull/5101 |
| Task-style PR body verified | yes | Read back body | gh pr view 5101 --json body: auto-release, issue/confidence, exact table, and four required sections present; no self-link |
| PR proof image hosting | no | N/A | No image used; exact DOM/console proof is textual |
| Tracker sync-back | yes | Comment issue | https://github.com/udecode/plate/issues/5043#issuecomment-5348836498 |
| Final handoff contract | yes | Fill fields | Completed below with PR, issue, confidence, flow, browser, outcome, caveat, design, and verification |
| Final lint | yes | Run lint | pnpm lint:fix: 3,286 files; final formatting applied; pnpm check lint has 0 errors |
| Output budget discipline | yes | Audit output | Commands capped; one buffered dev-server flood recorded and subsequent server shutdown capped |
| Timed checkpoint | no | N/A | No duration requested |
| Goal plan complete | yes | Run checker | check-complete.mjs exit 0 after all plan phases closed |
| Browser interaction proof | yes | Exercise paste | Approved in-app Browser on local /blocks/playground; exact multiline paste passed |
| Browser console/network check | yes | Record state | Zero fresh console errors; no relevant network failures |
| Browser final proof artifact | yes | Record route/DOM caveat | Local route, one DOM child, exact 20 characters; screenshot N/A for nonvisual behavior |
| Public API / package boundary proof | yes | Audit impact | @platejs/core runtime behavior only; no export, type, manifest, or public shape change |
| Release artifact classification | yes | Classify | Published @platejs/core runtime bug fix |
| Published package changeset | yes | Add patch changeset | One-package patch changeset; no forbidden minor |
| Registry changelog | no | N/A | Not registry-only and no registry diff |
| No release artifact | no | N/A | Release artifact is required and present |
| Package typecheck/build/test | yes | Run owner checks | Core typecheck 5/5; core build passed; focused 9/9; full check exit 0 |
| 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 | issue, skills, owner files, focused solutions search, branch and red repro recorded | implementation |
| Implementation | complete | leaf-scoped convergent trim; exact crash/invariant/structure regressions green | verification |
| Verification | complete | focused 9/9; core typecheck 5/5; browser one block/20 chars/no errors; autoreview clean; pnpm check exit 0 | PR / tracker sync |
| PR / tracker sync | complete | PR #5101 open/mergeable with verified body; issue comment posted | closeout |
| Closeout | complete | PR/issue/body synced; plan checker exit 0; final CI watch follows the last push | final response |
Findings:
LengthPlugin enforces the limit inside its apply override and recursive
deletes re-enter that override.docs/solutions search found no existing LengthPlugin fix.Decisions and tradeoffs:
Implementation notes:
isTrimming and shouldTrimAgain closure state.insertData crash coverage and the exact empty-block
fragment counterexample that rejected PR #5100.@platejs/core patch changeset.Review fixes:
codex/fix-5043-multiline-paste; owner LengthPlugin; intended behavior is
crash-free exact enforcement; final code scope is plugin + focused spec;
release/ledger scope is one changeset + this plan..agents/skills/autoreview/scripts/autoreview --mode local --stream-engine-output -> clean, zero findings, patch correct at 0.86
confidence.Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
Browser route loaded stale @platejs/core/dist and reproduced the old crash | 2 | Build the artifact-facing core package, reload, and timestamp-filter fresh console logs | pnpm --filter @platejs/core build; exact paste then passed with zero fresh errors |
| Dev-server shutdown returned oversized buffered watcher output | 1 | Stop broad output immediately; use capped exact commands only | No repo change; recorded here and resumed with scoped reads |
Final core typecheck found nullable editor.api.start([]) target | 1 | Guard the unresolved point and rerun the same package check | Guard added; 5/5 typecheck tasks pass |
Verification evidence:
bun test packages/core/src/lib/plugins/length/LengthPlugin.spec.ts in repo
root on origin/main implementation -> 7 pass, faithful multiline paste test
fails with TypeError: undefined is not an object (evaluating 'node.text').http://localhost:3001/blocks/playground, rebuilt core dist,
temporary empty editor with maxLength: 20: pasted
123456789012345678901\n\ntrailing text; DOM contained exactly
12345678901234567890 plus two empty blocks and fresh console errors were
[]. Temporary playground configuration was restored; targeted diff exit 0.pnpm turbo typecheck --filter=./packages/core after the final null guard ->
5/5 tasks pass.pnpm lint:fix after final implementation -> 3,286 files checked, no fixes.bun test packages/core/src/lib/plugins/length/LengthPlugin.spec.ts
-> 9 pass, 0 fail, 10 expectations.http://localhost:3000/blocks/playground: exact paste produced one DOM child,
innerText/textContent of exactly 12345678901234567890, and zero fresh
console errors; temporary config restored with targeted diff exit 0.pnpm check in /Users/zbeyens/git/plate -> exit 0; lint 0 errors (one
pre-existing hook warning), package build 54/54, package typecheck 54/54,
fast 3,459/3,459, slow 352/352 plus isolated slow rows, and slowest suite
completed with zero failures.Final handoff contract:
/blocks/playground with rebuilt coregh pr view 5101 --json body readback matches task formatTask-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:
Timeline:
createPlateEditor + MarkdownPlugin + plain-text insertData: 7 pass, 1 fail.@platejs/core, reran exact local paste, observed 20 text characters and no
fresh console errors, then restored the temporary route configuration.pnpm check exit 0.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | All implementation and verification gates green; shipping PR |
| Where am I going? | Commit/push, PR body readback, issue sync, plan closeout |
| What is the goal? | Fix multiline overflow paste without weakening maxLength |
| What have I learned? | Safe truncation needs leaf-scoped text deletion plus atomic removal of empty overflow blocks |
| What have I done? | Fixed crash/invariant/blank-block regression and proved tests, package checks, and browser behavior |
Open risks:
maxLength counts JavaScript string length while Slate
locates deletion targets by character; this patch preserves that contract.