Files
Sylpheed/tools/port/audit-kinds
Sylpheed port agent d1a1633619 port: assert the scan boundary I had hand-verified, and give audit-kinds a self-test
check-claims --control plants a revival in docs/port/ and requires exit 1. That
the plant lands INSIDE a scanned directory was a property I checked manually, one
time, and wrote up -- the exact pattern I had criticised in this same tool one
iteration earlier. A fifth case now plants the identical text OUTSIDE the scanned
root and requires 0, so the pair asserts the boundary is real: same text, 1 inside
and 0 outside. Either half alone is consistent with the tool scanning everything,
or nothing. Five cases: clean 0, unmarked 1, marked 0, outside-root 0, empty
register 2.

audit-kinds has always reported what it found and was never asked whether it can
find anything, while its clean runs are cited as evidence that fifteen labels are
grounded. --selftest pushes three synthetic rows through the real classifier and
reads its verdict: citing nothing must read BARE, a real path ok, a missing path
DANGLING. Verified two-directionally -- an extractor stubbed to accept everything
returns exit 2. Asserting in check-all.

All four submenus are now measured to reset -- LOAD GAME, TUTORIAL and OPTIONS
joining EXTRAS -- and the main menu remains the only screen that remembers. Three
of the four are not in this export, so no authored value changes.

NOT promoted to a rule, deliberately. 'Submenus reset' at 4/4 is better evidence
than the 2/2 that made wrap a menu-wide rule, and adopting it would change nothing
today because the only submenu this port ships is already measured. What it would
do is pre-decide the next screen from a generalisation instead of a measurement --
the trap that nearly let a derived rule overwrite EXTRAS' measured opening item.
The guard prints the 4/4 finding beside its per-screen values so the evidence is
visible without being load-bearing.

MISSION-SELECT-versus-top-item stays open: none of the three separates it, each
opens on its own first item, and NEW GAME is untested.

Remaining without a harness self-test: verify-transcode-fidelity. Every asserting
check passes, 13 of them.

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

