The Decoder corrected their own focus delivery: the persistence run's item names were two positions out, from a reader using design-space rows against captures carrying Xenia's chrome and a 1.060 scale. Two things follow. initial_focus_kind moves from authored to measured. NEW GAME on a fresh boot, 2/2 fresh boots, both the first menu entry. The value did not change; its standing did, and the upgrade is not because the measurement agrees with me -- they had said my agreeing with their records was no evidence, which was correct, and this is a direct reading independent of the reasoning that chose NEW GAME here. "First entry" is load-bearing: since the menu remembers its cursor, a reading taken later measures history, which is the objection that voided the earlier TUTORIAL-versus-NEW-GAME disagreement. The superseded reasoning is kept under (was) lines -- the field existing and being labelled honestly is what made arriving at a measurement a label change rather than an archaeology problem, the third time that has paid off after loop_start_s and the +0x08 read. My check_focus_persists anchor survived a correction it should not have been able to detect. It anchors on the heading, the conclusion, not on the item names. That is lucky rather than designed: the conclusion is geometry-free -- ring at y 384.0 before the round trip and 385.5 after, an equality immune to a constant offset -- while the names were not. The check would not have caught the label error, and nothing in it distinguishes anchored-on-a-robust-claim from anchored-above-the- part-that-was-wrong. Their generalisation: a control that only checks differences is blind to the origin. check_splash_dwell is that shape -- it compares the widest gap between keyframe times, and a reader with every time shifted by a constant passes. Added check_splash_times, asserting the absolute list the contract prints. Origin and difference now fail independently. Writing that control reproduced the error one level down: its perturbation literal was written from memory of the prose, with a space where the document has a newline, so it reported its own anchor gone. A control written from a memory of the source rather than from the source is the class of error these checks exist to catch. Thirteen controls, all firing. Q2 closed: fixed same day, and the row was worse than I reported -- the splashes were also mis-paired as 10/11, one half each of two different pairs. EXTRAS remains unmeasured; the run meant to settle it navigated to OPTIONS believing it was EXTRAS. Every asserting check passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
385 lines
18 KiB
Python
Executable File
385 lines
18 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""Reconcile the numbers the CONTRACT states against the numbers the PORT ships.
|
||
|
||
`docs/port/HANDOFF.md` is the contract, and this port reads it from `main` --
|
||
where it is frozen at 926 lines while the live document, on the Decoder's branch,
|
||
is 4 111. Two days of deliveries addressed to the port landed on a page the port
|
||
does not open. Reading 70 unread sections by hand is how that gets missed again.
|
||
|
||
So the values are checked instead of read. Each check names a quantity, pulls it
|
||
OUT OF THE LIVE HANDOFF TEXT by pattern -- never restating it here, or this file
|
||
would be a third copy to go stale -- and compares it against the port's own
|
||
`export/` tree or `authored/` mapping.
|
||
|
||
Three outcomes, and the third is the point:
|
||
|
||
ok the contract and the port agree
|
||
MISMATCH they disagree; one of us is wrong and this says which values
|
||
ANCHOR the pattern no longer matches the contract -- the check has STOPPED
|
||
CHECKING. Reported as loudly as a mismatch, because a check whose
|
||
anchor has drifted passes forever while measuring nothing.
|
||
|
||
Reads the newest HANDOFF on ANY ref, not the working tree's, and says which.
|
||
"""
|
||
import json, re, subprocess, sys, os
|
||
|
||
FAIL = 0
|
||
|
||
|
||
def git(*a):
|
||
return subprocess.run(["git", *a], capture_output=True, text=True).stdout
|
||
|
||
|
||
def contract():
|
||
"""The newest HANDOFF anywhere, and how far the working tree's copy is behind."""
|
||
sha = git("log", "--all", "--format=%h", "--", "docs/port/HANDOFF.md").split()[0]
|
||
mine = git("log", "-1", "--format=%h", "--", "docs/port/HANDOFF.md").strip()
|
||
text = git("show", f"{sha}:docs/port/HANDOFF.md")
|
||
behind = len(git("log", "--all", "--not", "HEAD", "--format=%h",
|
||
"--", "docs/port/HANDOFF.md").split())
|
||
print(f" contract: {sha} ({len(text.splitlines())} lines)")
|
||
print(f" my copy : {mine} ({len(git('show', f'{mine}:docs/port/HANDOFF.md').splitlines())} lines)"
|
||
f"{'' if behind == 0 else f' <- {behind} HANDOFF commit(s) unread'}")
|
||
return text
|
||
|
||
|
||
def report(name, want, got, ok):
|
||
global FAIL
|
||
if want is None:
|
||
FAIL += 1
|
||
print(f" {name:<30} 🔴 ANCHOR LOST -- the contract no longer states this")
|
||
elif ok:
|
||
print(f" {name:<30} ok contract {want} port {got}")
|
||
else:
|
||
FAIL += 1
|
||
print(f" {name:<30} 🔴 MISMATCH contract {want} port {got}")
|
||
|
||
|
||
def jload(p):
|
||
return json.load(open(p)) if os.path.exists(p) else None
|
||
|
||
|
||
def el(screen, prefix):
|
||
d = jload(f"export/screens/title/{screen}.json")
|
||
if not d:
|
||
return None
|
||
return next((e for e in d["elements"] if e["id"].startswith(prefix)), None)
|
||
|
||
|
||
# --- the checks ------------------------------------------------------------
|
||
|
||
def check_fade_quads(h):
|
||
"""The fade-in that a broken helper reported 5x too slow for years.
|
||
|
||
The contract prints the three builds' `pteff00` poses in one fence. The port
|
||
animates that quad from its OWN export, so agreement here is two readers of
|
||
the same bytes -- theirs rebuilt after the record-layout fix, mine the pinned
|
||
crate -- and a disagreement would mean one reader never got the fix.
|
||
"""
|
||
for screen, build in (("title", 4), ("main_menu", 5), ("extras", 6)):
|
||
m = re.search(rf"build {build} \([^)]*\)\s+pteff00\.prm\s+(.+)", h)
|
||
want = None
|
||
if m:
|
||
want = [(int(t), int(a)) for t, a in re.findall(r"t=\s*(\d+)\s*α=(\d+)", m.group(1))]
|
||
e = el(screen, "pteff00")
|
||
got = [(k["t"], int(k["fade_argb"][2:4], 16)) for k in e["keyframes"]] if e else None
|
||
report(f"fade quad, {screen}", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_plate_period(h):
|
||
"""`+0x08` is the loop length: 120, and the port must not run the glow at 105."""
|
||
m = re.search(r"the plate's pulse period is (\d+), not (\d+)", h)
|
||
want = int(m.group(1)) if m else None
|
||
e = el("press_start", "ptbtn00")
|
||
got = (e.get("focus") or {}).get("loop_length_units") if e else None
|
||
report("plate glow cycle", want, got, want is not None and want == got)
|
||
a = jload("authored/timing.json") or {}
|
||
auth = a.get("looping_focus_records", {}).get("press_start/ptbtn00", {}).get("period_units")
|
||
report(" ... authored 2nd witness", want, auth, want is not None and want == auth)
|
||
|
||
|
||
def check_bgm_window(h):
|
||
"""The menu loop, as an ffmpeg window the contract states literally."""
|
||
m = re.search(r"the window is \*\*`-ss ([\d.]+) -t ([\d.]+)`\*\*", h)
|
||
want = (float(m.group(1)), float(m.group(2))) if m else None
|
||
a = ((jload("authored/audio.json") or {}).get("bgm") or {}).get("main_menu", {})
|
||
got = (a.get("loop_start_s"), a.get("loop_end_s"))
|
||
report("menu BGM loop window", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_black_hold(h):
|
||
"""The gap between screens is not a load: the contract says keep it at 0."""
|
||
m = re.search(r"Keep `black_hold_units` at (\d+)", h)
|
||
want = int(m.group(1)) if m else None
|
||
got = (jload("authored/timing.json") or {}).get("black_hold_units")
|
||
report("black hold between screens", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_menu_bank(h):
|
||
"""Which bank the menu plays -- the row the port once got wrong by authoring."""
|
||
m = re.search(r"`(BGM_\d+)` confirmed from the RUNTIME", h)
|
||
want = m.group(1) if m else None
|
||
got = (((jload("authored/audio.json") or {}).get("bgm") or {})
|
||
.get("main_menu", {}).get("bank", ""))
|
||
report("menu BGM bank", want, got, want is not None and got.startswith(want))
|
||
|
||
|
||
def check_fade_out(h):
|
||
"""The fade-OUT lengths, derived from the same poses the fade-in check reads.
|
||
|
||
Stated as prose rather than in the fence, so this parses the sentence. Split
|
||
from the fade-in deliberately: they came from the same broken helper, and a
|
||
single check covering both would let one wrong half hide behind a right one.
|
||
"""
|
||
m = re.search(r"Fade-out = (\d+) units, (\d+) units, and \*\*(\d+)\*\* on the title", h)
|
||
want = [int(m.group(i)) for i in (1, 2, 3)] if m else None
|
||
got = []
|
||
for screen in ("main_menu", "extras", "title"):
|
||
e = el(screen, "pteff00")
|
||
ks = [k["t"] for k in e["keyframes"]] if e else []
|
||
got.append(ks[-1] - ks[-2] if len(ks) >= 2 else None)
|
||
report("fade-out ramps", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_splash_dwell(h):
|
||
"""The two boot splashes' dwell -- the retraction the port's recomputation caused.
|
||
|
||
The contract gives 190 and 145 as the widest gap in each entry's own times.
|
||
The port plays the declared timeline, so the same gap must come out of the
|
||
export. This is the retracted claim re-derived from a third reading.
|
||
"""
|
||
m = re.search(r"the splashes are (\d+) and (\d+)", h)
|
||
want = [int(m.group(1)), int(m.group(2))] if m else None
|
||
got = []
|
||
for screen in ("publisher_logo", "developer_logos"):
|
||
d = jload(f"export/screens/title/{screen}.json")
|
||
ts = sorted({k["t"] for e in d["elements"] for k in e["keyframes"]}) if d else []
|
||
got.append(max((b - a for a, b in zip(ts, ts[1:])), default=None))
|
||
report("boot splash dwells", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_splash_times(h):
|
||
"""The splash's ABSOLUTE keyframe times, not just the gap between two of them.
|
||
|
||
🔴 Added 2026-08-30 because the dwell check above is a DIFFERENCE, and a
|
||
difference is blind to the origin: a reader whose times were all shifted by a
|
||
constant would produce the same 190 and pass. That is not hypothetical -- the
|
||
Decoder's own control asserted "two DOWNs move two items", which a constant
|
||
offset preserves exactly, and it passed for a whole session on a reader that
|
||
was two items wrong. Ground truth caught it; the control could not.
|
||
|
||
The contract prints entry 10's times in full, so the origin is checkable.
|
||
"""
|
||
m = re.search(r"entry 10's times are\s*`\[([0-9, ]+)\]`", h)
|
||
want = [int(x) for x in m.group(1).split(",")] if m else None
|
||
d = jload("export/screens/title/publisher_logo.json")
|
||
got = sorted({k["t"] for e in d["elements"] for k in e["keyframes"]}) if d else None
|
||
report("splash absolute times", want, got, want is not None and want == got)
|
||
|
||
|
||
def check_initial_focus(h):
|
||
"""What the menu opens on FROM A FRESH BOOT -- measured, and it was authored.
|
||
|
||
Anchored on the measurement rather than on the value, so that if the reading
|
||
is corrected again this fails instead of silently agreeing.
|
||
"""
|
||
want = "NEW GAME" if re.search(
|
||
r"\*\*Initial focus on a fresh boot is `NEW GAME`\*\*", h) else None
|
||
scr = ((jload("authored/flow.json") or {}).get("screens") or {}).get("main_menu", {})
|
||
bid = scr.get("initial_focus")
|
||
got = (scr.get("buttons") or {}).get(bid, {}).get("label")
|
||
kind = scr.get("initial_focus_kind")
|
||
report("menu opens on (fresh boot)", want, f"{got} [{kind}]",
|
||
want is not None and got == want and kind == "measured")
|
||
|
||
|
||
|
||
|
||
|
||
def fn_nav_perturbed(fn, old, new):
|
||
"""Run a walk-anchored check against a perturbed copy of the walk.
|
||
|
||
`nav()` reads from git, so the perturbation is injected by swapping the
|
||
function out rather than by editing a file -- nothing on disk is touched.
|
||
"""
|
||
global nav
|
||
real = nav
|
||
nav = lambda: (real()[0].replace(old, new, 1), real()[1])
|
||
try:
|
||
fn(None)
|
||
finally:
|
||
nav = real
|
||
|
||
|
||
def control(h):
|
||
global FAIL
|
||
import io, contextlib
|
||
ok = True
|
||
print(" known negatives -- every check must notice a perturbed contract:\n")
|
||
for fn, old, new in CONTROLS + [(f, o, n) for f, o, n in NAV_CONTROLS]:
|
||
src = h if (fn, old, new) in CONTROLS else nav()[0]
|
||
if old not in src:
|
||
print(f" {fn.__name__:<22} 🔴 the control's own anchor is gone")
|
||
ok = False
|
||
continue
|
||
before, FAIL = FAIL, 0
|
||
with contextlib.redirect_stdout(io.StringIO()):
|
||
if src is h:
|
||
fn(h.replace(old, new, 1))
|
||
else:
|
||
fn_nav_perturbed(fn, old, new)
|
||
noticed, FAIL = FAIL > 0, before
|
||
print(f" {fn.__name__:<22} {'✅ fails as it must' if noticed else '🔴 PASSES A WRONG CONTRACT -- it checks nothing'}")
|
||
ok = ok and noticed
|
||
return ok
|
||
|
||
|
||
def nav():
|
||
"""The player's-eye walk, from the newest ref that carries it.
|
||
|
||
A second unreachable document: `docs/game/navigation.md` was filled in from
|
||
the committed oracle frames and, like HANDOFF, is not on `main`. The port's
|
||
`authored/flow.json` is the executable form of that walk, so the two must not
|
||
drift -- and the drift would be invisible, because nothing in the port fails
|
||
when a label is wrong.
|
||
"""
|
||
sha = git("log", "--all", "--format=%h", "--", "docs/game/navigation.md").split()[0]
|
||
return git("show", f"{sha}:docs/game/navigation.md"), sha
|
||
|
||
|
||
def flow_buttons(screen):
|
||
d = jload("authored/flow.json") or {}
|
||
b = ((d.get("screens") or {}).get(screen) or {}).get("buttons") or {}
|
||
return [v.get("label") for _, v in sorted(b.items())]
|
||
|
||
|
||
def check_focus_persists(h):
|
||
"""The menu remembers its cursor -- MEASURED, on the main menu, one screen.
|
||
|
||
🔴 This checked a PAIR until 2026-08-30: on for `main_menu`, off everywhere
|
||
else. The second half asserted that `extras` does NOT persist, and **nothing
|
||
measured that**. What the corpus has is EXTRAS' initial focus from a single
|
||
entry and Ⓑ restoring the PARENT's focus 4/4 — neither says what a submenu's
|
||
own cursor does on re-entry. So one measured behaviour and one absence of a
|
||
measurement were being reported identically, and if the game does persist
|
||
EXTRAS the check would have held the port to the wrong behaviour AND PASSED.
|
||
|
||
The mirror of the trap it was written to avoid: refusing to let a derived
|
||
rule overwrite a measured value, then letting "not measured here" become a
|
||
positive assertion of the negative. Now only the measured half is asserted
|
||
against the contract; the scope is a guard, below.
|
||
"""
|
||
want = bool(re.search(r"the main menu remembers its cursor; re-entry is not a reset", h))
|
||
got = (((jload("authored/flow.json") or {}).get("screens") or {})
|
||
.get("main_menu", {}).get("focus_persists"))
|
||
report("menu remembers its cursor", want or None, got, want and got is True)
|
||
|
||
|
||
def guard_focus_scope(_h):
|
||
"""NOT a contract check. A regression guard on an AUTHORED DEFAULT.
|
||
|
||
No screen but `main_menu` persists its cursor in this port, and that is the
|
||
port's choice, not a finding: it preserves EXTRAS' measured opening item and
|
||
invents the least. Guarded so that widening it is a deliberate edit with a
|
||
`why`, and labelled so a passing run cannot be read as the game being known
|
||
to reset.
|
||
"""
|
||
global FAIL
|
||
on = sorted(n for n, v in ((jload("authored/flow.json") or {}).get("screens") or {}).items()
|
||
if isinstance(v, dict) and v.get("focus_persists"))
|
||
if on == ["main_menu"]:
|
||
print(f" {'focus_persists scope':<30} guard only main_menu"
|
||
f" -- AUTHORED DEFAULT, unmeasured elsewhere")
|
||
else:
|
||
FAIL += 1
|
||
print(f" {'focus_persists scope':<30} 🔴 GUARD {on} -- widened past the"
|
||
f" one screen measured; needs a why and a measurement")
|
||
|
||
|
||
def check_menu_labels(_h):
|
||
"""The five main-menu labels, in order, off the walk's own table."""
|
||
n, sha = nav()
|
||
rows = re.findall(r"^\| [1-5] \| \*\*([A-Z ]+)\*\* \|", n, re.M)
|
||
want = rows or None
|
||
report(f"main menu labels ({sha})", want, flow_buttons("main_menu"),
|
||
want is not None and want == flow_buttons("main_menu"))
|
||
|
||
|
||
def check_extras_labels(_h):
|
||
"""EXTRAS' three items, written as prose rather than a table."""
|
||
n, _ = nav()
|
||
m = re.search(r"Three items: `([A-Z ]+)` · `([A-Z ]+)` · `([A-Z ]+)`", n)
|
||
want = [m.group(i) for i in (1, 2, 3)] if m else None
|
||
report("extras labels", want, flow_buttons("extras"),
|
||
want is not None and want == flow_buttons("extras"))
|
||
|
||
|
||
def check_wrap(_h):
|
||
"""The cursor wraps, and it is a MENU rule -- the walk says so in two places."""
|
||
n, _ = nav()
|
||
want = True if re.search(r"one item, and it \*\*wraps\*\* at both ends", n) else None
|
||
got = ((jload("authored/flow.json") or {}).get("navigation") or {}).get("wrap")
|
||
report("cursor wraps", want, got, want is not None and want == got)
|
||
|
||
|
||
# Each check paired with a one-token edit to the CONTRACT that must break it.
|
||
# A check that has never been observed to fail is not evidence -- it may be
|
||
# reading nothing, comparing a value to itself, or anchored on a pattern that
|
||
# matches anything. `--control` perturbs the contract and requires every check to
|
||
# notice. This is the same discipline the checks themselves enforce: an
|
||
# instrument goes through a known negative before its clean run is believed.
|
||
CONTROLS = [
|
||
(check_fade_quads, "pteff00.prm t= 0 α=255 t= 12", "pteff00.prm t= 0 α=255 t= 13"),
|
||
(check_fade_out, "Fade-out = 10 units, 10 units", "Fade-out = 11 units, 10 units"),
|
||
(check_plate_period, "pulse period is 120, not 105", "pulse period is 121, not 105"),
|
||
(check_bgm_window, "`-ss 9.44 -t 61.87`", "`-ss 9.45 -t 61.87`"),
|
||
(check_black_hold, "Keep `black_hold_units` at 0", "Keep `black_hold_units` at 3"),
|
||
(check_menu_bank, "`BGM_103` confirmed from the RUNTIME", "`BGM_999` confirmed from the RUNTIME"),
|
||
(check_splash_dwell, "the splashes are 190 and 145", "the splashes are 191 and 145"),
|
||
(check_focus_persists, "the main menu remembers its cursor; re-entry is not a reset",
|
||
"the main menu forgets its cursor; re-entry is a reset"),
|
||
# The list sits on the line AFTER "times are", so the perturbation has to
|
||
# carry the newline the check's `\s*` spans. A control whose own anchor is
|
||
# written from memory of the prose rather than from the prose is the same
|
||
# class of error the checks exist to catch.
|
||
(check_splash_times, "times are\n`[0,15,30,45,235,239,251,255]`",
|
||
"times are\n`[1,16,31,46,236,240,252,256]`"),
|
||
(check_initial_focus, "**Initial focus on a fresh boot is `NEW GAME`**",
|
||
"**Initial focus on a fresh boot is `TUTORIAL`**"),
|
||
]
|
||
|
||
# The walk's controls perturb `navigation.md` instead of HANDOFF, so they are
|
||
# applied to a different document and kept separate rather than folded in.
|
||
NAV_CONTROLS = [
|
||
(check_menu_labels, "| 1 | **NEW GAME**", "| 1 | **NEW GAMES**"),
|
||
(check_extras_labels, "`MISSION SELECT` · `MOVIE THEATER`", "`MISSION SELECTS` · `MOVIE THEATER`"),
|
||
(check_wrap, "one item, and it **wraps** at both ends", "one item, and it stops at both ends"),
|
||
]
|
||
|
||
|
||
def main():
|
||
if not os.path.exists("export/manifest.json"):
|
||
sys.exit("no export/ -- run the exporter first; this check reads what is shipped")
|
||
h = contract()
|
||
print()
|
||
if "--control" in sys.argv:
|
||
return 0 if control(h) else 1
|
||
for fn in (check_fade_quads, check_fade_out, check_plate_period,
|
||
check_bgm_window, check_black_hold, check_menu_bank,
|
||
check_splash_dwell, check_menu_labels, check_extras_labels,
|
||
check_wrap, check_focus_persists, guard_focus_scope,
|
||
check_splash_times, check_initial_focus):
|
||
fn(h)
|
||
print()
|
||
print(" A passing run means the port agrees with the contract ON THESE VALUES.")
|
||
print(" It is not a statement about the 70 sections nobody has reduced to a")
|
||
print(" check -- those are still read by hand, or not read at all.")
|
||
if FAIL:
|
||
print(f"\n🔴 {FAIL} disagreement(s) or lost anchor(s) with the contract")
|
||
else:
|
||
print("\nthe port agrees with the contract on every value checked")
|
||
return 1 if FAIL else 0
|
||
|
||
|
||
sys.exit(main())
|