Files
Sylpheed/tools/re-capture/check_refuted.py
sylph-decoder b2a2c6a834 tools: assert the scan boundary in check_refuted's self-test
sylpheed-port's point: a control that runs where the tool does not look proves
nothing about the tool. Case 2 plants a revival INSIDE the scanned root and demands
exit 1; a new case plants the same text OUTSIDE it and demands exit 0. The pair
asserts the scan boundary is real rather than leaving it to be reasoned about --
which is how I had it, correctly but only in my head.

Verified first by hand: identical plant text gives exit 1 inside the root and 0
outside it. Five cases now, all passing, and the real corpus still runs clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 01:24:51 +00:00

203 lines
10 KiB
Python
Executable File

#!/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 os
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
# Overridable so the harness self-test can drive the REAL machinery over a
# synthetic corpus instead of reasoning about what it would do.
if "--selftest" in sys.argv:
# ── HARNESS SELF-TEST ────────────────────────────────────────────────────
# sylpheed-port closed this gap first: their controls asserted
# failure-on-perturbation but nothing asserted that a BROKEN harness reports
# broken. Their stub is a check that cannot fail; the equivalent here is a
# register with NO CLAIMS LOADED, which reports clean forever.
#
# These push synthetic corpora through this script as a SUBPROCESS and read
# its real exit code. An earlier version of their test reasoned about what
# the machinery would do instead of running it -- the error this whole thread
# is about, committed inside the tool built to prevent it.
#
# Exit convention, theirs: 0 fine · 1 a real check failed · 2 the HARNESS is
# broken and nothing it reports can be trusted.
import subprocess, tempfile, textwrap
def _corpus(d, refuted, other):
(d / "re").mkdir(parents=True, exist_ok=True)
(d / "re" / "REFUTED.md").write_text(refuted)
(d / "re" / "other.md").write_text(other)
CLAIM = 'the synthetic widget is on the disc nowhere'
REG = f'* "{CLAIM}"\n'
cases = [
("clean corpus, claim not revived", REG, "Nothing to see here.\n", 0),
("verbatim revival, no marker", REG,
f"A live assertion: {CLAIM} and that is that.\n", 1),
("revival WITH a marker nearby", REG,
f"~~{CLAIM}~~ was refuted on 2026-01-01.\n", 0),
("EMPTY REGISTER — must refuse, not pass", "no quoted claims at all\n",
f"A live assertion: {CLAIM}.\n", 2),
# sylpheed-port's point: a control that runs where the tool does not look
# proves nothing about the tool. Case 2 plants INSIDE the scanned root and
# demands exit 1; this plants the SAME text OUTSIDE it and demands exit 0.
# The pair asserts that the scan boundary is real, instead of leaving it
# to be reasoned about.
("plant OUTSIDE the scanned root — tool must not see it", REG, None, 0),
]
bad = 0
print("── check_refuted harness self-test ──", flush=True)
with tempfile.TemporaryDirectory() as td:
for name, refuted, other, want in cases:
d = Path(td) / name.replace(" ", "_").replace(",", "")
if other is None:
# register inside the root, revival text deliberately outside it
(d / "re").mkdir(parents=True, exist_ok=True)
(d / "re" / "REFUTED.md").write_text(refuted)
out = Path(td) / (d.name + "_elsewhere")
out.mkdir(exist_ok=True)
(out / "other.md").write_text(f"A live assertion: {CLAIM}.\n")
else:
_corpus(d, refuted, other)
env = dict(os.environ, CHECK_REFUTED_ROOT=str(d))
r = subprocess.run([sys.executable, __file__], env=env,
capture_output=True, text=True)
n = 0
for line in r.stdout.splitlines():
if "quoted claims in REFUTED.md" in line:
n = int(line.split()[0])
ok = (r.returncode == want)
bad += not ok
print(f" {'' if ok else '🔴'} {name:38} exit={r.returncode} "
f"(want {want}), claims={n}")
if bad:
print("🔴 HARNESS SELF-TEST FAILED — nothing this tool reports can be "
"trusted.", flush=True)
sys.exit(2)
print(" ✅ harness self-test passed", flush=True)
sys.exit(0)
root = Path(os.environ.get("CHECK_REFUTED_ROOT", "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")
# 🔴 A register that loaded NOTHING cannot fail, and would report clean forever --
# the same shape as sylpheed-port's stub that prints "everything is fine" and
# asserts nothing. Refuse rather than pass. Found by this tool's own harness
# self-test, which is the only reason it was visible.
if not claims:
print("🔴 NO CLAIMS PARSED from REFUTED.md — this run asserts NOTHING. "
"Exiting 2 (harness broken), not 0.")
sys.exit(2)
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")
# 🔴 EXIT CODE, added 2026-08-31. This printed its findings and returned 0 no
# matter what -- a planted unmarked revival was reported and the run still
# succeeded, so any pipeline using it asserted NOTHING. sylpheed-port shipped the
# same shape (`return 0` unconditional) one day after writing that defect up in
# someone else's work; mine had been live since the tool was written.
# Unmarked assertions FAIL. Suppressed mentions do not -- they are unverified,
# not wrong, and failing on them would make the clean state unreachable.
if hits:
sys.exit(1)