port: check the contract's numbers instead of reading 4111 lines of it

HANDOFF on main is 926 lines frozen at 0fd8e69; the live one is 4111 at 27938aa,
99 commits and 70 sections this port has never opened. 'Read the other one' is
not good enough -- more arrive daily and nothing says which of them contradict
what the port ships.

tools/port/contract-check pulls each expected value OUT OF THE LIVE HANDOFF TEXT
by pattern, never restating it, and reconciles it against export/ and authored/:
the three fade quads' poses, the fade-out ramps, the plate glow cycle, the menu
BGM loop window, the black hold, the menu bank, and the boot splash dwells. All
seven agree. It reads the newest HANDOFF on any ref and prints how far the
working tree's copy is behind.

Three outcomes, and ANCHOR LOST is the point: a check whose pattern no longer
matches the contract has stopped checking, and passes forever while measuring
nothing. Reported as loudly as a mismatch.

--control perturbs the contract one token per check -- 120 becomes 121, -ss 9.44
becomes 9.45 -- and requires every check to fail. All seven do. Without it these
would be seven passes from an instrument nobody had seen react to anything, the
same unfalsifiable clean run the suppression count fixed in check-claims. Both
steps now assert in check-all.

Refutation attempted on 'your menu fade-in is 5x too slow', which corrected a
0.97 s fade-in to 12 units after their helper was found reading each pose's time
from the next record's word. The port never held that number -- ScreenView
animates pteff00 from its own exported keyframes, so the error could not reach
it, the second consecutive iteration where an instruction was aimed at a state I
was not in. But it makes the export an independent check on the correction, and
it holds exactly: their rebuilt tools and my pinned crate are different readers
of the same bytes, so agreement means both got the record-layout fix -- which is
what their helper had not had. Same for the 190/145 splash retraction, now
re-derived a third time.

Filed for the human: only a human merges a topic branch, so the port cannot land
HANDOFF on main and will not merge another agent's branch into its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-30 21:00:20 +00:00
parent c909d1dc49
commit ad80938efc
4 changed files with 305 additions and 1 deletions

View File

@@ -38,6 +38,12 @@ step() { # name, expectation, command...
echo "asserting checks:"
step format-validator must-pass "$BIN" check
# The contract lives on a branch this checkout does not merge: HANDOFF on `main`
# is frozen at 926 lines while the live one is 4 111. Reading 70 unread sections
# by hand is how two days of deliveries went unread. These are the values that
# have been reduced to a check; the rest are still read by eye, or not at all.
step contract-values must-pass tools/port/contract-check
step contract-control must-pass tools/port/contract-check --control
step modding-rules must-pass tools/port/check-modding
step capture-controls must-pass tools/port/check-capture-controls
step menu-audio must-pass env OUT="$OUT/audio" tools/port/verify-menu-audio

219
tools/port/contract-check Executable file
View File

@@ -0,0 +1,219 @@
#!/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)
# 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"),
]
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:
if old not in h:
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()):
fn(h.replace(old, new, 1))
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 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):
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())