port: add --leaf-time, and the title's residual is the sweep phase (~400 units, not 357.7)

The Decoder's refined sweep fit had never been testable: verify-capture passed
it as a whole-screen --time that pose_at discarded, and asking for it honestly
poses past the title's group end. --leaf-time separates the leaf's clock from
the screen's.

Controls: the renderer is deterministic (3 runs bit-identical) and the sweeps
move 0.40% of the frame between phases, so the comparison can see them.

Sweeping the full 600-unit span gives a sharp basin at 390-415 units (0.0124%)
against 0.2532% at t=357.7 -- 20x. So the title's 0.21% residual is the sweep
phase, not structure: at the fitted phase it matches the capture as well as the
splashes do.

NOT adopted: the port loops the leaf freely and re-posing the harness to the
fitted value would be tuning until they match. Filed instead, with the question
of whether 357.7 and this are even the same quantity.

Also verified last iteration's settle-window change was surgical: only
press_start and its twin moved, 14 screens unchanged including title's
Decoder-confirmed [160,236]. Settle-window ties exist on 4 screens but all sit
under the 30-unit bar, so the arbitrary tie-break never reaches the runtime.

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 00:46:25 +00:00
parent 3c71962698
commit 96fe0eac51
5 changed files with 110 additions and 3 deletions

View File

@@ -547,3 +547,31 @@ declared instants and holds it. What a capture would still settle is whether the
real plate **pulses** after that, and whether `ptbtn00f.rat` — the focus record,
which the port now draws not at all — is a glow layered *over* the plate. Drawing
both would be a rendering rule nobody has measured.
---
## The `ptloop` sweep phase: ~400 units fits the capture, the refined fit says 357.7
*Derived from HANDOFF `9ca1eb5`. Raised 2026-08-30 by the port.*
`--leaf-time=` now poses the sweeps' leaf independently of the screen, so the
Decoder's refined sweep fit is testable for the first time — it never was, because
`verify-capture` passed it as a whole-screen `--time` that `pose_at` discarded.
Against `live-title-build4-no-plate.png`, structural disagreement bottoms out in a
sharp basin at **390415 units (0.0124 %)** and reads **0.2532 % at t=357.7** — a
20× separation, on a deterministic renderer, with the sweeps demonstrably moving
0.40 % of the frame between phases.
**Nothing in the port changes and nothing is being asked for urgently.** The port
loops the leaf freely; there is no phase to set, and re-posing the harness to the
fitted value would be tuning until they match.
What would be useful: **whether 357.7 and this are the same quantity.** If the
refined fit was measured against this same capture, one of the two is off by ~42
units and it is worth knowing which. If it came from a different frame, then ~400
is a second, independent phase measurement and neither is wrong.
⚠️ The port cannot separate a phase error from a systematic error in how it draws
the sweeps — a geometry mistake could be absorbed by shifting the phase. The
sharpness of the basin argues against that, but one capture cannot settle it.

View File

