Skip to main content

Module markdown

Module markdown 

Source
Expand description

The CommonMark referee — one definition of “code” for every content reader in the engine.

The engine used to carry two referees for the same markdown. Section splitting and wiki-link scanning ran on a hand-rolled line scanner that recognised exactly one shape of code block (a column-0 backtick fence, closed by any backtick-prefixed line); section content validation (crate::section_format, agent-toolbox plan 08) ran on pulldown-cmark. They disagreed in six verified ways, and the disagreement sat on the write path — the validator judged content the splitter had already mis-partitioned:

  1. indented code blocks were not masked at all;
  2. legally indented fences (1–3 spaces — the normal shape inside a list item) did not open a block;
  3. tilde fences were unhandled;
  4. a closing line carrying an info string — content, per CommonMark — closed the block early;
  5. fences inside blockquotes were not masked;
  6. the opening fence was stored but never compared on close, so a -fenced block ended on the first ``` ````.

section_format’s header states the thesis this module generalises: a reader that disagrees with the renderer every agent uses sends repair loops that cannot converge. The parser is the referee.

Both masks preserve byte offsets and line counts exactly — every masked byte becomes an ASCII space except \n and \r, so a caller may scan the masked copy and slice the original by the offsets it finds. That is the whole mechanism: boundaries come from the parser, bytes come from the original.

Heading recognition is deliberately not widened here. A section is still a column-0 ATX ## line and nothing else — setext headings and indented ATX create sections nowhere. This module fixes what code blocks hide; it does not change what counts as a heading.

§Give these functions a BODY, never a whole entity file

Frontmatter is not markdown. Handing it to a CommonMark parser invents block structure that is not there: a YAML value that reads as a fence opener — legal at 1–3 spaces, and honoured here since indented fences were fixed — opens a code block that runs past the --- terminator to end of file and blanks the entire body. Every ## heading, every [[link]], and every git conflict marker in that file becomes invisible to whatever scans the result.

Callers holding a section body are already safe — section bodies are frontmatter-free by construction. A caller holding a raw file or a git blob is not, and must trim it first with crate::entity::parser::body_after_frontmatter. This bit the engine three times during the migration that introduced these masks: in parse_markdown, in the git-branch ripple scanner, and in the merge-conflict guard — the last one silently defeating a data-integrity check. Any new caller is the fourth unless it trims.

Functions§

closing_fence_if_unterminated
When text — a section body — ends inside an unterminated fenced code block that would swallow whatever the caller writes after it, return the closing fence that terminates it. None when the text is safely self-delimiting: balanced fences, indented code (a column-0 heading line ends it), or a fence inside a container a following column-0 line closes implicitly (blockquote, list item).
mask_code_blocks
Mask every CommonMark code block, preserving byte offsets and line count.
mask_code_blocks_and_spans
Mask every CommonMark code block and every inline code span, preserving byte offsets and line count.
parser_options
The engine’s CommonMark dialect — one Options for every reader, so the block model the masks see is the block model crate::section_format checks against.