docs/plans/4985-ordered-list-heading-paragraph-numbering.md
Objective: Fix #4985 ordered-list start regression; done when paragraph lists after numbered headings start at 1 and package checks pass; plan docs/plans/4985-ordered-list-heading-paragraph-numbering.md.
Goal plan: docs/plans/4985-ordered-list-heading-paragraph-numbering.md
Template: docs/plans/templates/task.md
Primary template: docs/plans/templates/task.md
Applied packs:
Task source:
Completion threshold:
@platejs/list behavior and
passes after the fix.@platejs/list normalizes ordered-list start values so heading numbering
does not seed non-heading list numbering, while non-heading mixed-block
continuation still works. Configured heading node types are treated as
headings, nested headings do not break outer list tracks, and non-numbered
headings do not reset ordered paragraph numbering.@platejs/list.node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/4985-ordered-list-heading-paragraph-numbering.md passes.Verification surface:
@platejs/list unit test for heading-to-paragraph ordered-list start
behavior.packages/list.Constraints:
Boundaries:
packages/list/**, focused tests, .changeset/**, and
this goal plan.Output budget strategy:
rg/rg --files scoped to packages/list and short sed ranges. Exclude
generated/build outputs and cap broad reads to filenames or top matches.Blocked condition:
Task state:
Current verdict:
pnpm check,
PR body verification, and issue sync all passed.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/4985-ordered-list-heading-paragraph-numbering.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | Read task, autogoal, tdd, changeset, local .agents/AGENTS.md. |
| Active goal checked or created | yes | get_goal returned none; create_goal created active goal for this plan. |
| Source of truth read before edits | yes | gh issue view 4985 --repo udecode/plate --comments --json .... |
| Tracker comments and attachments read | yes | Issue has one bot comment and one screenshot attachment; no video. |
| Video transcript evidence required | no | N/A: issue evidence is a static image, not video/screen recording. |
docs/solutions checked for non-trivial existing-code work | yes | `find docs/solutions -maxdepth 2 -type f |
| TDD decision before behavior change or bug fix | yes | Use one focused behavior regression test before implementation. |
| Branch decision for code-changing task | yes | Started on main; after pnpm check, created codex/fix-4985-list-heading-numbering. |
| Release artifact decision | yes | Published package behavior changes, so add .changeset for @platejs/list. |
| Browser tool decision for browser surface | no | N/A: issue marks Browser surface No; focused package test owns this behavior. |
| PR expectation decision | yes | Active task workflow requires PR after verified code unless blocked by check/auth. |
| Tracker sync expectation decision | yes | Sync issue after PR or report blocker; no early tracker comment. |
| Output budget strategy recorded | yes | Narrow packages/list searches and short reads; no unbounded repo scans. |
| Package/API pack selected | yes | Applied package-api pack to plan. |
| Public surface or package boundary identified | yes | Runtime behavior of published @platejs/list normalizer. |
| Release artifact path selected | yes | .changeset patch for @platejs/list. |
changeset skill loaded when .changeset is required | yes | Read .agents/rules/changeset.mdc. |
| Barrel/export impact decision recorded | yes | Internal helper added under lib/internal; pnpm --filter @platejs/list brl passed with no public barrel changes. |
Work Checklist:
<video-transcripts> XML, or marked N/A with reason..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.tooling/data/plate-ui-changelog.mdx and generated /registry/changelog/* JSON instead of adding a package changeset.main.Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | yes | Run the command, proof, source audit, or artifact check named in this plan | pnpm check passed in /Users/zbeyens/git/plate; PR #5011 created and issue #4985 synced. |
| Bug reproduced before fix | yes | Record failing test/repro or N/A with reason | pnpm --filter @platejs/list test normalizeListStart failed before fix: paragraph items after numbered headings normalized as 3, 4. |
| Targeted behavior verification | yes | Run focused test/proof for changed behavior or record N/A | pnpm --filter @platejs/list test isSameListSequence; pnpm test:slow packages/list/src/lib/normalizers/normalizeListStart.slow.tsx; pnpm --filter @platejs/list test toggleList passed. |
| TypeScript or typed config changed | yes | Run relevant typecheck | pnpm turbo typecheck --filter=./packages/list passed. |
| Package exports or file layout changed | yes | Run pnpm brl before final verification and keep generated barrel updates | Internal helper added under lib/internal; pnpm --filter @platejs/list brl passed with no generated public barrel changes. |
| Package manifests, lockfile, or install graph changed | no | Run pnpm install and relevant package checks | N/A: no package manifest or lockfile changed. |
| Agent rules or skills changed | no | Run pnpm install and verify generated skill sync | N/A: no agent rules or skills changed. |
| Workspace authority proof | yes | Run verification in the owning repo/package/app/route/tool and record cwd; do not count the wrong workspace as proof | Owning cwd /Users/zbeyens/git/plate; package commands scoped to @platejs/list; root pnpm check passed. |
| Browser surface changed | no | Capture Browser Use proof or record explicit waiver/blocker | N/A: issue marks Browser surface No; no runnable package route owns this normalizer behavior. |
| Browser final proof | no | Attach screenshot or exact browser verification caveat when browser proof applies | N/A: browser proof waived for package normalizer behavior. |
| CI-controlled template output changed | no | Restore generated template output or record why it is intentionally kept | N/A: no templates/** output changed. |
| Package behavior or public API changed | yes | Add a changeset or record why no changeset applies | .changeset/fix-list-heading-paragraph-start.md added for @platejs/list patch behavior fix. |
| Registry-only component work changed | no | Update tooling/data/plate-ui-changelog.mdx, run node tooling/scripts/generate-ui-changelog-entries.mjs --write, or record N/A | N/A: no registry files changed. |
| Docs or content changed | no | For docs-heavy work, use --template docs; for incidental docs, verify source-backed claims, links, examples, and rendered output or record N/A | N/A: only goal plan docs changed as workflow artifact. |
| High-risk mini gate | yes | For public API/runtime/package-boundary/browser/agent-action/command-contract changes, record realistic failure mode, proof plan, and why the chosen boundary is right; otherwise N/A | Failure mode: over-splitting mixed block lists or under-splitting numbered heading/paragraph tracks; proof covers numbered headings, nested headings, non-numbered headings, configured heading node types, non-heading blockquotes, and polite restarts. Boundary: list normalizer and toggle restart path own numbering. |
| Agent-native review for agent/tooling changes | no | For .agents/**, .claude/**, .codex/**, skills, hooks, commands, prompts, or user-action tooling, load .agents/skills/agent-native-reviewer/SKILL.md and close accepted/actionable findings, or record N/A | N/A: no agent/tooling changes. |
| Local install corruption suspected | no | Run pnpm run reinstall once, rerun the exact failing command, or record N/A | N/A: failures were expected red tests or review findings, not install corruption. |
| Autoreview for non-trivial implementation changes | yes | Load .agents/skills/autoreview/SKILL.md; use dirty local --mode local, branch/PR --mode branch --base <base>, or committed slice --mode commit --commit <ref> until no accepted/actionable findings, or record N/A for docs-only/trivial/no local patch | .agents/skills/autoreview/scripts/autoreview --mode local clean; earlier P2 findings accepted and fixed. |
| PR create or update | yes | Run check before PR work and sync PR body to the task-style final handoff | pnpm check passed; PR #5011 created at https://github.com/udecode/plate/pull/5011. |
| Task-style PR body verified | yes | Verify the PR body with gh pr view --json body; it must preserve auto-release blocks when applicable, must not include a current-PR self-link, and must use the kitcn PR #270 emoji format: ๐ Fixes ..., ๐ข 95-100% confidence, Phase / ๐งช Tests / ๐ Browser table, and bold emoji Outcome/Caveat/Design/Verified sections | gh pr view 5011 --repo udecode/plate --json url,body,headRefName,baseRefName,isDraft verified auto-release block and required task format. |
| PR proof image hosting | no | If PR body needs browser proof, replace local image paths with hosted GitHub URLs or record N/A | N/A: PR body has no browser proof image. |
| Tracker sync-back | yes | Post concise issue/Linear sync after PR exists, or record N/A/blocker | Commented on #4985: https://github.com/udecode/plate/issues/4985#issuecomment-4700114410. |
| Final handoff contract | yes | Fill the final handoff fields below with exact PR/issue/confidence/tests/browser/outcome/caveats/design/verification content or N/A reason | Final handoff fields completed below. |
| Final lint | yes | Run pnpm lint:fix or scoped equivalent | pnpm lint:fix passed, no fixes applied after the final helper move. |
| Output budget discipline | yes | Verify no unbounded high-volume command output was streamed, or record the accidental output and recovery | Broad output came only from required pnpm check; exploratory reads were scoped. |
| Goal plan complete | yes | Run node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/4985-ordered-list-heading-paragraph-numbering.md | To run after this evidence update. |
| Public API / package boundary proof | yes | Source-audit public API, exports, and package boundary impact | rg audit of package index/normalizer exports showed no export surface change; runtime behavior change only. |
| Release artifact classification | yes | Record whether the change is published package behavior/API/types/config/runtime, registry-only, or no published user-visible delta | Published package runtime behavior fix for @platejs/list. |
| Published package changeset | yes | If published package users see a delta, load changeset, add/update one .changeset/*.md per package, and prove no forbidden minor on @platejs/slate, @platejs/core, or platejs | .changeset/fix-list-heading-paragraph-start.md contains only "@platejs/list": patch. |
| Registry changelog | no | If the change is registry-only under apps/www/src/registry/**, update tooling/data/plate-ui-changelog.mdx, run node tooling/scripts/generate-ui-changelog-entries.mjs --write, and do not add a package changeset | N/A: not registry-only work. |
| No release artifact | no | If no artifact is needed, record the exact reason: internal-only, docs-only, agent-only, test-only, or no user-visible delta from main | N/A: release artifact required and added. |
| Package typecheck/build/test | yes | Run owning package checks or record N/A with reason | pnpm --filter @platejs/list test isSameListSequence; pnpm test:slow packages/list/src/lib/normalizers/normalizeListStart.slow.tsx; pnpm --filter @platejs/list test toggleList; pnpm turbo typecheck --filter=./packages/list passed. |
| Barrel/export generation | yes | Run pnpm brl when exports or exported file layout changed, otherwise N/A | pnpm --filter @platejs/list brl passed; helper lives under internal, which barrelsby excludes. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | Issue, comment, skills, local AGENTS, and capped docs/solutions scan read. | done |
| Implementation | complete | Normalizer and toggleList sibling checks share heading/non-heading list-track boundary. | done |
| Verification | complete | Focused tests, package typecheck, lint, autoreview, and pnpm check passed. | done |
| PR / tracker sync | complete | PR #5011 created; issue #4985 commented. | done |
| Closeout | complete | Final plan evidence recorded. | final response |
Findings:
@platejs/[email protected] / [email protected]:
ordered-list paragraph numbering continues from numbered headings when both
use decimal style.normalizeListStart.ts / previous-list lookup as the
likely root cause; local source confirmed that normalizeListStart used only
listStyleType to find the previous list item.toggleList also used getPreviousList to decide whether
listRestartPolite should apply; it needed the same boundary as the
normalizer.Decisions and tradeoffs:
h1-h6 strings, because
Plate plugins can configure node.type.Implementation notes:
isSameListSequence is a private list helper that compares list style plus
heading/non-heading track using editor-resolved heading types.normalizeListStart now continues ordered starts only when the previous
matching list item has the same list style and belongs to the same resolved
heading category as the current item.toggleList now merges plugin/caller sibling options once and uses the same
heading-category query when deciding whether a polite restart is first in its
list track.Review fixes:
listRestartPolite still used old cross-type
previous-list semantics. Fixed toggleList restart boundary and added a
polite restart regression after numbered headings.editor.getType. Added fallback to raw heading keys.breakQuery.Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| Initial heading fixture stripped list props because indent/list target plugins were paragraph-only | 1 | Configure both BaseIndentPlugin and BaseListPlugin for heading targets in the test harness | Fixed regression fixture; red test then showed paragraph starts 3, 4. |
| Autoreview found restart-politeness mismatch | 1 | Apply same sibling boundary in toggleList | Fixed and tested. |
| Autoreview found raw-type boundary over-split mixed non-heading lists | 1 | Use editor-resolved heading-category boundary | Fixed and tested. |
Autoreview found plain Editor callers could crash on editor.getType | 1 | Add raw heading-key fallback when getType is absent | Fixed and tested. |
| Autoreview found query-only heading split could skip headings and resume earlier paragraph numbering | 1 | Move same-track heading/paragraph split into breakQuery | Fixed and tested. |
| Autoreview found hard heading split broke outer lists across nested headings | 1 | Require same indent before breaking lookup | Fixed and tested. |
| Autoreview found hard heading split broke lists across non-list or differently styled headings | 1 | Require same defined list style before breaking lookup | Fixed and tested. |
| Shared helper initially lived in a barrel-exported query module | 1 | Move helper under lib/internal and run package barrel generation | Fixed; no public barrel changes. |
Verification evidence:
pnpm --filter @platejs/list test normalizeListStart failed before fix with
paragraph starts 3, 4; passed after fix.pnpm --filter @platejs/list test isSameListSequence passed after final fix
with 111 fast tests after moving normalizeListStart to the slow lane.pnpm test:slow packages/list/src/lib/normalizers/normalizeListStart.slow.tsx
passed after final fix with 36 slow tests.pnpm --filter @platejs/list test toggleList passed after final fix with 111
fast tests after moving normalizeListStart to the slow lane.pnpm test:slowest passed after the slow-lane move; the fast-suite budget no
longer reports normalizeListStart over the file threshold.pnpm turbo typecheck --filter=./packages/list passed.pnpm lint:fix passed, no fixes applied after the final boundary change.pnpm --filter @platejs/list brl passed with no generated public barrel
changes..agents/skills/autoreview/scripts/autoreview --mode local passed clean after
accepted fixes; final clean result reported no accepted/actionable findings.pnpm check passed. It emitted an existing eslint warning in
apps/www/src/components/ui/sidebar.tsx and exited 0.gh pr view 5011 --repo udecode/plate --json url,body,headRefName,baseRefName,isDraft
verified the PR body.gh issue comment 4985 --repo udecode/plate ... posted
https://github.com/udecode/plate/issues/4985#issuecomment-4700114410.Final handoff contract:
pnpm --filter @platejs/list test normalizeListStart, browser N/A.pnpm check, browser N/A.pnpm check emitted existing eslint warning in apps/www/src/components/ui/sidebar.tsx but passed.listStart normalization.gh pr view 5011 --repo udecode/plate --json ....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, command exited 0.Timeline:
normalizeListStart.pnpm check passed.pnpm check passed.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Closeout complete |
| Where am I going? | Final response |
| What is the goal? | Fix #4985 so paragraph ordered lists after numbered headings start at 1, with focused package proof. |
| What have I learned? | The correct boundary is same-indent, same defined list-style heading-vs-non-heading tracks using editor-resolved heading types; raw element type is too strict and global heading breaks are too blunt. |
| What have I done? | Fixed normalizer and toggle restart logic, added regressions, verified, opened PR, synced issue. |
Open risks: