Repository navigation
Description
Summary
In the id web markdown codec (pkgs/id/src/web/markdown.rs), a footnote definition with more than one paragraph loses the paragraph boundary on save: the paragraphs are fused into a single run of text with no separator at all — first + second para saves as firstsecond para. Words are glued together and the definition's block structure is destroyed.
Evidence (verified this run, main 76672b9)
Standalone probe crate carrying a verbatim copy of src/web/markdown.rs (comrak 0.56.0, the locked version), round-tripping markdown_to_prosemirror → prosemirror_to_markdown twice. Probe logs: goal hidden_files/probe-2026-10-26-1815/ (batch2.log, inspect.log).
Input:
text[^a]
[^a]: first
second paraProseMirror after parse (inspect.log): the footnote_definition node's content is two bare text nodes — [{"text":"first"},{"text":"second para"}] — the paragraph wrappers are already gone.
First save:
text[^a]
[^a]:
firstsecond paraThe output is a stable spelling from then on, but the document's words are fused and the second paragraph no longer exists as a paragraph.
Root cause (code evidence)
- Parse direction,
markdown.rs:505-520(NodeValue::FootnoteDefinitionarm): iterates the definition's block children and extends one flat inline-content vec withcollect_inline_children(child, &[])for each — block boundaries between children are discarded by construction. - Save direction,
markdown.rs:1203-1220: re-wraps all of that inline content in a singleParagraphbefore handing the AST to comrak, so nothing can reintroduce the boundary; adjacent text nodes render concatenated with no separator.
This is the footnote counterpart of #1003 (multi-paragraph table cells fusing), at a different code site with a different representation problem: the PM schema for footnote_definition currently cannot represent more than one block at all. A definition containing a list or code block flattens the same way.
Why it matters
Multi-paragraph footnotes are ordinary markdown (the standard way to write a long footnote). Opening such a document in the id web editor and saving — even without touching the footnote — permanently fuses its paragraphs and glues words together. Silent data corruption on save, in the same family as #981/#1003/#1017.
Acceptance criteria
- A footnote definition with two paragraphs round-trips with the paragraph boundary preserved (two paragraphs, or a representation that renders identically and is a fixpoint from the first save).
- No word fusion: saved text never concatenates two block children's text without a separator.
- A definition containing another block type (list, code block) either round-trips or degrades with an explicit separator, never silent fusion.
- Regression tests in
web::markdowncover the two-paragraph definition (fixpoint asserted over two saves); existing markdown tests pass.
Suggested direction (triage decides): either carry block children on the PM footnote_definition node (schema change, editor side must tolerate it), or — if the flat inline representation stays — join block children with an explicit paragraph break on parse and split on save.
Dedup: searched open + closed issues for footnote — #945 (closed) covers basic footnote round-tripping and single-paragraph definitions; no issue covers multi-block definitions. #1003 is the table-cell site, distinct code path.
Activity
developing-today commentedon Oct 26, 2026
CODE-185
dezren39 commentedon Oct 26, 2026
🤖 Agent note · filed
firstsecond para— paragraph boundary and separator lost; save side re-wraps all inline content in one Paragraph (:1203-1220). Enqueued as a triage packet to repo-health-code-pilot — the fix may need a PM schema decision for multi-block footnote definitions.35 remaining items
Merge pull request #1040 from developing-today/queue/1210-1031-footno…
dezren39 commentedon Oct 27, 2026
Done — PR #1040 merged (merge commit 596096a); issue auto-closed. CI rerun fully green (the first run's failures were Magic Nix Cache 418 infra, as diagnosed). Codec probe verification from the implementation shift: web::markdown suite 68/68.