You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implementation half of the construct inventory (verdict table in the latest comment on #63, verified against the reader on origin/main — per-construct greps with file:line citations on that ref, not spec recollection), feeding the harmonised vocabulary in ExaDev/document-schema.js#22. The pole-case finding holds: markdown needs no new generic construct kind — every semantic verdict lands on a mechanism the schema major already defines (definitions table, anchor, link, ContentFormula, list-membership/tree fields). Everything below is gated on ExaDev/document-schema.js#24 landing the construct descriptor kinds and package tables additively.
Already carried and untouched: GFM tables with alignment (ContentTable plus per-cell ContentParagraph.alignment), strikethrough (ContentRun.strike), autolinks (ContentRun.hyperlink, with the <...> spelling re-derived on emit when text equals destination). Derivable, dropped without loss: the reference-link form itself (the destination survives on hyperlink; label spelling is a serialisation choice restorable from the definitions table), entity spelling (emit re-escapes), serialisation choices (ATX/setext, bullet glyph, delimiters, fence char, CRLF, tight/loose), and soft line breaks — with the honest caveat that soft breaks are the single largest conformance-exclusion bucket by count.
Semantic rows — reading work, by target in the harmonised vocabulary:
Reference link definitions ([label]: dest "title") — package-level definitions table, the same facility the styles table rides; the map is built and returned today but dies at lowering (lowerParsedMarkdown reads only parsed.document.children), and a definitions-only paragraph leaves no block behind — src/block/definitions.ts:66 (extract + consume); src/block/block.ts:782 (map returned); src/lower/lower.ts:258,275 (map ignored); fixtures src/block/block.test.ts:139-158,233
Footnotes ([^n] marker + definition) — done in feat: read and write GitHub footnotes, with definitions as anchor constructs #69. The mapping this row proposed turned out backwards on both halves: the DEFINITION is the anchor construct (its body rides the construct's own extent, since AnchorDescriptor.definition names a definitions-table key and a flat ContentDocument has no root table to key into), and the MARKER cannot be an anchor at all — a construct's extent is block-scoped, and a reference site sits between two runs inside a paragraph. The reference ships as a marked run (md/footnote-reference-preserved-as-text) and needs a run-level extent mechanism in document-schema.js to close properly. See the comment below for the full reading
Link/image titles — link node: exactly the annotated link extent a flat run field cannot express, the case the standing reconciliation on build(deps): bump document-schema.js to 2.6.1 #22 reserves the node kind for; dropped today on every path (inline links, images both run-degraded and resolved-block, and the definitions table's own titles, parsed then dead with the map) — md/link-title-dropped; src/lower/inline.ts:72-84; src/lower/lower.ts:98; src/test-support/conformance-exclusions.ts (LINK_TITLE)
GFM task lists — a real checked/task field on the list-membership representation, replacing today's degraded encoding (a checkbox-glyph text run prepended to the first paragraph plus a task bit encoded into the minted numId; state lost entirely when the item's first block is a table or resolved image) — src/ast/ast.ts:81; src/block/block.ts:685-700; src/lower/lower.ts:165-208 (applyTaskCheckbox, false-return case documented)
Code fence info string — the language word as an annotation on the code representation (a field, not a node kind); the remainder goes to residue (see the residue checklist); dropped today when non-empty, diagnosed — md/code-block-info-string-dropped; src/lower/lower.ts:117-124
Math ($$ blocks, \(...\) inline, Parse $$ display-math blocks and \( \) inline math #53) — ContentFormula with the canonical-plus-source pattern: LaTeX as the source spelling beside whatever canonical form the schema settles; today's preserved-as-text (MathBlock styleId, inline marker fontFamily) is the interim, not the destination — src/lower/lower.ts:143-152; src/lower/inline.ts:64-67; the MarkdownMathInlineNode comment in src/ast/ast.ts
Frontmatter, the unmapped half — mapped keys already reach ContentDocument.metadata; the unmapped remainder (md/front-matter-key-unmapped plus drop) and the non-flat shapes the flat reader silently skips (nested mappings, block lists, multi-line scalars) become a raw front-matter block in the package-level residue channel — src/lower/front-matter.ts:66-87 (switch + default-drop); emit inverse src/emit/front-matter.ts
Multi-block list item boundary — item identity on the list-membership model (the schema's list work, not a construct-vocabulary node); today ContentListMembership carries numId/level only, so "one item, several blocks" and "several items" are indistinguishable, and tables/images in an item lose membership entirely — md/list-item-multi-block-flattened, md/list-item-block-unlisted; lowerListItem in src/lower/lower.ts
Blockquote container / nesting depth — a container representation in the schema tree, sibling of the item-boundary gap; carried degraded today as indentLeftPt plus a Quote styleId, with depth beyond 1 approximate and the container boundary lost — md/blockquote-nested-depth; lowerBlockquote in src/lower/lower.ts; conformance-exclusions.ts (BLOCKQUOTE_STRUCTURE, ADJACENT_SAME_DEPTH)
Raw HTML blocks (all seven kinds) — per-node source: { format, ... }, restorable on re-emission; the canonical residue-channel use case from build(deps): bump document-schema.js to 2.6.1 #22, and it also fixes a live ambiguity: literal text that happens to look like a tag is indistinguishable from preserved HTML today (RAW_TEXT_TAG_AMBIGUITY exclusion). Current behaviour is preserve-as-literal-text (default), HTMLPreformatted styleId, or drop under rawHtml: 'drop' — never parsed — src/html/html.ts (recogniser, all 7 block kinds plus the inline grammar); src/lower/lower.ts:130-141; codes md/raw-html-preserved-as-text, md/raw-html-dropped; fixtures src/diagnostics/diagnostics.test.ts:110-121, src/lower/lower.test.ts:281-290
Raw HTML inline — per-run source channel, same treatment and same diagnostics — src/lower/inline.ts:57-62
Code fence info-string remainder — pandoc-style {.class #id} attributes quarantined beside the semantic language word (see the info-string row above)
Decisions needed — uncertainty to resolve before the split rows freeze:
Info-string split: the language-word/residue-remainder divide is a design proposal, not an observed distribution — real-world prevalence of pandoc-style attribute syntax versus bare language words is unsurveyed. Measure before freezing.
Pandoc inline footnotes (^[...]) have no GFM analogue — the inventory treats the marker-plus-tail-definition pair as the scope and inline notes as out of scope; confirm or widen deliberately rather than by omission.
Implementation half of the construct inventory (verdict table in the latest comment on #63, verified against the reader on origin/main — per-construct greps with file:line citations on that ref, not spec recollection), feeding the harmonised vocabulary in ExaDev/document-schema.js#22. The pole-case finding holds: markdown needs no new generic construct kind — every semantic verdict lands on a mechanism the schema major already defines (definitions table,
anchor,link,ContentFormula, list-membership/tree fields). Everything below is gated on ExaDev/document-schema.js#24 landing the construct descriptor kinds and package tables additively.Already carried and untouched: GFM tables with alignment (
ContentTableplus per-cellContentParagraph.alignment), strikethrough (ContentRun.strike), autolinks (ContentRun.hyperlink, with the<...>spelling re-derived on emit when text equals destination). Derivable, dropped without loss: the reference-link form itself (the destination survives onhyperlink; label spelling is a serialisation choice restorable from the definitions table), entity spelling (emit re-escapes), serialisation choices (ATX/setext, bullet glyph, delimiters, fence char, CRLF, tight/loose), and soft line breaks — with the honest caveat that soft breaks are the single largest conformance-exclusion bucket by count.Semantic rows — reading work, by target in the harmonised vocabulary:
[label]: dest "title") — package-level definitions table, the same facility the styles table rides; the map is built and returned today but dies at lowering (lowerParsedMarkdownreads onlyparsed.document.children), and a definitions-only paragraph leaves no block behind —src/block/definitions.ts:66(extract + consume);src/block/block.ts:782(map returned);src/lower/lower.ts:258,275(map ignored); fixturessrc/block/block.test.ts:139-158,233[^n]marker + definition) — done in feat: read and write GitHub footnotes, with definitions as anchor constructs #69. The mapping this row proposed turned out backwards on both halves: the DEFINITION is theanchorconstruct (its body rides the construct's own extent, sinceAnchorDescriptor.definitionnames a definitions-table key and a flatContentDocumenthas no root table to key into), and the MARKER cannot be an anchor at all — a construct's extent is block-scoped, and a reference site sits between two runs inside a paragraph. The reference ships as a marked run (md/footnote-reference-preserved-as-text) and needs a run-level extent mechanism in document-schema.js to close properly. See the comment below for the full readinglinknode: exactly the annotated link extent a flat run field cannot express, the case the standing reconciliation on build(deps): bump document-schema.js to 2.6.1 #22 reserves the node kind for; dropped today on every path (inline links, images both run-degraded and resolved-block, and the definitions table's own titles, parsed then dead with the map) —md/link-title-dropped;src/lower/inline.ts:72-84;src/lower/lower.ts:98;src/test-support/conformance-exclusions.ts(LINK_TITLE)src/ast/ast.ts:81;src/block/block.ts:685-700;src/lower/lower.ts:165-208(applyTaskCheckbox, false-return case documented)md/code-block-info-string-dropped;src/lower/lower.ts:117-124$$blocks,\(...\)inline, Parse $$ display-math blocks and \( \) inline math #53) —ContentFormulawith the canonical-plus-source pattern: LaTeX as the source spelling beside whatever canonical form the schema settles; today's preserved-as-text (MathBlockstyleId, inline markerfontFamily) is the interim, not the destination —src/lower/lower.ts:143-152;src/lower/inline.ts:64-67; theMarkdownMathInlineNodecomment insrc/ast/ast.tsContentDocument.metadata; the unmapped remainder (md/front-matter-key-unmappedplus drop) and the non-flat shapes the flat reader silently skips (nested mappings, block lists, multi-line scalars) become a raw front-matter block in the package-level residue channel —src/lower/front-matter.ts:66-87(switch + default-drop); emit inversesrc/emit/front-matter.tsContentListMembershipcarries numId/level only, so "one item, several blocks" and "several items" are indistinguishable, and tables/images in an item lose membership entirely —md/list-item-multi-block-flattened,md/list-item-block-unlisted;lowerListIteminsrc/lower/lower.tsindentLeftPtplus aQuotestyleId, with depth beyond 1 approximate and the container boundary lost —md/blockquote-nested-depth;lowerBlockquoteinsrc/lower/lower.ts;conformance-exclusions.ts(BLOCKQUOTE_STRUCTURE,ADJACENT_SAME_DEPTH)Residue rows — quarantined source channel (per the #22 two-channel model):
source: { format, ... }, restorable on re-emission; the canonical residue-channel use case from build(deps): bump document-schema.js to 2.6.1 #22, and it also fixes a live ambiguity: literal text that happens to look like a tag is indistinguishable from preserved HTML today (RAW_TEXT_TAG_AMBIGUITYexclusion). Current behaviour is preserve-as-literal-text (default),HTMLPreformattedstyleId, or drop underrawHtml: 'drop'— never parsed —src/html/html.ts(recogniser, all 7 block kinds plus the inline grammar);src/lower/lower.ts:130-141; codesmd/raw-html-preserved-as-text,md/raw-html-dropped; fixturessrc/diagnostics/diagnostics.test.ts:110-121,src/lower/lower.test.ts:281-290src/lower/inline.ts:57-62{.class #id}attributes quarantined beside the semantic language word (see the info-string row above)Decisions needed — uncertainty to resolve before the split rows freeze:
key: valuereader matches the de facto convention, but how often real documents use nested shapes it silently skips is unmeasured; the residue half needs the package channel from Add the fidelity construct descriptor kinds additively (contentControl / field / anchor / link / provenance; package-level tables for layers/attachments/destinations) document-schema.js#24.^[...]) have no GFM analogue — the inventory treats the marker-plus-tail-definition pair as the scope and inline notes as out of scope; confirm or widen deliberately rather than by omission.