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:
@@ -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 **390–415 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.
|
||||
|
||||
@@ -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 % |
|
||||
| **395–402** | **0.0124 %** |
|
||||
| 405 | 0.0127 % |
|
||||
| 440 | 0.2033 % |
|
||||
|
||||
A sharp basin at **390–415 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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user