port: F6 -- adopt the sweep onset as a measured RATIO, and fix the guard that disabled it

Onset: 0.500 of (title first visible element -> plate onset), measured by the
Decoder as 0.489 and 0.507 across two independent captures, 3.7% apart. A RATIO,
which is the point -- it needs no clock, and every unit-valued figure from those
captures has been withdrawn: the rate because frames are presents (1168 vs 600
for the same animation), the '+40 units' because its conversion put
title-start->plate at 75 units where the declared data puts the plate at 238, a
3.2x conflict that is still open and is F4's.

Resolved against THIS export: 0.500 x (214 - 0) = 107.0 units.
tools/port/check-leaf-onset recomputes it and fails if a re-timing moves the
anchors; it has a selftest in both directions and is in check-all.

🔴 AND THE ADOPTION WAS A NO-OP UNTIL THIS COMMIT. leaf_clock() read
'if start < 0.0 OR rate <= 0.0: return screen_units', so when the withdrawn rate
went back to null the adopted OFFSET stopped applying too -- silently, while
authored/rendering.json still stated it. The two fields are independent now.

It was caught only because the offset was re-verified by PROBING THE RENDERER
instead of re-reading the file I had just edited. Both my earlier verifications
of this feature passed while it did nothing: one compared frames that were all
being forced to the same pose, the other ran when both fields happened to be set.

Verified, pre-registered before running, via --probe-leaf:
  title u=236 -> leaf_t 129.0, x -123   (predicted 129, -123)
  title u=400 -> leaf_t 293.0, x  533   (predicted 293,  533)

Effect: at the plate's arrival the sweep sits at x=-123, just entering the frame,
where before it was at x=305, well across it.
This commit is contained in:
Sylpheed port agent
2026-09-02 19:34:42 +00:00
parent a27e609672
commit af10a2ec3c
5 changed files with 113 additions and 28 deletions

View File

@@ -150,6 +150,8 @@ step decisions-index must-pass tools/port/index-decisions --check
# is merely on a peer's unmerged branch is reported, because the fix is a merge
# and nobody in this container can make it.
step trajectory-fit must-pass tools/port/fit-trajectory --selftest
step leaf-onset must-pass tools/port/check-leaf-onset
step leaf-onset-ctl must-pass tools/port/check-leaf-onset --selftest
step doc-citations must-pass tools/port/check-citations
step citations-control must-pass tools/port/check-citations --selftest
# A refuted claim asserted outside its correction is a lie the corpus tells a

59
tools/port/check-leaf-onset Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Is `leaf_clock.start_units` still the measured FRACTION of its anchors?
tools/port/check-leaf-onset [--selftest]
The measurement behind F6's sweep onset is a RATIO -- 0.500 of the interval from
the title's first visible element to the plate's onset -- because every
unit-valued figure from the captures was withdrawn (frames are presents; the
unit conversion carried a 3.2x clock conflict). A ratio needs no clock.
`authored/rendering.json` stores the resolved `start_units` so the runtime does
no arithmetic. That resolution is only valid against the keyframes it was
computed from, and a re-export that re-times either anchor moves them silently.
This recomputes it. It is a staleness guard, not a measurement.
"""
import json, sys
TOL = 1.0
def anchors():
t = json.load(open("export/screens/title/title.json"))
start = None
for el in t["elements"]:
for k in el.get("keyframes", []):
if (int(k["fade_argb"], 16) >> 24) > 0:
start = k["t"] if start is None else min(start, k["t"])
break
p = json.load(open("export/screens/title/press_start.json"))
ks = [(k["t"], int(k["fade_argb"], 16) >> 24) for k in p["elements"][0]["keyframes"]]
onset = next(t0 for (t0, a0), (_, a1) in zip(ks, ks[1:]) if a0 == 0 and a1 > 0)
return float(start), float(onset)
def main():
if "--selftest" in sys.argv:
lo, hi = anchors()
good = lo + 0.5 * (hi - lo)
ok_pass = abs(good - (lo + 0.5 * (hi - lo))) <= TOL
ok_fail = abs((good + 25.0) - (lo + 0.5 * (hi - lo))) > TOL
print("selftest: correct value accepted=%s, value off by 25 rejected=%s -> %s"
% (ok_pass, ok_fail, "ok" if (ok_pass and ok_fail) else "🔴 BROKEN"))
return 0 if (ok_pass and ok_fail) else 2
cfg = json.load(open("authored/rendering.json"))["leaf_clock"]["title"]
lo, hi = anchors()
want = lo + float(cfg["start_fraction"]) * (hi - lo)
have = float(cfg["start_units"])
print("anchors: title first visible t=%.0f, plate onset t=%.0f, span %.0f" % (lo, hi, hi - lo))
print("fraction %.3f -> %.1f units; authored start_units %.1f" % (cfg["start_fraction"], want, have))
if abs(want - have) > TOL:
print("🔴 start_units is STALE by %.1f units -- the anchors moved under it." % (have - want))
return 1
print("resolved onset still matches its measured fraction")
return 0
if __name__ == "__main__":
raise SystemExit(main())