docs: the timeline lands on rest -- except on six elements, where the game

agrees with the timeline

P2's gate is the buttons sliding in, and `tools/screen-strip` renders the strip
that shows it. But the useful result came out of checking where the animation
settles.

On 8 of 12 screens the settled timeline is BYTE-IDENTICAL to the declared `rest`
pose -- the port walks the keyframes with an authored time unit and arrives, to
the pixel, where the pinned decoders independently say the screen rests. On
`main_menu` the two differ in exactly one region, 400x470 at (440,108): the
bounding box of `ptframe1` and `ptframe2` and nothing else.

`rest` puts both at their first keyframe, off-position and transparent. The
capture of the running game shows them -- the bright circuit bracket around the
menu. Cropping the same region from the capture and from both renders puts the
ring and its elbow trace in the timeline render pixel-aligned with the game's,
and absent from the rest render. Geometry, so it does not depend on the
capture's gamma or on its having been taken with NEW GAME focused.

`ui_layout::rest_plateau` excludes a trailing run of identical keyframes because
it is normally the exit. On an element with NO exit animation the trailing run IS
the hold. The condition that identifies these exactly, with no false positives
here, is "the final untimed keyframe has the same pose as the last timed one" --
six elements, and `rest()` misses all six. Filed in BLOCKED.md for the RE agent:
the decoders are pinned and are not this port's to fix, and `sylpheed-cli screen
render` is missing the bracket too.

Worth saying plainly what this does to P1: the port and the reference agreed on
`main_menu` to 3/255 and BOTH were missing two elements the game draws. Two
renderers reading one field through one decoder agreeing is not evidence the
field is right. BLOCKED.md had already said that about the pivot; here it bit.

The title is NOT settled and P2 does not claim it. `rest` and the timeline
disagree there by 142-247/255, the only live title capture composites the PRESS A
plate over build 4 so it cannot be diffed against the title alone, and both of
the port's modes draw a cyan glow slab the game does not have -- a third problem,
P3's. Recorded as an open question rather than resolved by tuning.

Also reconciled against the RE agent's new work: Q8 is answered -- the SE waves
are located in `Static.slb` (move/confirm/back), which unblocks P6's audio; and
the title's transitions are a lookup by NAME, giving P3/P5 the game's own screen
vocabulary as candidate `goto` targets, marked as the name match it is.
This commit is contained in:
Sylpheed port agent
2026-08-28 19:51:57 +00:00
parent 0db45b4c66
commit 89de93b18f
5 changed files with 251 additions and 7 deletions

View File

@@ -272,3 +272,122 @@ Both renderers skip them. The bundle declares them and carries **zero** RATC
children for either, so there is no texture on the disc to export — this is a
property of the disc, not a gap in the exporter, and `ScreenView` reports it as
`no sprite in the export` rather than dropping it silently.
---
## P2 — keyframe animation, 2026-08-28
### The time unit is authored, in one file, and says loudly that it is not on the disc
`authored/timing.json`. HANDOFF Q1 is answered — linear ramp, 2 units per
rendered frame, 1 unit = 1/60 s — but that conversion is **measured off the
running game**, not read from a file, which is exactly the case the
derived/authored split exists for. It is expressed as
`keyframe_units_per_second: 60` rather than seconds-per-unit so the value is
exact instead of a repeating decimal, and it carries the two independent lines
that support it. `t` stays raw everywhere in `export/`; seconds appear only
where this file is applied, which is one line of `boot.gd`.
`exit_ramp_seconds` is deliberately **null**. See below.
### The timeline stops at the last *timed* keyframe, and never plays the exit
The last keyframe of every group carries **no `t`** — the disc has no time slot
there. Across this export that final frame is an *exit* pose: for 116 of 134
elements it differs from the last timed keyframe **in alpha only** (a fade-out),
for 12 it is the loading splash's scale-and-slide exit, and for 6 it is
identical (no exit animation at all).
So the group is `pre-roll → ramp in → hold → [exit]`, and the port plays it up to
the hold and stops. Playing into the exit would mean **inventing how long the
ramp takes**, because the disc does not say. That duration is the screen
transition — HANDOFF Q7 measured it at ~0.4 s — and it belongs to P3, with its
own evidence. This is why `exit_ramp_seconds` is null rather than 0.4: P2 has no
business holding it.
### The interpolation is checked by where it lands, not by inspection
For **8 of the 12** screens the settled timeline is **byte-identical** to the
`--pose=rest` render. That is the useful assertion: the port walks the keyframes
with an authored time unit and arrives, to the pixel, at the pose the pinned
decoders independently identify as the resting one. `tools/screen-strip` reports
this per screen, so a change to the interpolation that drifts by one unit shows
up as a diff rather than as nothing.
The four that differ do so for two distinct reasons, below.
## `rest` misidentifies six elements, and the running game says so
On `main_menu`, the settled timeline and `rest` differ in exactly one region:
**400×470 at (440,108)** — the bounding box of `ptframe1` and `ptframe2`, and
nothing else on the screen.
`rest` puts both at their **first** keyframe: off-position and fully
transparent. The keyframes say they slide (620,108)→(440,108) and (403,267)→
(583,267) while fading 0x00→0xff, and then hold that pose for their last three
keyframes including the untimed one.
`/reborn/docs/re/captures/main-menu-oracle.png`, a capture of the running game,
**shows them**: the bright circuit-frame bracket around the menu, with a ring at
the bottom right. Cropping the same 250×180 region from the capture and from
both renders puts the ring and its elbow trace in the port's timeline render
**pixel-aligned with the game's**, and absent from the `rest` render. That is
geometry, not luminance, so it does not depend on the capture's gamma or on the
fact that it was taken with `NEW GAME` focused.
### Why the decoders get it wrong, precisely
`ui_layout::rest_plateau` excludes a run of identical keyframes that **ends the
group**, because that run is normally the exit — the comment cites the pause
menu, where taking the trailing run erased the word PAUSE. That exclusion is
right in general and wrong for an element with **no exit animation**, where the
trailing run *is* the hold. The rule then falls back to an earlier run, which
for a slide-in is the invisible pre-roll.
The condition that identifies the affected elements exactly, with no false
positives in this export, is:
> the final untimed keyframe has the **same pose** as the last timed keyframe
Six elements match it and `rest` misses all six: `ptframe1`/`ptframe2` on
`main_menu` and `main_menu_jp`, and `pteff02` on `title` and `title_jp`. This is
a **finding for the RE agent** about `sylpheed-formats`, not something this port
fixes: the decoders are pinned and must not be reimplemented here. The port
simply does not use `rest` — it derives the arrived pose from the keyframes,
which needs no heuristic — and `verify-screen` still asks for `--pose=rest` so
that renderer-vs-renderer diffing compares like with like.
Note what this says about P1: the port and the reference renderer **agreed** on
`main_menu` to 3/255, and both were missing two elements the game draws. Two
renderers reading the same field through the same decoder agreeing is not
evidence that the field is right. `docs/BLOCKED.md` had already said that about
the pivot; here it bit for real.
## The title is not settled, and P2 does not claim it
`title` and `title_jp` differ between the two modes by much more (max 142 and
247), and there the disagreement is **not** the six-element bug alone. `rest`
picks a mid-timeline hold for several glows (`pteff01`, `ptlogoall_eff`,
`ptlogoall_eff2`, `ptlogo_back2eff5`) where the timeline runs on to a much
brighter pose.
I could not settle which is right, and did not try to make the numbers agree:
* No element's alpha ever reverses direction anywhere in this export, so the
title's 4.48 s timeline is a slow one-way ramp, not a pulse — which removes the
obvious reason to expect a loop, but does not prove there is none.
* The only live title capture composites the **`PRESS Ⓐ` plate (build 2) over
the title (build 4)**, so it cannot be diffed against build 4 alone. Mean
luminance is oracle 64.1, `rest` 62.8, timeline 80.0 — which looks like it
favours `rest`, except that the plate *adds* brightness and `rest` is carrying
a 25 % black dim quad (`pteff02`) that is itself one of the six misidentified
elements. The comparison is confounded in both directions and settles nothing.
* **Both modes are visibly wrong anyway.** Side by side with the capture, the
port draws a washed-out cyan glow slab across the logo that the running game
does not have — in `rest` mode too. That is a third problem, independent of
this one, and it is P3's.
So: the timeline is the default because it is derived from the disc's own
keyframes with one measured constant and no heuristic, and because it is proven
right on the screen this milestone gates. On the title it is **unverified**, and
P3 should not assume P2 settled it.