deps: require trafilatura >=2.2 — the #882 canary fired, close the loop - #6
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The canary worked
tests/test_content.pypinned trafilatura's broken nested-emphasis serializer on purpose, so an upgrade would fail loudly rather than silently change output. It fired:xml.pysource 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 nestedhichainsCI 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):
2.2.0 repairs three of the five silent-omission failures the stub guard was built on:
sbir.gov/awards421 → 3,155 chars — previously returned only the "official website of the United States government" banner with zero award dataatlas.nomic.ai/pricing259 → 1,661The 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_peripheralregenerated. 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.pyis 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.
Verification
ruff·mypy· 252 tests ·check_consistency.py— all green (the consistency gate caught the 247→252 count drift, as designed)🤖 Generated with Claude Code