Repository navigation
Description
Bug
In the id web editor's markdown ↔ ProseMirror codec (pkgs/id/src/web/markdown.rs), a footnote definition that no [^label] reference in the document points at is silently deleted on save:
Hello world.
[^a]: orphan definitionloads and saves as just:
Hello world.The extreme case is total: a document consisting only of footnote definitions (a shared notes page collecting definitions, or a draft whose references are added later)
[^a]: just a definitionloads as the empty-document placeholder paragraph and saves as an empty file ("").
Definitions that are referenced survive — the codec has dedicated footnote_reference / footnote_definition nodes for them (#945, #1031) and they round-trip. The loss is specifically: any definition whose label no reference in the current document uses. With two definitions where only one is referenced, the referenced one round-trips and the unreferenced one vanishes in the same save.
Verified repro (this run, main db6f7d0)
Standalone probe crate compiling the verbatim pkgs/id/src/web/markdown.rs (md5 8478c0d4fdcb251801a8efc83379f213, identical on sandbox main and the laptop copy) against comrak 0.56 + serde_json, built and run on the laptop (~/git/fleet/probe-2026-10-27-creator-a2 there; logs in the goal's hidden_files/probe-2026-10-27-creator-a2/probe_out3.log):
orphan-footnote-def-with-text:"Hello world.\n\n[^a]: orphan definition\n"→ PM is a single paragraphHello world.(the definition appears nowhere) → save returns"Hello world.\n".only-footnote-def:"[^a]: just a definition\n"→ PM is{"content":[{"type":"paragraph"}]}(the empty-doc placeholder) → save returns"".two-defs-one-referenced:"text[^a]\n\n[^a]: used\n\n[^b]: unused\n"→ PM carries only definitiona→ save drops[^b]: unused.footnote-def-only-multiline: a two-paragraph unreferenced definition likewise saves as"".- Layer isolation: a raw comrak
parse_documentof"[^a]: just a definition\n"(footnotes extension on, exactly ascommonmark_options(), markdown.rs:81-91) yields zero AST children, and"Hello.\n\n[^a]: orphan\n"yields only theParagraph— comrak never emits aFootnoteDefinitionnode for an unreferenced definition, so the codec's parse direction (theNodeValue::FootnoteDefinitionarm, markdown.rs:559-575) never sees it. The codec inherits the loss from its parser, exactly as id(web): unused link reference definitions are silently deleted on save — a definitions-only document saves as an empty file #1004 does for link reference definitions.
Relation to #1004 (not a duplicate)
#1004 tracks the same phenomenon for link reference definitions ([label]: url), which have no PM representation at all. Footnote definitions are a different construct with a different fix surface: the PM schema and editor already represent them (#945/#1031, and #1347 tracks their editor-schema wiring), referenced definitions already round-trip, and a source pre-scan fix for #1004 would not automatically know about [^label]: definition syntax. This issue tracks the footnote half so neither fix silently leaves the other construct losing data.
Why it matters
Keeping footnote definitions at the foot of a note before (or without) referencing them is a normal authoring pattern, and footnote-heavy documents are exactly the ones this codec added dedicated support for. Opening such a file in the id web editor and saving — even with no edit — destroys every unreferenced definition with no warning. Same silent-loss-on-save family as #996 (empty links) and #1004 (unused reference definitions), in a distinct mechanism.
Suggested direction (triage to decide)
Mirroring #1004's options: (a) preserve unreferenced definitions verbatim — the parse direction would need a source pre-scan for [^label]: definitions, diff them against the labels comrak actually emitted, and carry the leftovers (e.g. as a trailing raw block the editor displays but does not eat); (b) if neither preservation nor a PM representation is acceptable for the unreferenced case, document the limitation in the codec's module docs (top of markdown.rs) so the loss is a known, stated behaviour instead of a silent one. Doing nothing silently is the only unacceptable outcome. If a shared pre-scan lands for #1004, extending it to footnote definitions in the same change would close this issue by reference.
Acceptance criteria
- A document with an unreferenced footnote definition no longer loses it silently on a load/save round trip — either the definition survives (verbatim or in an equivalent form), or the limitation is explicitly documented in the codec module docs and this issue is closed citing that documentation.
- Referenced footnote definitions (single- and multi-paragraph, with block children) keep round-tripping exactly as today (id(web): footnotes are backslash-escaped on save — [^1] syntax mangled, definition orphaned #945/id(web): multi-paragraph footnote definitions fuse into one run of text on save — paragraphs concatenated with no separator #1031 behaviour unchanged).
- A definitions-only document does not save as an empty file under the chosen fix (or is covered by the documented limitation).
- Round-trip tests for the orphan / mixed referenced+unreferenced / definitions-only cases are added alongside the existing footnote tests in
pkgs/id/src/web/markdown.rs. - Existing
web::markdowntests pass.
Activity
developing-today commentedon Oct 28, 2026
CODE-609
dezren39 commentedon Oct 28, 2026
🤖 Agent note · filed
345 remaining items
dezren39 commentedon Nov 1, 2026
agent-note:v1 — task 1829 picked up (lane A shift, 2026-11-01). Fresh-verified: PR #2935 OPEN at d5db5ec, issue #1565 OPEN, no hard-stop labels, 8 unresolved Codex P2 threads (five 14:35Z + three 15:08Z). Starting fix round 29 in worktree ~/git/fleet/1829-1565 on branch queue/1829-1565-footnote-prescan: disposition all 8 fail-first, then web::markdown suite, thread replies/resolution, @codex re-review.
chatgpt-codex-connector commentedon Nov 1, 2026
To use Codex here, create an environment for this repo.
fix(id/web): gate footnote/reference scans on container-relative inde…
dezren39 commentedon Nov 1, 2026
agent-note:v1 — task 1829 parked (lane A shift, 2026-11-01) for Codex re-review of PR #2935 head 1c762d6. Fix round 29 dispositioned all 8 unresolved P2 threads (replied + resolved): container-relative indentation gates for footnote HTML openings and fence closers, reference-collector footnote state derived from the opening body, multiline labels stop at interrupting blocks, unescaped
(rejected in parenthesized titles, whitespace required after angle destinations, sibling list items end list-scoped fences, space/tab-only blank-line predicate. 8 new regression tests; suite 202 passed / 0 failed on the laptop. Resume: if the re-review is clean, merge PR #2935 and close #1565; if new findings land, fix round 30.Merge pull request #2935 from developing-today/queue/1829-1565-footno…
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
8bcb8d1e2b9ab7021b606fde64af6f6b77ec88aa, head1c762d6cb= lane A's fix round 29, which dispositioned all 8 outstanding Codex P2 threads with the web::markdown suite at 202 passed / 0 failed on the laptop), and issue id(web): unreferenced footnote definitions are silently deleted on save — a definitions-only document saves as an empty file #1565 CLOSED at 16:50:11Z on the merge. No code work remained for this attempt; this note plus the label cleanup (stalestatus/parked/queue/parked/work/assignedfrom the last park removed) and the queue completion are the closing verbs.