Minimal input
What markdown-it-py does (3.0.0 and 4.2.0; the JS original 14.3.2 behaves identically)
<br> is taken as a lazy continuation of the list item's paragraph, so ## Next becomes a second heading:
<h2>Opts</h2>
<ul>
<li>a
<br></li>
</ul>
<h2>Next</h2>
What cmark-gfm (via cmarkgfm 2025.10.22) and commonmark.py 0.9.2 (port of commonmark.js) do
The list is closed, <br> opens a type-7 HTML block, and the block swallows ## Next until a blank line:
<h2>Opts</h2>
<ul>
<li>a</li>
</ul>
<br>
## Next
The same holds for <span>, </pre> and <img src="x">. With a blank line between - a and the tag, all three implementations agree.
Why it matters
Two reference-lineage implementations (cmark, commonmark.js) agree against markdown-it. Anything that maps headings to line numbers against GitHub's rendering sees a phantom heading here.
Mechanism: rules_block/html_block.py marks the type-7 sequence as "cannot terminate a paragraph", and list.py's terminator check therefore lets the line continue the item's paragraph lazily. cmark performs the "can this line start a block?" check against the outer container once the list item failed to match, so the type-7 restriction (which is about interrupting a paragraph) does not apply there.
Flipping the terminate flag for type 7 is not a fix — measured: it makes <br> interrupt a paragraph with no list at all, where all implementations currently agree.
Found while building a differential test suite for a Markdown section parser (amirbiron/CodeBot#3418); the shape is pinned there as a known divergence.
Minimal input
What markdown-it-py does (3.0.0 and 4.2.0; the JS original 14.3.2 behaves identically)
<br>is taken as a lazy continuation of the list item's paragraph, so## Nextbecomes a second heading:What cmark-gfm (via cmarkgfm 2025.10.22) and commonmark.py 0.9.2 (port of commonmark.js) do
The list is closed,
<br>opens a type-7 HTML block, and the block swallows## Nextuntil a blank line:The same holds for
<span>,</pre>and<img src="x">. With a blank line between- aand the tag, all three implementations agree.Why it matters
Two reference-lineage implementations (cmark, commonmark.js) agree against markdown-it. Anything that maps headings to line numbers against GitHub's rendering sees a phantom heading here.
Mechanism:
rules_block/html_block.pymarks the type-7 sequence as "cannot terminate a paragraph", andlist.py's terminator check therefore lets the line continue the item's paragraph lazily. cmark performs the "can this line start a block?" check against the outer container once the list item failed to match, so the type-7 restriction (which is about interrupting a paragraph) does not apply there.Flipping the terminate flag for type 7 is not a fix — measured: it makes
<br>interrupt a paragraph with no list at all, where all implementations currently agree.Found while building a differential test suite for a Markdown section parser (amirbiron/CodeBot#3418); the shape is pinned there as a known divergence.