Files
Sylpheed/docs/port/splash-animation-fixed.md
MechaCat02 a23c321831 port: land the play-tested work, and only that
Takes the port branch up to 77320d5e -- the state the human play-tested on
2026-09-02 -- for SOURCE paths only. Not a branch merge: `auto/port-p6-audio`
is 366 commits and 938 files, and most of that must not land.

WHAT COMES IN (76 files, all human-confirmed working):
  * the logo splash animation. 08ed3dd1 found it: `pose_at` ASSIGNED the settle
    instant instead of clamping to it, so the splash never animated at all --
    and the same bug manufactured a passing harness result, because the harness
    photographed t past the settle. Confirmed by play-test: "cannot notice any
    obvious difference from the actual game."
  * gamepad input -- (A)/(B) bound additively (`ui_accept` ships with NO joypad
    binding), stick latched with hysteresis at the game's own 61% digitise
    threshold. This is what made (A), video-skip and Extras work at all.
  * menu navigation and flow, menu audio, the exporter, the authored
    declarations, and 23 verification tools under tools/port/.

WHAT IS DELIBERATELY LEFT ON THE BRANCH:
  * everything after c0ae460a -- the F5/F6 title-timing investigation, whose own
    tip commit calls itself a "hand-off for one-minute human checks". Unchecked
    by definition; it goes through the new review gate like anything else.
  * the OPTIONS menu work of 2026-09-03. Real, probably good, NOT play-tested.
  * the F1 repeat mechanism, which its own commit calls "deliberately inert".

WHAT MUST NOT LAND, AND WHY THE .gitignore CHANGED:
  545 MB of extracted game content was committed on that branch -- 850 sprite,
  audio and transcoded video files under `export-probe/` and `export-probe2/`,
  plus 246 MB of loose .wav and .tsv at the repo root. This repository's own
  rule, in this file, is "never game content".

  The rule was not missing. It was written, and it was tightened on that very
  branch, with a careful comment explaining why BOTH `export/` and `data/base/`
  had to be listed -- while the exporter was writing to a third name that
  nobody had thought to list. Enumerating names is the thing that failed. So
  the ignore rules now describe the SHAPE: any top-level `export*/`, game media
  by extension, and loose capture output at the root. Verified both ways -- it
  catches all four offenders and ignores nothing currently tracked.

Verified: `cargo check --workspace` clean; all nine GDScript files parse in
project context, with a positive control (an injected syntax error is detected,
3 lines) so the clean result means something. `tools/port/check-all` was NOT
run -- it needs the container, the export tree and a display.
2026-09-04 16:17:14 +02:00

6.5 KiB
Raw Blame History

The splash: frozen, then fixed, and now checked for SHAPE as well as motion

Status: animates, and the ramp matches the declared curve. Written 2026-09-02 by the Port; HANDOFF on this branch answers 9ca1eb5.

The 2026-09-02 play-test found the splash frozen. The cause and the fix are in the commit history; this page is the part that comes after — is the animation now the right animation? motion-census says explicitly that it cannot answer that: "a wrong ramp that moves every frame passes here."

Pre-registered (R2)

palogo_sqex_eff declares 0:a=0 → 15:a=255 → 30:a=212 → 45:a=0 — three segments, three gradients:

segment declared
0 → 15 +17.0 per unit
15 → 30 2.87 per unit
30 → 45 14.13 per unit

So a film should show three straight runs with breakpoints at 15 and 30, the middle slope about 1/5 the magnitude of the last, and the rise about 1.2× the last.

Measured, from a film of a real boot

Region: 686x11+299+319 — the companion's top strip, which no other element overlaps, so the number is that element's own alpha and not a composite.

view units strip mean slope/unit
5.58 0.02809
10.00 0.05086 +0.00515
13.04 0.06611 +0.00503
16.12 0.07530 +0.00298 ← crossing the breakpoint
19.21 0.07260 0.00087
25.38 0.06695 0.00092
28.39 0.06482 0.00071
31.39 0.05719 0.00254 ← crossing the breakpoint
34.39 0.04471 0.00416
40.39 0.01910 0.00428
43.39 0.00655 0.00418
46.39 0.00000 gone

Breakpoints land where declared: the rise stops between 13.0 and 16.1, the gentle fall steepens between 28.4 and 31.4.

ratio declared measured
middle : last 0.203 0.213
rise : last 1.20 1.20

Within 5 % and exact respectively. The port interpolates piecewise-linearly across the declared segments — which is what the Decoder independently measured the game doing (28 distinct alphas over 28 consecutive presents, modal steps 3 and 14 against predicted 2.87 and 14.13).

⚠️ Ratios, not absolutes, and deliberately. The strip mean is an alpha scaled by whatever the sprite's own pixels are; its absolute value carries the texture. A ratio between segments divides that out, which is why the shape is checkable from a composite at all. Anyone quoting 0.00515 as an alpha is misreading it.

What this still does not establish

  • That it looks right. Three instruments have now agreed with a picture a human called wrong, and the fourth agreeing does not change the standing of the fifth. A play-test is the check.
  • Anything about the developer splash's three logo/companion pairs. One element, one screen. Closed below, and the developer splash turned out to be the far better test.
  • The absolute alpha. The shape matches; whether the port's alpha equals the game's at a given unit is verify-capture's question, and it answers 0.01 % at the settled pose only.

The developer splash: three elements, TWO declared shapes, on one screen

The publisher check above had a structural weakness I named at the time: one element, one screen, one shape. If the port applied some single ramp to everything, that check would pass.

The developer splash cannot be fooled that way, because it declares two different shapes at once:

element declared alpha middle segment
palogo_gamearts_eff 0:0 15:255 30:255 45:0 flat
palogo_seta_eff 0:0 15:255 30:255 45:0 flat
palogo_anima_eff 0:0 15:255 30:212 45:0 decays 17 %

Same screen, same frames, same clock, same code path. The two flat elements are the control for the one that is not — no second run, no second renderer, and nothing for a phase or rate error to hide behind, because any such error hits all three identically.

Pre-registered, before the film was read

In units 15→30, gamearts and seta hold flat while anima falls gently. All three fall steeply 30→45.

Measured, off a real filmed boot

Companion-only strips (each companion's rectangle minus the logo sitting inside it), mean luma, --film-interval=0.03:

segment     units 16-29  units 31-44
gamearts       +0.00000     -0.00101
seta           +0.00000     -0.00151
anima          -0.00001     -0.00003

The two flat ones are flat to five decimals. The decaying one decays. And all three fall over 3144, so the flatness is not a dead element.

The quantitative version, and the falsification arm

Each trace normalised by a single solved gain — one scalar per element, not a per-point fit — against the declared curve, over the whole t=0..45 ramp:

element fitted against max err rms err
gamearts its own (flat) 0.49 % 0.23 %
seta its own (flat) 0.50 % 0.27 %
anima its own (decay) 2.18 % 1.19 %
gamearts anima's decay 8.32 % 4.80 %
seta anima's decay 8.08 % 4.86 %
anima flat 8.82 % 4.94 %

Both directions. Every element fits its own declared shape 417× better than it fits the other one available on the same screen. A port that drew one ramp for all three would sit at ~8 % on at least one row; none does.

anima's 2.18 % is the loosest row and the reason is its signal: its companion-only strip means ~0.0005 against gamearts's ~0.015, thirty times dimmer, so its noise floor is thirty times higher in these units. It still separates from the wrong curve by 4×.

What this adds over the publisher check

The publisher check established that the ramp has the declared breakpoints and slope ratios. This establishes that the ramp is per-element — that the port reads each element's own keyframes rather than applying a screen-wide curve. That is a different failure mode, and it is the one that would have survived the publisher check unnoticed.

What it still does not establish

  • That it looks right. This is instrument five. The standing of a human's eyes is unchanged by it.
  • The absolute alpha, for the same reason as above — a solved gain is deliberately scale-free, so this is a shape result and says nothing about whether the port's alpha equals the game's at a given unit.
  • Anything about the logo elements. Both checks measure the pre-blurred companions, because those are the strips that can be isolated from the sharp logo underneath them.