#!/usr/bin/env python3 """Is any REFUTED claim still asserted, unmarked, elsewhere in the corpus? `REFUTED.md` publishes deaths; it does not enforce them. `sylpheed-port`'s `check-claims` register fails their run when a refuted claim is quoted without a `[refuted]` token, and feeding it four withdrawals immediately flagged three still asserted unmarked -- every one inside a correction they had written themselves. Their point is the one worth stealing: **the token tests for something an author must place, not for language that sounds retracted.** All three read as corrections to a human and the marker fired anyway. This is the equivalent for a prose corpus. For each `* "claim"` in REFUTED.md it searches `docs/` for that exact claim text and reports every occurrence whose neighbourhood carries no refutation marker. ⚠️ Its known weakness, stated rather than discovered: it matches the claim's EXACT wording. A restatement in different words is invisible to it. So a clean run means "no verbatim revival", not "no revival". check_refuted.py [--context N] """ import re import sys from pathlib import Path MARKERS = ("refuted", "REFUTED", "withdrawn", "WITHDRAWN", "retracted", "RETRACTED", "🔴", "~~", "used to say", "used to read", "was wrong", "is wrong", "no longer", "superseded", "corrected", "retracts", "an earlier version", # text explicitly DECLINING to revive a claim reads as an assertion to # a neighbourhood scan; two real hits were exactly this. "does **not** revive", "does not revive") # ⚠️ STRUCTURAL LIMIT, found by running this against the corpus. `BACKLOG.md` is an # APPEND-ONLY DATED LOG: an entry under a 2026-08-12 header recording what was # believed then is history, not revival, and reads identically to a live claim. # A neighbourhood-language detector cannot separate "asserted now" from "recorded # as believed then". sylpheed-port's `check-claims` avoids this by testing for a # TOKEN AN AUTHOR MUST PLACE rather than for language -- their design is right and # this one is a weaker approximation of it. Files that are chronological records # are skipped rather than reported, and that is a real hole, not a fix. CHRONOLOGICAL = {"BACKLOG.md"} CTX = int(sys.argv[sys.argv.index("--context") + 1]) if "--context" in sys.argv else 4 root = Path("docs") ref = root / "re" / "REFUTED.md" claims = [] seen_report = set() for line in ref.read_text().splitlines(): m = re.match(r'\s*\*\s*~?~?"([^"]{25,})"', line) if m: claims.append(m.group(1)) print(f"{len(claims)} quoted claims in REFUTED.md\n") hits = 0 suppressed = [] seen_marked = set() for c in claims: needle = c.strip() for f in root.rglob("*.md"): if f == ref or f.name in CHRONOLOGICAL: continue lines = f.read_text(errors="replace").splitlines() for i, l in enumerate(lines): if needle in l: lo, hi = max(0, i - CTX), min(len(lines), i + CTX + 1) # ⚠️ A +-CTX neighbourhood misses the commonest real marker: a # SECTION HEADER that retracts a whole list. Two false positives # were exactly this -- bullets under "## 🔴 What this retracts", # each bullet a claim being killed, no marker within 4 lines. # Scope, not proximity, is what marks them, so include the # nearest preceding header in the scan. hdr = "" for j in range(i, -1, -1): if re.match(r"#{1,4} ", lines[j]): hdr = lines[j] break near = "\n".join(lines[lo:hi]) + "\n" + hdr marked = any(m.lower() in near.lower() for m in MARKERS) if marked: # dedup like the reported path: two registered claims can be # substrings of one line, which printed it twice. if (f, i) not in seen_marked: seen_marked.add((f, i)) suppressed.append((f, i + 1, needle, l.strip())) continue if (f, i) not in seen_report: seen_report.add((f, i)) hits += 1 print(f"🔴 {f}:{i+1}") print(f' claim: "{needle[:80]}"') print(f" line : {l.strip()[:110]}\n") print(f"{hits} unmarked assertion(s) of a refuted claim") # 🔴 A CLEAN RUN IS NOT A PASS, and until 2026-08-30 it was reported as though it # were. Measured: 8 of 8 mentions of a registered claim in this corpus are # suppressed by marker language, so the count above was 0 whether or not any of # them was a live revival. A planted REAL revival, written into a paragraph that # merely discussed corrections, was missed silently -- the marker words in the # surrounding prose vouched for it. # # sylpheed-port's token-based hook has the opposite bias: it OVER-reports on # well-written corrections, which is the safe direction. This one under-reports, # which is not. So the suppressed set is printed rather than hidden. print(f"{len(suppressed)} mention(s) suppressed by nearby marker language " f"-- NOT verified, only vouched for by neighbouring prose") if "--show-marked" in sys.argv: for f, ln, c, l in suppressed: print(f" · {f}:{ln}\n claim: \"{c[:70]}\"\n line : {l[:100]}") elif suppressed: print(" re-run with --show-marked to read them")