fix: declare markdown-it-py floor and make the autolink round-trip tail run visibly (#801) (#802)

* fix: declare markdown-it-py floor and make the autolink round-trip tail run visibly (#801)

The no-link_open round-trip tail of test_gfm_bare_urls_emails_and_schemes_
cannot_autolink soft-imported markdown-it-py (undeclared in requirements-
dev.txt) and silently returned when absent, so it had never run in CI, while
ambient markdown-it-py 2.x failed it on clean main (2.2.0 + linkify-it-py
2.0.3, reported in #799). Verified dividing line: 2.2.0 fails, 3.0.0 and
4.0.0 pass with linkify-it-py held at 2.0.3.

- Split the tail into test_escaped_markdown_yields_no_linkify_tokens_on_
  round_trip, gated by pytest.importorskip minversions (markdown_it 3.0.0,
  linkify_it 2.0.3): ambient-old environments skip visibly.
- Declare markdown-it-py>=3.0 + linkify-it-py>=2.0.3 in requirements-dev.txt
  with a reverse pointer at the consuming test, so CI exercises the round
  trip for the first time.
- Move the identical soft-import tail in test_renderer_neutralizes_markdown_
  active_inventory_path (newly activated in CI by the same declaration) to
  the same importorskip idiom; no floor needed (default CommonMark, no
  linkify) — verified passing under 2.2.0, 3.0.0, and 4.0.0.
- Consolidate the triplicated hostile-row construction in
  test_evidence_rows.py into one _hostile_row helper.

Renderer behavior and every renderer-side assertion are unchanged.
Verification: both full files 414 passed under markdown-it-py 4.0.0;
affected tests re-run under 2.2.0 (pass + visible skip) and 3.0.0 (pass).

Closes #801

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013R81d1YwGvJAznkPKk9gNw

* fix: flatten inline token children in the newly activated manifest markdown scan (#801)

Cross-model review (codex, xhigh) on PR #802 flagged that the twin test's
token scan iterated only top-level tokens, but markdown-it nests link_open /
image / html_inline under inline tokens' children — so the assertion could
only ever catch html_block. Verified empirically, then flattened children
into the scan (same idiom as the evidence-rows round-trip test).
Strengthened assertion passes under markdown-it-py 2.2.0, 3.0.0, and 4.0.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013R81d1YwGvJAznkPKk9gNw

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Edward Cheng-I Wu
2026-08-27 10:35:29 +08:00
committed by GitHub
parent e5718cbf58
commit 30ad279cdf
4 changed files with 45 additions and 49 deletions
+2
View File
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
- **CJK titles no longer fail the shared exact-title gate in the four index resolvers (#798).** `chinese_literature_client.py` already carried a Chinese-aware `normalize_cn_title` / `has_cjk` (#431 §"Chinese title matching"), but the four index resolvers (Semantic Scholar / OpenAlex / Crossref / arXiv) never saw it — they read the ASCII-centric `_text_similarity` helpers, where `.lower()` folds case but never width ( U+FF30 never reaches P U+0050) and `string.punctuation` contains none of `。`, `《》`, or U+3000. A Chinese paper an index served in a different-but-legitimate typesetting therefore missed on **two** paths: the DOI-keyed cross-check, which gates on the fuzzy ratio *alone* and scored a fullwidth spelling of the identical title at **0.625 — under the 0.70 floor — reporting a correct DOI as `DOI_MISMATCH`**; and the title-fallback search, which requires ratio **and** exact-normalized equality and so fell to `unresolvable`. Both feed the `*_unmatched` contamination signals, so a genuine paper could accumulate *k* across indexes and be rendered `CONTAMINATED-TRIANGULATION-UNMATCHED` — the protocol doc's own "P0, next to the word 'fabricated'". The failure was invisible to the English test corpus for a measurable reason: a Han character is a whole word, so the same six-codepoint corruption is 37.5% of a 16-character Chinese title but 8% of its 68-character English equivalent (measured 0.625 vs 0.912). Fix promotes `has_cjk` / `normalize_cn_title` into `scripts/_text_similarity.py` (behaviorally equivalent — the promotion hoists locals to module constants, precompiles the regex, and rewrites comments; the CJK client now re-imports rather than keeping a private copy, per the #128 anti-drift goal), adds the Chinese-aware form to `exact_normalized_title` as an additive third branch, and folds it into `_similarity` through the existing `max`. Both are gated on **both** sides carrying a Han ideograph, so every non-CJK verdict and every non-CJK *ratio* is provably unchanged — pinned by two oracle tests that re-state the pre-fix `exact_normalized_title` and `_similarity` formulas in full (the latter including the dotted-acronym branch) and assert exact agreement in both directions, so a raised score is caught as loudly as a lowered one. Cross-script and romanized pairs still cannot match (no translation oracle), Simplified/Traditional is still not folded, and an empty normalized key still never matches. Under the Chinese-aware form the ratio also regains discriminative power on the motivating pair: 1.000 for the identical title against an unchanged 0.510 for a genuinely different paper (the base form separated 0.566 from 0.510 — almost nothing). Also measured and pinned: on the motivating pair the pre-fix ratio was actively *anti-correlated* — the identical title scored **0.606 while a genuinely different paper scored 0.645**, so the wrong paper ranked higher. The repair leaves the unrelated pair's score byte-identical (0.6452 before and after; it is not equal under the CJK form, so nothing is folded in) and lifts only the true match, restoring the ordering title ranking depends on. Not in scope, and unchanged: the base ASCII normalization still collapses `ER+`/`ER-` and `p53`/`P53` because it maps ASCII punctuation to whitespace and lowercases, and `exact_normalized_title` ORs that form in; the fuzzy floor also remains a weak separator for CJK generally (that 0.645 near-miss is pre-existing and untouched here). 32 new tests, including 5 integration tests through the real Crossref client covering both broken paths, each verified to fail against the pre-fix module, plus a monkeypatched detonator pinning that the Chinese DOI-path matcher never consults the fuzzy ratio in either direction.
- **Autolink round-trip test no longer rides on an undeclared, version-sensitive dependency (#801).** `test_gfm_bare_urls_emails_and_schemes_cannot_autolink` soft-imported markdown-it-py — not listed in `requirements-dev.txt` — and silently returned when it was absent, so its no-`link_open` tail had never actually run in CI, while an ambient markdown-it-py 2.x (whose linkify pass still tokenizes the escaped forms as links; reproduced under 2.2.0 + linkify-it-py 2.0.3, reported by @akshath-raj in #799) failed the test on clean `main`. Verified dividing line: 2.2.0 fails, 3.0.0 and 4.0.0 pass with linkify-it-py 2.0.3 held constant. The tail is split into its own test (`test_escaped_markdown_yields_no_linkify_tokens_on_round_trip`) gated by `pytest.importorskip("markdown_it", minversion="3.0.0")` + `pytest.importorskip("linkify_it", minversion="2.0.3")` — ambient-old environments now skip visibly instead of failing or passing vacuously — and `requirements-dev.txt` declares `markdown-it-py>=3.0` + `linkify-it-py>=2.0.3` with a reverse pointer at the consuming test, so CI exercises the round-trip for the first time. The same declaration also newly activates the identical soft-import tail in `test_renderer_neutralizes_markdown_active_inventory_path` (`scripts/test_build_submission_packet_manifest.py`), which moves to the same `importorskip` idiom (no version floor: it parses default CommonMark without linkify) and gains the inline-children flatten its top-level-only token scan was missing — `link_open` / `image` / `html_inline` nest under `inline` tokens, so the pre-existing assertion could only ever catch `html_block`; its strengthened assertions verified under markdown-it-py 2.2.0, 3.0.0, and 4.0.0. The triplicated hostile-row construction in `test_evidence_rows.py` is consolidated into one `_hostile_row` helper. Renderer behavior and every renderer-side assertion are unchanged.
## [3.21.1] - 2026-08-24 — Bounded workflow substrates, sealed bakeoffs, and transport hardening
### Fixed
+4
View File
@@ -3,3 +3,7 @@ ruamel.yaml>=0.17
jsonschema[format]>=4.17
pypdf
defusedxml
# Floors consumed by scripts/test_evidence_rows.py::test_escaped_markdown_yields_no_linkify_tokens_on_round_trip (#801).
# markdown-it-py < 3 linkifies escaped bare URLs; dropping these lines makes that test silently skip in CI.
markdown-it-py>=3.0
linkify-it-py>=2.0.3
@@ -2416,14 +2416,14 @@ def test_renderer_neutralizes_markdown_active_inventory_path(
for line in observation_lines.splitlines()
) == 1
try:
from markdown_it import MarkdownIt
except ImportError:
return
tokens = MarkdownIt().parse(rendered)
markdown_it = pytest.importorskip("markdown_it")
tokens = markdown_it.MarkdownIt().parse(rendered)
# link_open / image / html_inline nest under inline tokens' children;
# only html_block appears at the top level.
flattened = [child for token in tokens for child in (token.children or [])]
assert not any(
token.type in {"link_open", "image", "html_inline", "html_block"}
for token in tokens
for token in [*tokens, *flattened]
)
+33 -43
View File
@@ -1460,21 +1460,25 @@ def test_source_replay_checks_hidden_off_page_rows(
assert "EVR-PAGE-0025" in rendered or r"EVR\-PAGE\-0025" in rendered
def _hostile_row(input_fixture: dict[str, Any], text: str) -> dict[str, Any]:
return er.build(
_raw_row(
input_fixture,
anchor_value="missing",
claim__text=text,
detail=text,
source__display_label=text,
),
None,
)
def test_line_separators_newlines_tabs_and_bidi_are_visible_data_not_structure(
input_fixture: dict[str, Any]
) -> None:
_runtime_required()
hostile = "first\n## forged\tcell\u202eRTL\u2028line\u2029paragraph"
row = er.build(
_raw_row(
input_fixture,
anchor_value="missing",
claim__text=hostile,
detail=hostile,
source__display_label=hostile,
),
None,
)
row = _hostile_row(input_fixture, hostile)
markdown = er.render_markdown([row])
html_output = er.render_html([row])
for rendered in (markdown, html_output):
@@ -1497,40 +1501,23 @@ def test_c1_nel_csi_and_osc_are_visible_ascii_not_raw_controls(
input_fixture: dict[str, Any],
) -> None:
_runtime_required()
hostile = f"before{codepoint}after"
row = er.build(
_raw_row(
input_fixture,
anchor_value="missing",
claim__text=hostile,
detail=hostile,
source__display_label=hostile,
),
None,
)
row = _hostile_row(input_fixture, f"before{codepoint}after")
for rendered in (er.render_markdown([row]), er.render_html([row])):
assert codepoint not in rendered
assert escape in rendered
_AUTOLINK_PROBE_PAYLOAD = (
"https://evil.example/path user@example.com ftp://evil.example/file "
"javascript:alert(1)"
)
def test_gfm_bare_urls_emails_and_schemes_cannot_autolink(
input_fixture: dict[str, Any]
) -> None:
_runtime_required()
payload = (
"https://evil.example/path user@example.com ftp://evil.example/file "
"javascript:alert(1)"
)
row = er.build(
_raw_row(
input_fixture,
anchor_value="missing",
claim__text=payload,
detail=payload,
source__display_label=payload,
),
None,
)
row = _hostile_row(input_fixture, _AUTOLINK_PROBE_PAYLOAD)
markdown = er.render_markdown([row])
html_output = er.render_html([row])
assert r"https\:\/\/evil\.example\/path" in markdown
@@ -1540,14 +1527,17 @@ def test_gfm_bare_urls_emails_and_schemes_cannot_autolink(
assert "<a" not in html_output.lower()
assert "href=" not in html_output.lower()
try:
from markdown_it import MarkdownIt
except ImportError:
return
try:
tokens = MarkdownIt("commonmark", {"linkify": True}).enable("linkify").parse(markdown)
except ModuleNotFoundError: # markdown-it can be installed without linkify-it-py.
return
def test_escaped_markdown_yields_no_linkify_tokens_on_round_trip(
input_fixture: dict[str, Any]
) -> None:
_runtime_required()
# markdown-it-py < 3 linkifies these escaped forms; the floors are declared in
# requirements-dev.txt so CI always exercises this round-trip (#801).
markdown_it = pytest.importorskip("markdown_it", minversion="3.0.0")
pytest.importorskip("linkify_it", minversion="2.0.3")
markdown = er.render_markdown([_hostile_row(input_fixture, _AUTOLINK_PROBE_PAYLOAD)])
tokens = markdown_it.MarkdownIt("commonmark", {"linkify": True}).enable("linkify").parse(markdown)
flattened = [child for token in tokens for child in (token.children or [])]
assert all(token.type != "link_open" for token in [*tokens, *flattened])