224 lines
9.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""What does each `kind` label in `authored/` actually REST on?
Every authored entry carries a `kind` -- `measured`, `authored`, `name match,
not measured` -- and a `why`. The label is the load-bearing part: `measured`
means the port is repeating something observed off the running game, and a
reader downstream will treat it as fact.
Nothing has ever checked them. That is the point: **a discipline that has never
visibly failed is the one nothing directs attention at.** The Decoder reached
this from the input side -- Ⓐ and Ⓑ were delivery-confirmed because they had
once broken, so the d-pad never was -- and on the same day a `measured` label of
mine turned out to rest on a single entry that may have been measuring history.
So this checks what is checkable about a label, and is explicit that the rest is
not:
citations resolvable references in the `why` -- a `docs/` path that exists on
some ref, a commit sha that resolves, a capture filename
BARE a label whose `why` cites nothing a reader could go and open
DANGLING a citation that does not resolve anywhere in the repository
🔴 What it CANNOT do is read the cited page and confirm it says what the `why`
claims. A label with three resolvable citations can still be wrong. This narrows
"which labels rest on nothing" from unknown to a list; it does not audit meaning.
"""
import json, glob, os, re, subprocess, sys
REFS = None
def known_paths():
"""Every path in the repo, across ALL refs -- docs/re/ lives on a branch.
Checked against the working tree as well: a file added this iteration is not
in any ref yet, and reporting a citation to it as unresolvable would make the
audit fail every time it is itself referenced.
"""
global REFS
if REFS is None:
out = subprocess.run(["git", "rev-list", "--all", "--objects"],
capture_output=True, text=True).stdout
REFS = {l.split(" ", 1)[1] for l in out.splitlines() if " " in l}
return REFS
HANDOFF_TEXT = None
def handoff():
"""The live HANDOFF, so a cited Q number is checked against the real table."""
global HANDOFF_TEXT
if HANDOFF_TEXT is None:
sha = subprocess.run(["git", "log", "--all", "--format=%h", "--",
"docs/port/HANDOFF.md"], capture_output=True,
text=True).stdout.split()[0]
HANDOFF_TEXT = subprocess.run(["git", "show", f"{sha}:docs/port/HANDOFF.md"],
capture_output=True, text=True).stdout
return HANDOFF_TEXT
def sha_ok(s):
r = subprocess.run(["git", "cat-file", "-e", s + "^{commit}"], capture_output=True)
return r.returncode == 0
def text_of(why):
if isinstance(why, str):
return why
if isinstance(why, list):
return " ".join(str(x) for x in why)
return ""
def citations(t):
"""References a reader could actually follow."""
out = []
for p in re.findall(r"\b(?:docs|crates|port|tools|authored)/[\w./-]+\w", t):
out.append(("path", p.rstrip(".,")))
for s in re.findall(r"\b([0-9a-f]{7,40})\b", t):
out.append(("sha", s))
for p in re.findall(r"\b([\w-]+\.(?:png|txt|tsv|wav))\b", t):
out.append(("file", p))
# The corpus cites two things that are not paths and are still followable:
# a HANDOFF question number, and a MISSION section. Leaving these out made
# the first run report four labels as resting on nothing when they rest on
# the two documents the mission names -- an audit inventing defects is worse
# than no audit, because its false positives are indistinguishable from its
# true ones until each is opened.
for q in re.findall(r"HANDOFF Q(\d+)", t):
out.append(("handoff", "Q" + q))
for m in re.findall(r"(PORT-MISSION|MISSION)[ ]section[ ](\d+)", t):
out.append(("mission", m[1]))
for r in re.findall(r"MODDING rule (\d+)", t):
out.append(("modding", r))
return out
def walk(o, f, path, out):
if isinstance(o, dict):
for k, v in o.items():
if (k == "kind" or k.endswith("_kind")) and isinstance(v, str):
stem = "" if k == "kind" else k[: -len("_kind")]
own = o.get((stem + "_why") if stem else "why")
# 🔴 An earlier version fell back to the parent's `why` when a
# label had none of its own, and reported the result as `ok`.
# That credits a label with evidence for a DIFFERENT claim:
# every `goto_name_kind` scored on a sibling `why` about the
# DESTINATION, while the label is about where the NAME came
# from. Borrowed evidence is now its own outcome, because a
# label resting on a neighbour's argument is exactly the case
# this audit exists to surface.
out.append((f, path + "/" + k, v, text_of(own),
own is None and bool(text_of(o.get("why")))))
walk(v, f, path + "/" + k, out)
elif isinstance(o, list):
for x in o:
walk(x, f, path, out)
def selftest():
"""Does this audit notice a label that rests on nothing?
🔴 THE GAP: `audit-kinds` has always reported what it found and never been
asked whether it can find anything. A walk that matched no labels, a citation
extractor that accepted everything, or a `main` that returned 0 regardless
would all have produced the same clean run -- and clean runs from this tool
are cited in `DECISIONS.md` as evidence that fifteen labels are grounded.
Three synthetic rows are pushed through the REAL classifier, and its verdict
is read rather than reasoned about:
a `why` citing nothing -> must be BARE
a `why` citing a path that exists -> must be ok
a `why` citing a path that does not -> must be DANGLING
Exit codes follow the convention the Decoder and I converged on: 0 all good,
1 a real audit failure, **2 the harness is broken** and no clean run from it
means anything.
"""
paths = known_paths()
cases = [
("bare", "no citation of any kind here, just prose", "BARE"),
("ok", "see tools/port/audit-kinds for the method", "ok"),
("dangling", "see docs/port/NO-SUCH-FILE-XYZ.md", "DANGLING"),
]
bad = 0
for name, why, want in cases:
cites = citations(why)
if not cites:
got = "BARE"
else:
unresolved = [c for t, c in cites
if t == "path" and c not in paths and not os.path.exists(c)]
got = "DANGLING" if unresolved else "ok"
mark = "✅" if got == want else "🔴"
print(f" harness: a why that is {name:<9} -> {got:<8} (want {want:<8}) {mark}")
if got != want:
bad += 1
print()
if bad:
print("🔴 the classifier cannot tell grounded labels from ungrounded ones.")
print(" Exit 2: nothing this tool has reported clean is trustworthy.")
return 2
print("the classifier separates bare, dangling and grounded citations")
return 0
def main():
if "--selftest" in sys.argv:
return selftest()
rows = []
for f in sorted(glob.glob("authored/*.json")):
walk(json.load(open(f)), f, "", rows)
paths = known_paths()
bare = dangling = 0
kinds = {}
print(f" {len(rows)} kind label(s) in authored/\n")
for f, where, kind, why, borrowed in rows:
kinds.setdefault(kind, 0)
kinds[kind] += 1
cites = citations(why)
bad = []
for typ, c in cites:
if typ == "handoff":
if not re.search(rf"\|\s*{c}\s*\|", handoff()):
bad.append(f"HANDOFF {c} (no such row)")
elif typ == "path" and c not in paths and not os.path.exists(c):
bad.append(c)
elif typ == "sha" and not sha_ok(c):
bad.append(c)
mark = "ok "
if not cites and borrowed:
mark, bare = "🔴 BORROW", bare + 1
elif not cites:
mark, bare = "🔴 BARE", bare + 1
elif bad:
mark, dangling = "🔴 DANGL", dangling + 1
print(f" {mark} {kind:<24} {f.split('/')[-1]}{where}")
if not cites and borrowed:
print(" no `why` of its own; a sibling `why` argues a"
" DIFFERENT claim")
elif not cites:
print(f" cites nothing openable -- {len(why)} chars of prose")
elif bad:
print(f" unresolvable: {', '.join(sorted(set(bad))[:4])}")
else:
print(f" {len(cites)} citation(s), all resolve")
print()
# Casing is checked because a consumer comparing == "measured" silently
# misses "MEASURED", and a label that fails to match reads as absent.
variants = [k for k in kinds if k.lower() == "measured"]
if len(variants) > 1:
print(f" ⚠️ {len(variants)} spellings of the same label: {variants}")
print(" A consumer comparing == 'measured' misses the others, and a")
print(" label that fails to match reads as ABSENT, not as wrong.\n")
print(f" {bare} bare or borrowed, {dangling} dangling, {len(rows) - bare - dangling} with resolving citations")
print(" 🔴 A resolving citation is not a verified label. Nothing here reads")
print(" the cited page to confirm it says what the `why` claims.")
return 1 if (bare or dangling) else 0
sys.exit(main())