Skip to content

deps: require trafilatura >=2.2 — the #882 canary fired, close the loop - #6

Merged
Wynelson94 merged 2 commits into
mainfrom
fix/trafilatura-2.2-close-882
Aug 11, 2026
Merged

deps: require trafilatura >=2.2 — the #882 canary fired, close the loop#6
Wynelson94 merged 2 commits into
mainfrom
fix/trafilatura-2.2-close-882

Conversation

@Wynelson94

Copy link
Copy Markdown
Owner

The canary worked

tests/test_content.py pinned trafilatura's broken nested-emphasis serializer on purpose, so an upgrade would fail loudly rather than silently change output. It fired:

  • trafilatura 2.2.0 published 2026-07-31; adbar/trafilatura#882 — the issue this repo filed — closed the same day
  • The fix is real, verified in xml.py source rather than release notes: _md_wrap() keeps flanking whitespace outside the emphasis marker (so the closing delimiter is right-flanking per CommonMark) plus _collapse_emphasis() for nested hi chains
  • The pinned test failed on cue, and is now flipped to pin the corrected serialization as a permanent regression guard

CI was not red yet only because it last ran 07-24 and triggers on push/PR — it would have gone red on the next push regardless of this branch.

Adoption was measured, not assumed

Replayed all 194 cached pages under both versions (zero network — the cache stores raw HTML):

better under 2.2.0 77
worse 42
identical 75
corpus total +2.89%
catastrophic (≥50% loss) 0

2.2.0 repairs three of the five silent-omission failures the stub guard was built on:

  • sbir.gov/awards 421 → 3,155 chars — previously returned only the "official website of the United States government" banner with zero award data
  • atlas.nomic.ai/pricing 259 → 1,661
  • NOAA SBIR archive 361 → 548

The stub guard now fires on 2 of 194 (was 5 of 162) because the corpus got better, not because the guard got weaker. Calibration table updated in place; thresholds unchanged.

Two pins moved — deliberately, neither quietly

article_peripheral regenerated. It padded its body with 14 byte-identical <p> tags; 2.2.0 deduplicates identical blocks, so the body collapsed 1,973 → 166 chars and the page started tripping the stub guard — while the real page it models went the other way (1,827 → 13,560, +642%). The synthetic had stopped modelling reality in both magnitude and direction. scripts/gen_article_peripheral_fixture.py is now its reproducible source, and the rebuild re-verifies every clause of the original contract (4 figures, max 3 per 1,500-char window, silent under shipped guards, warns at floor=1).

Its tests were why nobody noticed: the body assertion checked only "Join Forces" — which the page title satisfies — so a 92% body collapse passed CI. It now pins real body sentences, total guard silence, and that the silence comes from the cluster floor rather than a toothless page.

quo's collapsed-column assertion narrowed. Quo is just as lossy (still 4 of 24 figures, 1 of 3 plan names) but 2.2.0 emits the repeated cell once instead of three times, so the repetition signature the guard keys on is gone — on quo and on every page in the replay. The dropped-price guard still catches quo, so the page is not silent. The substantive loss is pinned separately and the coverage gap is recorded in CHANGELOG under Known issues rather than papered over.

⚠️ Known issue this surfaces: the collapsed-column guard now has zero real-page coverage. Tracked for the follow-up guard-coverage pass, not fixed here.

Verification

  • ruff · mypy · 252 tests · check_consistency.py — all green (the consistency gate caught the 247→252 count drift, as designed)
  • Full live eval re-run on 2.2.0 per the standing post-upgrade protocol — report added to this PR

🤖 Generated with Claude Code

Wynelson94 and others added 2 commits August 11, 2026 13:10
The canary worked. tests/test_content.py pinned trafilatura's BROKEN nested-emphasis
serializer on purpose so an upgrade would fail loudly. trafilatura 2.2.0 shipped the
fix on 2026-07-31 (issue #882 closed the same day), the test failed exactly as
designed, and it is now flipped to pin the CORRECT serialization as a permanent
regression guard.

Adoption was measured, not assumed. Replaying all 194 cached pages under both
versions: 77 extract more, 42 less, 75 identical, corpus total +2.89%, zero
catastrophic losses. 2.2.0 repairs three of the five silent-omission failures the
stub guard was built on — sbir.gov/awards (421 -> 3,155 chars; it previously
returned only the "official website of the United States government" banner with
no award data), atlas.nomic.ai/pricing (259 -> 1,661) and the NOAA SBIR archive.

Two pins moved, both deliberately and neither quietly:

- article_peripheral (the false-positive fixture) was regenerated. It padded its
  body with 14 byte-identical <p> tags; 2.2.0 deduplicates identical blocks, so the
  body collapsed 1,973 -> 166 chars and the page began tripping the stub guard —
  while the real page it models went the other way (+642%). The synthetic had
  stopped modelling reality in both magnitude and direction.
  scripts/gen_article_peripheral_fixture.py is now its reproducible source, and the
  rebuild re-verifies every clause of the original contract.

  Its tests were the reason nobody noticed: the body assertion checked only
  "Join Forces", which the page TITLE satisfies, so a 92% collapse passed CI. It
  now pins real body sentences, total guard silence, and that the silence comes
  from the cluster floor rather than a toothless page.

- The quo collapsed-column assertion was narrowed. Quo is just as lossy (still 4 of
  24 figures, 1 of 3 plan names) but 2.2.0 emits the repeated cell once instead of
  three times, so the repetition signature the guard keys on is gone — on quo and on
  every page in the cache replay. The dropped-price guard still catches quo, so the
  page is not silent. The substantive loss is now pinned separately, and the
  zero-real-page-coverage gap is recorded rather than papered over.

Stub-guard calibration table recalibrated against 2.2.0: thresholds unchanged, the
corpus improved. Fires on 2 of 194 (was 5 of 162).

Gate: ruff, mypy, 252 tests, consistency all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Standing protocol: re-run the eval after every dependency upgrade. This clears BOTH
outstanding debts — the 2.2.0 upgrade trigger, and the 07-24 stub guard that shipped
without a run (evals were last run 07-16, 26 days stale).

All 9 gates PASS. 40 items + a 45-scrape burst (40 ok, 5 honest refusals, 0 FAIL,
0 exceptions). Zero fabrications, zero cache poison, zero unhandled exceptions,
baselines stable across the 5 pinned immutables.

The single FAIL is wiki_idaho's $74,900 infobox figure — the documented single-figure
floor, the same tolerated item as the 2026-07-16 run 2, so it is stable rather than a
regression. No guard can arm on one figure without spamming false positives.

linkedin_fp passes live (0/2 peripheral figures shown, no warning), so the false-positive
protection the regenerated fixture pins offline also holds against the real page.

This report is stamped `tearsheet 0.1.5` — the first one that is. The editable install
had gone stale at 0.1.0, which silently mislabeled both 07-16 reports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Wynelson94
Wynelson94 merged commit 06296c8 into main Aug 11, 2026
2 checks passed
@Wynelson94
Wynelson94 deleted the fix/trafilatura-2.2-close-882 branch August 11, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant