re(ui): withdraw the render evidence -- rest()'s dwell fallback is unsound

Last iteration I reported a 13.1% render change in GP_TITLE build 7 as
evidence AGAINST the keyframe-time shift, arguing that language twins
should match in brightness. Withdrawn. Localising the diff to an element
shows it is not about the time association at all.

The element is ptlogo_eff3.t32, a transient bloom:

   46: (98,42)  100%,100% a=0
   61: (108,72)   0%,0%   a=0
  103: (108,72) 200%,200% a=255 r=80
    -: (108,72)   0%,0%   a=0   r=150

No two adjacent poses are equal, so there is no plateau, and rest() falls
through to its longest-dwell fallback. The longest gap is 61->103, during
which the sprite grows from nothing to 200% at full alpha and then
collapses. The rule returns whichever end of that movement the indexing
lands on: the invisible frame as decoded, the 200% peak shifted. An
896x389 sprite at 200% is larger than the screen, which accounts for the
entire 13.1% and the entire 4.9-unit luminance gap. I was comparing a
heuristic against itself.

The defect generalises, and structurally. A dwell gap is time spent
interpolating FROM pose k TO pose k+1; neither pose is held during it
unless the two are equal -- which is a plateau, and the plateau path has
already returned by then. So the fallback is guaranteed to be guessing
every time it is reached, under any reading of the times. Every element
with no two adjacent identical poses has a guessed rest pose, in our
renderer and in anything built from it.

Consequences: the case FOR the shift (26x on the hold:fade-out ratio) is
no longer opposed by render evidence -- 10 of 11 builds byte-identical,
the 11th differing only through this heuristic. It is still not adopted,
now because it flips this element to the visibly wrong answer, so the
shift and a decision about plateau-less elements must land together, and
neither half has a capture to verify against.

Default-mode suite green (122 passed, 0 failed across 3 suites so far).
This commit is contained in:
Sylpheed RE agent
2026-08-28 23:10:41 +00:00
parent 6f23f4d113
commit b947fa8e8e
4 changed files with 117 additions and 6 deletions

View File

@@ -268,12 +268,27 @@ authored version can be deleted.
also makes `rest()`'s plain dwell rule pick the visible hold instead of a
fully transparent pose, and removes the decoder's "last block's time is
unreadable" special case.
🔴 **What stops it:** rendering every build of six UI paks under both readings,
10 of 11 are byte-identical and one changes — `GP_TITLE` build 7, the Japanese
twin of build 4, by 13.1 % of pixels. Build 4 (the one with a live capture) is
unchanged, so the only build the shift moves is the one with no capture to
judge it, and the proxy goes against: twins should match in brightness, and
build 7 is 70.94 as decoded against build 4's 71.41, but 76.32 shifted.
~~🔴 **What stops it:** … `GP_TITLE` build 7 … twins should match in
brightness …~~ **Withdrawn 2026-08-28.** That render difference is **not**
evidence about the time association. It is one element — `ptlogo_eff3.t32`, a
transient bloom that grows `0 % → 200 %` at full alpha while rotating, then
collapses — and it has **no resting pose at all**. `rest()` falls through to
its dwell fallback and returns whichever end of that movement the indexing
lands on: the invisible frame as decoded, the 200 % peak shifted. An 896×389
sprite at 200 % is larger than the screen, which is the whole 13.1 % and the
whole luminance gap. I was comparing a heuristic, not a decode.
🔴 **And that is a real defect you should know about:** `rest()`'s dwell rule
is unsound *whenever it runs*. A dwell gap is time spent interpolating **from**
pose k **to** pose k+1; neither is held unless they are equal — which is a
plateau, and the plateau path has already returned by then. **So any element
with no two adjacent identical poses has a guessed rest pose**, in our renderer
and in anything built from it. You can test for that in one pass over the
keyframes; consider flagging it rather than inheriting our guess.
[`structures/ui-resting-pose.md`](../re/structures/ui-resting-pose.md)
🟡 **The shift is still not adopted**, now for a different reason: it flips
this element to the visibly wrong answer, so it and a decision about
plateau-less elements have to land together, and neither half has a capture to
verify against.
**Default unchanged**, experiment reachable via `SYLPHEED_KF_TIME_SHIFT=1`.
**What this means for you:** the interpolation *law* is settled (linear, 2
units/frame); a multi-keyframe group's *timing* is not — do not expect a