Repository navigation
Description
Bug
Saving a ProseMirror table whose first row is made of table_cell nodes (no table_header row) produces output that is not a table at all:
{"type":"doc","content":[{"type":"table","content":[
{"type":"table_row","content":[
{"type":"table_cell","content":[{"type":"paragraph","content":[{"type":"text","text":"x"}]}]}
]}
]}]}prosemirror_to_markdown returns Ok("| x |\n") — a single pipe row with no delimiter row (| --- |). Re-parsing that output yields a plain paragraph: the table structure is silently destroyed on save, with no error.
Root cause: in json_to_ast's table_row arm (pkgs/id/src/web/markdown.rs, ~:1087), a row is marked as the header row only when its first cell is a table_header node. A table built entirely from table_cell rows therefore gets no header row in the comrak AST, and comrak's commonmark renderer emits no delimiter row for a headerless table. Nothing in the codec promotes the first row, synthesizes a header, or rejects the shape.
Evidence (verified 2026-10-26 on main cf60812)
Standalone probe crate carrying a verbatim copy of src/web/markdown.rs (comrak 0.56.0 locked) — log in goal hidden_files probe-2026-10-26-2145/out4.log, case t_table_no_header: SAVE4 table_no_header | ok="| x |\n". Markdown-parsed tables always carry a header row, so this only bites documents produced as ProseMirror JSON (editor variants without a header row, imports, collab/API writers) — exactly the inputs the save direction exists for.
Related but distinct: #1003 (two paragraphs inside one table cell fuse on save). This issue is about the table's header row, not cell contents.
Why it matters
A document containing a headerless table saves "successfully" while its table silently becomes a paragraph — structural data loss with no signal, the same failure family as the other codec save-direction losses (#1003, #1004, #1036).
Acceptance criteria
- Saving a PM table whose rows are all
table_cellpreserves the table: either the first row is promoted to a header row (delimiter row emitted, re-parse yields an equivalent table), or the conversion returns aConversionError— silently emitting a non-table is not acceptable. - Regression test in
web::markdown: headerless PM table round-trips as a table (or errors, per the chosen behavior); header-row tables render byte-identically to before. - Existing
web::markdowntests still pass.
Activity
developing-today commentedon Oct 27, 2026
CODE-189
dezren39 commentedon Oct 27, 2026
🤖 Agent note · filed
24 remaining items
dezren39 commentedon Oct 27, 2026
🤖 Agent note · parked
clippy-lintFAILS on a pre-existing main break —panic!atsrc/web/markdown.rs:1999in the id(web): prosemirror_to_markdown panics on misplaced nodes — json_to_ast never validates block/inline placement #1043 MisplacedNode test added by PR fix(id-web): validate block/inline placement in prosemirror_to_markdown (#1043) #1089 (merged today 03:48Z); PR fix(id-web): promote first row when a PM table has no header row (#1044) #1193's own diff (2 hunks, table arm + tests) contains nopanicand its probe is 79/79 — and (2)crash-gatestill pending. Filed the clippy break as clippy: panic! in #1043 MisplacedNode test (markdown.rs:1999) breaks clippy-lint on main for every PR #1245 (fix enqueued as queue task 1264). Parked, not merged: merging into a red-clippy main state would hide a fleet-wide break.queue/1233-1044-headerless-tablewith main, await green CI incl. crash-gate, merge PR fix(id-web): promote first row when a PM table has no header row (#1044) #1193, verify this issue auto-closes, complete task 1233.dezren39 commentedon Oct 27, 2026
🤖 Check-in — fleet monitor
dezren39 commentedon Oct 27, 2026
🤖 Agent note · handed off
queue/queued+status/needs-triage. Next holder: read the PR's checks fresh, update the branch with main if CI needs it, merge if green, verify this issue auto-closes.dezren39 commentedon Oct 27, 2026
🤖 Agent note · handed off
queue/queuedbut has NO live task in repo-health-code-pilot, and the monitor cannot re-enqueue it with a standard packet.Why it is stuck:
repo-queue.py enqueuewith either standard dedupe key for this issue returns the existing terminal row instead of creating a task — triage key → task 1230 (done), fix key → task 1233 (cancelled). Verified twice this run (17:52Z and 17:56Z). The prior monitor run (16:55Z) reported this issue enqueued as task 1469; no such pilot task exists — that enqueue was a dedupe hit on task 1230. Until the queue gains a requeue/revive path for dedupe-consumed issues (#1289), this item needs operator repair to get a live task; it is flagged prominently in the monitor's run summary.dezren39 commentedon Oct 29, 2026
🤖 Agent note · bound