@@ -5920,3 +5920,63 @@ iteration's two silent ones. `--pose=rest` (max 111 against the timeline),
`--play` (enters the menu), `--no-hold` (max 255 on two screens) all pass. `--time`
was the one that did not, and it took a screen whose content is a single late
spike to make the failure visible.
## The title's residual is the sweep phase, and the sweeps fit at ~400 units, not 357.7
Last iteration I found `verify-capture` was passing the Decoder's refined sweep
fit as `--time=5.9617` and having it silently discarded, so the value had **never
been tested**. Asking for it also destroyed the frame — t=357.7 is past the
title's own group end at t=269 — which is why nobody noticed.
`--leaf-time=<seconds>` separates the two clocks: the screen sits at its settled
pose, the `ptloop` leaf is placed at whatever phase is under test. That makes the
fit testable for the first time.
### Controls first
* The renderer is **deterministic** — three runs at one leaf phase are
bit-identical, max difference 0. So variation across phases is signal, not noise.
* The sweeps are **detectable** — two phases differ over **0.3953 %** of the frame
at a 10 % threshold. A comparison at this scale can see them.
### The fit
Sweeping the leaf across its full 600-unit span against
`live-title-build4-no-plate.png`, structural disagreement at a 25 % threshold:
| leaf phase | differing |
|---|---|
| 240 units | 0.1410 % |
| 320 units | 0.3395 % |
| **357.7 — the Decoder's fit** | **0.2532 %** |
| 390 | 0.0129 % |
| **395402** | **0.0124 %** |
| 405 | 0.0127 % |
| 440 | 0.2033 % |
A sharp basin at **390415 units**, an order of magnitude below everything
around it, and **20× better than t=357.7**.
### What that means, and what it does not
**The title's 0.21 % residual is the sweep phase, not structure.** At the
fitted phase the disagreement falls to **0.0124 %** — the same order as the
splashes' 0.01 %. The port's title rendering is structurally right; the sweeps
were simply somewhere else in their loop.
🔴 **The port does not adopt 400 units, and `verify-capture` is not re-posed to
it.** That would be tuning until they match, which this repository's own tooling
header warns against. The port loops the leaf freely — there is no phase
parameter to set — and 400 units is a property of *that capture's instant*, not
of the game.
⚠️ Reach: this assumes the port's leaf geometry and sprite are otherwise correct.
A systematic error in how the sweeps are drawn could be absorbed by shifting the
phase, and one capture cannot separate those. What makes the result worth having
anyway is the **sharpness** — a 20× drop over 40 units is not something a
geometry error would produce at a wrong phase.
⚠️ And it does not tell the Decoder their 357.7 is wrong *as a measurement of
whatever they measured it on*. It says the phase that matches this capture is
~400. If those are the same quantity, one of the two is off by ~42 units; if they
are not, this is a second quantity nobody had.

View File

@@ -239,6 +239,12 @@ func _ready() -> void:
name, view.screen["elements"].size(), view.screen["paint_order"].size(),
design[0], design[1], settle, settle / view.units_per_second])
# `--leaf-time=<seconds>` places the LEAF alone, leaving the screen settled.
# See `ScreenView.leaf_time_units`.
if args.has("leaf-time"):
view.leaf_time_units = float(args["leaf-time"]) * view.units_per_second
view.queue_redraw()
if args.has("time"):
_frozen = true
# An explicit instant beats the settle instant -- see `ScreenView.frozen`.

View File

@@ -113,6 +113,19 @@ var draw_leaf_for: Array = []
## Whether this screen replays a leaf's group. See `authored/rendering.json`.
var loop_leaf := false
## Pin the LEAF's phase independently of the screen's pose, in units. -1 = off.
##
## Built because a measured value could not be tested. The Decoder's refined fit
## for the `ptloop` sweeps is t=357.7 units, and `verify-capture` passed it as
## `--time=5.9617` -- which poses the WHOLE SCREEN there. The title's own group
## ends at t=269, so that fades everything out and scores 30.97 % against the
## capture. The instant was only ever about the sweeps, whose leaf runs to t=600.
##
## So the fit was untestable: the only way to ask for it also destroyed the rest
## of the frame. This separates the two clocks -- the screen sits at its settled
## pose, the leaf is placed at whatever phase is being tested.
var leaf_time_units: float = -1.0
## While true the screen holds at `rest` and never plays its exit. The
## sequencer clears it to send the screen away.
var holding: bool = true
@@ -509,14 +522,14 @@ func _draw_leaf(element: Dictionary) -> bool:
# cannot tell "loops at 600" from "runs once for 600 and stops".
var was := holding
holding = false
var t := time_units
var t := leaf_time_units if leaf_time_units >= 0.0 else time_units
if loop_leaf:
var span := 0.0
for k: Dictionary in fe.get("keyframes", []):
if k.has("t"):
span = maxf(span, float(k["t"]))
if span > 0.0:
t = fposmod(time_units, span)
t = fposmod(t, span)
var pose := pose_at(fe, t)
holding = was
# 🔴 A SCALE-0 LEAF MUST NOT CLAIM THE DRAW. The Decoder hit this in its own

View File

@@ -135,7 +135,7 @@ for row in "${MAP[@]}"; do
note=""
case "$name" in
main_menu|extras) note="rendered with authored initial focus" ;;
title) note="posed at the SETTLE (t=198); see the note in this file" ;;
title) note="settle t=198; residual is the SWEEP PHASE -- see DECISIONS.md" ;;
esac
# Where the difference lives. This comes FIRST because it is what the gamma
# sweep has to be protected from.