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
224 lines
9.4 KiB
Python
Executable File
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())
|