Repository navigation
Description
Summary
In the id web editor, a loose task list item — a checkbox item containing a second paragraph (or a code block) — is corrupted on save. The paragraph boundary is dropped on the first save, and on the second save the item's blocks fuse into a single run of text. A code block inside a task item is destroyed outright (it becomes inline paragraph text).
Bullet and ordered list items with the exact same structure round-trip correctly, so this is specific to the task-list path.
Evidence (verified on main 70efcd4, standalone probe crate carrying a verbatim copy of pkgs/id/src/web/markdown.rs, comrak 0.56.0; logs in the goal's hidden_files probe-2026-10-26-1945/out3.log)
Multi-paragraph task item:
orig: "- [ ] first\n\n second para\n- [ ] other\n"
save 1: "- [ ] first\n second para\n- [ ] other\n" ← blank separator gone
save 2: "- [ ] first second para\n- [ ] other\n" ← paragraphs fused into one
PM after save 1 confirms the damage is structural, not cosmetic: the item holds ONE paragraph containing first, , second para.
Task item with a code block:
orig: "- [ ] item\n\n ```\n code\n ```\n"
save 1: "- [ ] item\n code\n"
save 2: "- [ ] item code\n" ← code block gone, text inlined
Controls (same probe, same shapes): multi-paragraph bullet and ordered items preserve the blank line and are two-save fixpoints; a task item whose second block is a nested list survives. Checked items (- [x]) are affected identically.
Root cause
- Parse direction,
pkgs/id/src/web/markdown.rs:214-220: thetask_listJSON node carries notightattr, unlikebullet_list/ordered_list, which storelist.tight. - Save direction,
markdown.rs:1019-1027: the"task_list"arm hardcodestight: truein the comrakNodeList. A loose list rendered tight gets no blank line between an item's block children, so the re-parse merges them. pkgs/id/web/src/editor.ts:98— thetask_listschema node likewise declares no attrs, so even a codec-side fix needs the schema/runtime side considered (same pattern as id(web): ordered task lists lose their ordering on save — rewritten as bullet task lists #997).
Why it matters
Silent data corruption on save for a common construct: anyone who writes a task with a note paragraph underneath it (or a code snippet in a task) loses the structure — first the separation, then the block itself. Because save 1 still looks plausible, the damage is easy to miss until the fused text is noticed.
Acceptance criteria
- A loose (multi-paragraph) task list item round-trips with its paragraph boundary preserved and is a two-save fixpoint, for both
- [ ]and- [x]items. - A code block inside a task list item survives a save as a code block (not inlined into the paragraph text).
- Tight (single-paragraph) task lists render exactly as before.
- Regression tests in
web::markdowncovering the cases above; existing markdown tests pass.
Suggested direction
Carry tight on the task_list node the way bullet/ordered lists do (parse stores it, save reads it instead of hardcoding true), and add the corresponding attr on the editor.ts task_list schema node if the runtime needs it. Related but distinct: #997 (ordered task lists lose their start number).
Activity
developing-today commentedon Oct 27, 2026
CODE-187
dezren39 commentedon Oct 27, 2026
🤖 Agent note · filed
43 remaining items
ci: retrigger checks for rustfmt fix (#1036)
dezren39 commentedon Oct 27, 2026
Resumed again by queue worker (Lane A), task 1216: the CI rerun for the rustfmt fix @ 5a92096 never triggered (only Auto Assign/CodeRabbit exist for that SHA — an Actions trigger gap; a follow-up empty commit didn't retrigger either, as it touches no
pkgs/id/**path). Meanwhile main landed #997 (ordered task lists) in the sametask_listcode and the PR went conflicted. I merged current main (bc9b67f) into the branch and resolved by combining both features: parse now emitsattrs {tight}always plusorderfor ordered task lists; save readsorderfor the list type andtight(with the #1036 legacy inference) for both bullet and ordered;editor.tstask_list carries both attrs. #997's shape assertion (bullet task list has no attrs) was updated to the combined shape (noorder,tight: true) — the one intentional PM-shape change #1036 exists to make. Verified in the verbatim-codec probe: 83/83 tests pass (both #1036 and #997 suites), rustfmt clean. Merge pushed @ f0c4444 and CI has now triggered normally. Parked for the run.dezren39 commentedon Oct 27, 2026
🤖 Agent note · picked up
clippy-lint, is not this PR: the sole error ispanic!at markdown.rs:1987 in the MisplacedNode test that landed on main via the id(web): prosemirror_to_markdown panics on misplaced nodes — json_to_ast never validates block/inline placement #1043 fix — that is issue clippy: panic! in #1043 MisplacedNode test (markdown.rs:1999) breaks clippy-lint on main for every PR #1245, actively being fixed by the parallel lane (queue task 1264). It fails identically on clean main. Per the standing merge directive (verify the change itself is sound; note the unrelated failure), merging fix(id-web): carry tight on task_list so loose task items stop fusing on save (#1036) #1041 now: its diff is 2 files (markdown.rs tight carry + editor.ts attr), probe 83/83 recorded at park, all functional checks green.Merge pull request #1041 from developing-today/queue/1216-1036-task-l…
dezren39 commentedon Oct 27, 2026
🤖 Agent note · done
fixed_merged— PR fix(id-web): carry tight on task_list so loose task items stop fusing on save (#1036) #1041 merged (merge sha0443d6cf944573f3132dbf8df6aab98edac80edd); this issue auto-closed on merge as expected. Thetask_listtightcarry (parse + save, legacy-doc inference, editor node-spec attr) is on main, so loose task items no longer fuse on save.clippy-lint, failing on the pre-existingpanic!at markdown.rs:1987 (MisplacedNode test from the id(web): prosemirror_to_markdown panics on misplaced nodes — json_to_ast never validates block/inline placement #1043 fix) — tracked as clippy: panic! in #1043 MisplacedNode test (markdown.rs:1999) breaks clippy-lint on main for every PR #1245 and being fixed under queue task 1264; it is red on clean main and unrelated to this diff. All other 26 checks passed.