Files
Sylpheed/tools/port/contract-check
Sylpheed port agent 1ca90bfbd6 port: EXTRAS resets, measured -- and being right by luck is not evidence
Ring at 347.5 on entry (MISSION SELECT), 427.5 after one delivery-confirmed DOWN,
347.5 on re-entry with the frame 0.0% different from first entry, screen
confirmed by eye because an earlier run was fooled about which screen it was on.

Two things settle here. The caveat on extras/initial_focus comes off: MISSION
SELECT is a genuine initial focus, because a screen that RESETS cannot have a
single-entry reading that is measuring history -- that objection was live only
while persistence here was unknown. And focus_persists: false for extras is now
written explicitly with kind: measured. Nothing changes at runtime, since the
port already defaulted to false; the point is that an absent key and a measured
false behave identically and mean opposite things -- 'nobody looked' versus 'the
game was watched doing it' -- and only the second is visible to audit-kinds.

It does not vindicate how it got there and is not recorded as if it did. For one
iteration contract-check ASSERTED extras non-persistence with nothing behind it,
the Decoder flagged it, and the measurement then agreed. Their separation is
sharper than my own account was: declining to generalise the memory was correct,
on the evidence then and on measurement now, since the two screens genuinely
disagree -- but encoding 'not measured here' as a positive assertion of the
negative was a different move that happened to land. Being right by luck does not
retroactively make it evidence. The check is rewritten to rest on the
measurement rather than left in place looking vindicated.

guard_focus_scope no longer polices 'only main_menu': there is no menu-wide rule
to state, since two measured screens disagree. It now states both measured values
and counts the screens that say nothing, printing UNMEASURED, not 'resets'.

Untested and not built on: OPTIONS, LOAD GAME, TUTORIAL. And nobody can separate
'resets to MISSION SELECT' from 'resets to the top item' -- they coincide, since
ptbtn11 is both. The port's value is right under either reading and the reason is
not established, which matters the day a screen is authored whose opening item is
not its first.

16 kind labels audited clean, 14 controls firing, every asserting check passes.
The P5 walk artifact now matches a measurement on both halves rather than one
measurement and one default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-30 22:14:10 +00:00

408 lines
19 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 check_extras_resets(h):
"""EXTRAS resets -- MEASURED 2026-08-30, and it used to be asserted unmeasured.
For one iteration the port asserted this with nothing behind it, which the
Decoder flagged; it then measured it and the assertion was right. That does
not make the assertion evidence, so the check is rewritten to rest on the
measurement rather than being left to look vindicated.
"""
want = False if re.search(r"EXTRAS resets, the main menu persists", h) else None
ex = ((jload("authored/flow.json") or {}).get("screens") or {}).get("extras", {})
got = ex.get("focus_persists")
report("extras resets its cursor", want, f"{got} [{ex.get('focus_persists_kind')}]",
want is not None and got is False and ex.get("focus_persists_kind") == "measured")
def guard_focus_scope(_h):
"""NOT a contract check. A guard over the screens NOBODY HAS LOOKED AT.
Two screens are now measured and disagree -- `main_menu` persists, `extras`
resets -- so there is no menu-wide rule to state. What this guards is the
rest: `OPTIONS`, `LOAD GAME` and `TUTORIAL` are untouched, and their absent
`focus_persists` is the port defaulting, not a finding.
📌 The absent key and a measured `false` behave identically and mean opposite
things. That is why `extras` now spends a key on saying `false` out loud.
"""
global FAIL
scr = ((jload("authored/flow.json") or {}).get("screens") or {})
stated = {n: v.get("focus_persists") for n, v in scr.items()
if isinstance(v, dict) and "focus_persists" in v}
silent = sorted(n for n, v in scr.items()
if isinstance(v, dict) and "focus_persists" not in v)
ok = stated == {"main_menu": True, "extras": False}
if ok:
print(f" {'focus_persists scope':<30} guard {stated} measured;"
f" {len(silent)} screen(s) silent = UNMEASURED, not 'resets'")
else:
FAIL += 1
print(f" {'focus_persists scope':<30} 🔴 GUARD {stated} -- a screen states"
f" this without a measurement behind it")
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_extras_resets, "EXTRAS resets, the main menu persists",
"EXTRAS persists, the main menu persists"),
(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, check_extras_resets):
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())