fix(docs-governance): read a comment that starts a list item or a block quote line as an HTML block

Only indentation counted as the start of a block, so '- <!--' was inline HTML, and a blank line before its closer turned it into text. Its hidden links were then reported. Block quote and list markers before the opener now count too, as in CommonMark. A comment after text in a list item stays inline.
This commit is contained in:
L4XB committed 2026-10-23 04:07:23 +02:00
1 parent 46e3480c93
commit 4e1dc5b0f9
5 files changed
+49 -26

No files matched your search

@@ -19,6 +19,8 @@ REFERENCE_USAGE_RE = re.compile(
r"(?<![\\])(?:\\\\)*\[(?P<text>[^\]\n]+)\](?:\[(?P<label>[^\]\n]*)\])?"
)
BLANK_LINE_RE = re.compile(r"\n[ \t]*\r?\n")
# -- what may come before a block on its line: indentation and block quote or list markers --
CONTAINER_PREFIX_RE = re.compile(r"(?:[ \t]*(?:>|[-+*][ \t]|\d{1,9}[.)][ \t]))*[ \t]*")
LINK_TITLE_RE = re.compile(
r'\s+(?:"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|\([^()]*\))\s*$'
)
@@ -72,15 +74,18 @@ def _is_escaped(text: str, index: int) -> bool:
def _html_comment_end(text: str, start: int, closing: int, blank: int) -> int:
"""Where the comment that opens at `start` and closes at `closing` ends, or -1 if it is text.
One that starts a line, after at most three spaces, opens an HTML block. The block may span
blank lines and takes the rest of the line that holds its closer. One inside a line is inline
HTML, which cannot outlast its paragraph, so when the first blank line after it, at `blank`,
comes before its closer, it is text.
One that starts a block, after nothing but indentation and block quote or list markers on
its line (as in `- <!--`), opens an HTML block. The block may span blank lines and takes the
rest of the line that holds its closer. One after text on its line is inline HTML, which
cannot outlast its paragraph, so when the first blank line after it, at `blank`, comes before
its closer, it is text.
"""
indent = 0
while indent < 4 and start - indent > 0 and text[start - indent - 1] == " ":
indent += 1
if indent < 4 and (start - indent == 0 or text[start - indent - 1] == "\n"):
# -- only the 64 characters before the opener are read, so a long line stays cheap --
window = max(0, start - 64)
line_start = text.rfind("\n", window, start) + 1
if (line_start or not window) and CONTAINER_PREFIX_RE.fullmatch(
text, line_start, start
):
newline = text.find("\n", closing + 3)
return len(text) if newline == -1 else newline
return -1 if blank < closing else closing + 3
@@ -10,7 +10,7 @@
"id": "hand-seeded",
"source": "manual-curation-pending-regeneration"
},
"registryDigest": "05f9875a0e4eaef5502902595e48b201ed8d83df7dec8587d1cfebaf08efa132",
"registryDigest": "e6f889b5f158501ed2a6b7dc99f67dd7baa29127b0f75d77bccb59cfab94a676",
"schemaVersion": 1,
"triggers": {
"skill:agent-runtime-gateway-smoke-test": [
@@ -19,6 +19,8 @@ REFERENCE_USAGE_RE = re.compile(
r"(?<![\\])(?:\\\\)*\[(?P<text>[^\]\n]+)\](?:\[(?P<label>[^\]\n]*)\])?"
)
BLANK_LINE_RE = re.compile(r"\n[ \t]*\r?\n")
# -- what may come before a block on its line: indentation and block quote or list markers --
CONTAINER_PREFIX_RE = re.compile(r"(?:[ \t]*(?:>|[-+*][ \t]|\d{1,9}[.)][ \t]))*[ \t]*")
LINK_TITLE_RE = re.compile(
r'\s+(?:"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|\([^()]*\))\s*$'
)
@@ -72,15 +74,18 @@ def _is_escaped(text: str, index: int) -> bool:
def _html_comment_end(text: str, start: int, closing: int, blank: int) -> int:
"""Where the comment that opens at `start` and closes at `closing` ends, or -1 if it is text.
One that starts a line, after at most three spaces, opens an HTML block. The block may span
blank lines and takes the rest of the line that holds its closer. One inside a line is inline
HTML, which cannot outlast its paragraph, so when the first blank line after it, at `blank`,
comes before its closer, it is text.
One that starts a block, after nothing but indentation and block quote or list markers on
its line (as in `- <!--`), opens an HTML block. The block may span blank lines and takes the
rest of the line that holds its closer. One after text on its line is inline HTML, which
cannot outlast its paragraph, so when the first blank line after it, at `blank`, comes before
its closer, it is text.
"""
indent = 0
while indent < 4 and start - indent > 0 and text[start - indent - 1] == " ":
indent += 1
if indent < 4 and (start - indent == 0 or text[start - indent - 1] == "\n"):
# -- only the 64 characters before the opener are read, so a long line stays cheap --
window = max(0, start - 64)
line_start = text.rfind("\n", window, start) + 1
if (line_start or not window) and CONTAINER_PREFIX_RE.fullmatch(
text, line_start, start
):
newline = text.find("\n", closing + 3)
return len(text) if newline == -1 else newline
return -1 if blank < closing else closing + 3
@@ -19,6 +19,8 @@ REFERENCE_USAGE_RE = re.compile(
r"(?<![\\])(?:\\\\)*\[(?P<text>[^\]\n]+)\](?:\[(?P<label>[^\]\n]*)\])?"
)
BLANK_LINE_RE = re.compile(r"\n[ \t]*\r?\n")
# -- what may come before a block on its line: indentation and block quote or list markers --
CONTAINER_PREFIX_RE = re.compile(r"(?:[ \t]*(?:>|[-+*][ \t]|\d{1,9}[.)][ \t]))*[ \t]*")
LINK_TITLE_RE = re.compile(
r'\s+(?:"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|\([^()]*\))\s*$'
)
@@ -72,15 +74,18 @@ def _is_escaped(text: str, index: int) -> bool:
def _html_comment_end(text: str, start: int, closing: int, blank: int) -> int:
"""Where the comment that opens at `start` and closes at `closing` ends, or -1 if it is text.
One that starts a line, after at most three spaces, opens an HTML block. The block may span
blank lines and takes the rest of the line that holds its closer. One inside a line is inline
HTML, which cannot outlast its paragraph, so when the first blank line after it, at `blank`,
comes before its closer, it is text.
One that starts a block, after nothing but indentation and block quote or list markers on
its line (as in `- <!--`), opens an HTML block. The block may span blank lines and takes the
rest of the line that holds its closer. One after text on its line is inline HTML, which
cannot outlast its paragraph, so when the first blank line after it, at `blank`, comes before
its closer, it is text.
"""
indent = 0
while indent < 4 and start - indent > 0 and text[start - indent - 1] == " ":
indent += 1
if indent < 4 and (start - indent == 0 or text[start - indent - 1] == "\n"):
# -- only the 64 characters before the opener are read, so a long line stays cheap --
window = max(0, start - 64)
line_start = text.rfind("\n", window, start) + 1
if (line_start or not window) and CONTAINER_PREFIX_RE.fullmatch(
text, line_start, start
):
newline = text.find("\n", closing + 3)
return len(text) if newline == -1 else newline
return -1 if blank < closing else closing + 3
+9 -1
View File
@@ -292,6 +292,11 @@ def test_artifact_scope_reads_html_comments_as_commonmark_does(project: Path) ->
# -- the line that holds its closer --
"<!--\n[hidden](missing-hidden.md)\n\n[hidden2](missing-hidden2.md)\n-->\n\n"
"<!-- c --> [rest](missing-rest.md)\n\n"
# -- so does one that starts a list item or a block quote line --
"- <!--\n\n [listed](missing-listed.md)\n -->\n\n"
"> <!-- c --> [quoted](missing-quoted.md)\n\n"
# -- after text in a list item, a comment is inline again --
"- item <!-- c\n\n [item-real](missing-item-real.md)\n -->\n\n"
# -- a comment where a destination must begin does not make a definition --
"[foo]: <!-- note --> missing-definition.md\n\n[foo]\n\n"
"[bar]: <!-- only a comment -->\n\n[bar]\n\n"
@@ -310,10 +315,13 @@ def test_artifact_scope_reads_html_comments_as_commonmark_does(project: Path) ->
assert "Broken Markdown link: 'guide.md' -> 'missing-real.md'" in result.stdout
assert "missing-hidden" not in result.stdout
assert "missing-rest.md" not in result.stdout
assert "missing-listed.md" not in result.stdout
assert "missing-quoted.md" not in result.stdout
assert "Broken Markdown link: 'guide.md' -> 'missing-item-real.md'" in result.stdout
assert "missing-definition.md" not in result.stdout
assert "Broken Markdown link: 'guide.md' -> 'missing-titled.md'" in result.stdout
assert "missing-fenced.md" not in result.stdout
assert result.stdout.count("Broken Markdown link") == 4, result.stdout
assert result.stdout.count("Broken Markdown link") == 5, result.stdout
def test_artifact_scope_ignores_title_with_escaped_quotes(project: Path) -> None: