diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 55e548bc..a925793c 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -5980,3 +5980,77 @@ geometry error would produce at a wrong phase. 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. + +## A second capture closes the sweep-geometry question, and the plate matches at 0.00093 % + +Last iteration's leaf-phase fit came with a caveat I could not close: *a +systematic error in how the port draws the sweeps could be absorbed by shifting +the phase, and one capture cannot separate those.* A second capture can, and +`live-title-press-a.png` — the title **with** the plate — was sitting in the +corpus unused. + +### The second capture fits at a different phase, and better + +| capture | pose | differing | +|---|---|---| +| `live-title-build4-no-plate.png` | settled, leaf at ~400 units | **0.0124 %** | +| `live-title-press-a.png` | t=237, everything | **0.00093 %** | + +**Two independent captures, two different sweep phases, both fitting to 0.01 % or +better.** A geometry error in how the sweeps are drawn would leave a floor in +*both*, and at a phase-independent level. Neither has one. ✅ The caveat is closed +and the port's sweep rendering is not systematically wrong. + +The two phases are also consistent with each other rather than merely different: +the plate is opaque only at t=236–238, so a frame containing it is early +(t≈237) and a frame without it is either earlier or later. The no-plate capture +fits at leaf ~400 — 6.7 s in, well past the plate's window — and its filename +says it is build 4 alone. Both readings agree on where each frame sits. + +⚠️ I nearly drew a further inference — that the no-plate capture *dates* the +plate's disappearance and therefore answers the BLOCKED question about whether +the plate stays up. It does not: the filename says `build4-no-plate`, so the +capture was taken **of build 4 alone, deliberately without the overlay**. It +carries no information about how long a plate lasts. That row stays open. + +### Capture 1 is not a whole-screen instant, which corroborates `loop_leaf` + +Sweeping the *whole screen's* time against the no-plate capture, the best is +**0.1483 % at t=230**, degrading sharply past 240 as the group fades out — an +order of magnitude worse than the leaf-only fit's 0.0124 % at phase ~400, which +the main timeline cannot reach without fading everything. + +So that capture is **not** "the screen at instant t". It is the screen **settled** +with the sweeps **still looping** — which is exactly what `authored/rendering.json`'s +`loop_leaf_on_screens: ["title"]` models. That decision was authored from the +leaf's zero slack; this is the first independent evidence for it. + +### 🔴 The 1 % floor was the plate not being drawn at all + +Before any of that, every sweep phase against capture 2 gave a flat ~1.0 %, with +the residual a row of glyph-sized blobs on the plate's own position. + +`--screen=X --overlay=Y` pushed the **raw elapsed clock** into the overlay — 9 +units at the moment `--capture` fires. `press_start` is transparent until t=214. +So the one flag whose entire purpose is *put the plate on the title* drew nothing +and reported `drew 0`, and the frame read as a title with no plate. + +A static overlay now poses at **its own arrival**. The `--boot` path is untouched: +there the shared clock is the finding — the 120 units between build 4's last ramp +and the plate's `a=255` is a fixed interval on one timeline. + +⚠️ My first patch for this was wrong and I nearly committed the comment for it: +I wrote that *"nothing outside a boot sequence drives the overlay's clock"*. It is +driven — from `view.time_units`, every frame. The symptom was identical either +way, and only re-reading the log after the fix failed showed the cause was the +opposite of what I had written down. + +### The new row + +`title_plate` joins `verify-capture` at **0.00%** — two orders below every other +row, which makes it the most sensitive regression detector in the harness. + +⚠️ Its instant is **fitted, not measured**. t=237 is where this capture's content +places it, found by sweeping. Choosing which frame to compare against is what +every row here does, but the 0.00093 % is a floor for *that pose*, not a general +statement of accuracy. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index 4549449d..0a2b555f 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -1002,7 +1002,28 @@ func _overlay_process(delta: float) -> void: # apart by a frame here and there, and the whole content of the finding # is that the 120 units between build 4's last ramp and the plate's # `a=255` is a fixed interval on a shared timeline. - overlay.time_units = view.time_units + # 🔴 A STATIC overlay poses at ITS OWN ARRIVAL, not at the shared clock. + # + # `--screen=X --overlay=Y` has no sequence driving it, so `view` sits at + # its settle instant while this line pushed the *raw elapsed* clock into + # the overlay -- 9 units at the moment `--capture` fires. For + # `press_start` that is alpha 0 (transparent until t=214, opaque only at + # t=236-238), so the one flag whose purpose is "put the plate on the + # title" drew NOTHING and reported `drew 0`. It read as a title with no + # plate, which is what anyone would conclude. + # + # It cost a measurement: against `live-title-press-a.png` every sweep + # phase gave a flat ~1.0 % floor, and the residual was a row of + # glyph-sized blobs on the plate's own position. Posed properly the same + # comparison is 0.00093 %. + # + # In a `--boot` sequence the shared clock is the whole point -- the 120 + # units between build 4's last ramp and the plate's a=255 is a fixed + # interval on ONE timeline -- so that path is untouched. + if _sequence.is_empty(): + overlay.time_units = overlay.settle_time() + else: + overlay.time_units = view.time_units overlay.queue_redraw() if _overlay_quit_at >= 0.0 and _elapsed >= _overlay_quit_at: print("boot ends on %s + %s at %.2f s" diff --git a/tools/port/verify-capture b/tools/port/verify-capture index 73f3b380..b97efba1 100755 --- a/tools/port/verify-capture +++ b/tools/port/verify-capture @@ -75,6 +75,23 @@ MAP=( # against 1.82 % here. Picking that one would be fitting the pose to the # score, which is the thing this harness exists not to do. "title:$CAPS/live-title-build4-no-plate.png:t357" + # A SECOND title comparison, and the most sensitive row this tool has. + # + # `live-title-press-a.png` is the title WITH the plate. Posed at t=237 -- inside + # the plate's own 8-unit opaque window, t=236-238 -- the port matches it at + # **0.00093 %**, two orders below every other row. That makes it the best + # regression detector here: anything structural that moves will show. + # + # ⚠️ The instant is FITTED, not measured: 237 is where this capture's content + # places it, found by sweeping. That is legitimate for choosing which frame to + # compare against -- every row does it -- but it is not a claim about the game, + # and the 0.00093 % is therefore a floor for THIS pose, not a general accuracy. + # + # It also closes the systematic-error question the leaf sweep left open. Two + # independent captures fit at two DIFFERENT phases -- this one at 237, the + # no-plate one at ~400 -- and both to 0.01 % or better. A geometry error in how + # the sweeps are drawn would leave a floor in both. Neither has one. + "title_plate:$CAPS/live-title-press-a.png:plate" "publisher_logo:$CAPS/live-splash-publisher.png:screen" "developer_logos:$CAPS/live-splash-developer.png:screen" ) @@ -94,6 +111,9 @@ for row in "${MAP[@]}"; do godot --path port --resolution 1280x720 -- "--menu=$name" --script=wait \ "--shots=$OUT/$name" >"$OUT/$name.log" 2>&1 || true [ -f "$OUT/${name}_00_start.png" ] && cp "$OUT/${name}_00_start.png" "$OUT/$name.render.png" + elif [ "$pose" = plate ]; then + godot --path port --resolution 1280x720 -- "--screen=title" --overlay=press_start \ + --time=3.95 "--capture=$OUT/$name.render.png" >"$OUT/$name.log" 2>&1 || true elif [ "$pose" = t357 ]; then # 🔴 NO `--time` HERE EITHER, and the row's note used to claim otherwise. #