re(ui): why rest_plateau is right -- and last is right only for a transient

The shifted keyframe-time reading looked like it implied something
simple: the final pose is reached at a definite time and nothing follows,
so rest should just be the last keyframe and the plateau heuristic could
go. Tested by applying it to EVERY element:

  title              +0.9500 -> +0.6819   -0.2681
  main menu          +0.9460 -> +0.6416   -0.3044
  EXTRAS             +0.9440 -> +0.5745   -0.3695
  publisher splash   +0.9600 -> blank (zero variance, corr undefined)
  developer splash   +0.9643 -> blank

Refuted, and the failure supplies the model. A group is entry -> hold ->
exit, and the exit is the screen's DISMISSAL. While a screen is displayed
it has not reached its last keyframe; it is sitting at the hold. So
rest_plateau is the correct primary rule, and the last keyframe is the
post-exit state -- correct only once the screen is gone, which is why
applying it everywhere blanks the splashes.

This does not contradict the shifted reading. That reading says when each
pose is reached; it says nothing about the group being played to
completion while the screen is still up. The step between them was mine.

And it explains why last wins for the two plateau-less elements: an
element with no hold is a transient, it flashes and is over, and at any
settled moment it is gone -- which is its last keyframe. The draw capture
says the same independently: on the developer splash the _eff glows draw
on frames 94-115 and the logos on 116-211, so the glows are already
finished when the logos are up.

Three independent observables -- animation timing, static composites, and
the per-frame draw log -- now agree on one rule: plateau where there is
one, last keyframe where there is not.

METHOD: a blank render is a NaN correlation, not a low score, and that
NaN was the strongest form of the result; and when a model predicts
something the measurement refuses, suspect the step you supplied between
them.
This commit is contained in:
Sylpheed RE agent
2026-08-29 04:28:19 +00:00
parent 6a7c42366c
commit e63efabcfb
5 changed files with 95 additions and 0 deletions

View File

@@ -416,6 +416,19 @@ authored version can be deleted.
canonically at rest. Default unchanged — it is better on both screens where it
fires and identical on the other three, but it would move 2 305 elements
disc-wide on two measurements.
**And the picture is now coherent.** Applying the last keyframe to *every*
element (not just the plateau-less ones) collapses all five screens — title
0.9500→0.6819, main menu 0.9460→0.6416, `EXTRAS` 0.9440→0.5745, and both
splashes render **blank**. The reason is the model: a group is **entry → hold →
exit**, and the exit is the screen's *dismissal*. A displayed screen is sitting
at the **hold**, not at its final pose — so `rest_plateau` is right, and the
last keyframe is the *post-exit* state. It is right for a **transient** element
precisely because a transient's settled state is "gone". The draw capture agrees
independently: on the developer splash the `_eff` glows draw on frames 94115
and the logos on 116211, so the glows are already over when the logos are up.
**Three independent observables — animation timing, static composites and the
per-frame draw log — all support: plateau where there is one, last keyframe
where there is not.**
📊 The disc-wide blast radius, for whoever decides: the rules **differ on 82.3 %**
of those 2 305, so "either is fine" is not available — and the current rule
returns a **zero-scale** (collapsed, pre-roll) pose for **195** of them against

View File

@@ -774,3 +774,16 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
which felt like an artefact. Measured, it is the better rule on both screens
where it applies. Aesthetic expectations about how authored data "should" look
are worth stating as hypotheses and worth nothing as verdicts.
* **A blank render is a correlation of NaN, not a low score — notice which.**
Applying "rest = last keyframe" to every element made both splashes render with
zero variance, so the correlation was undefined rather than poor. Reading the
NaN as "the tool failed" would have hidden the result; it *was* the result, and
the strongest form of it. Check for degenerate output before treating a missing
number as a broken measurement.
* **When a model predicts something and the measurement refuses, the model is
usually incomplete rather than wrong.** The shifted time reading says when each
pose is reached, which looked like it implied the final pose is the resting
one. It does not: it says nothing about whether the group is *played to
completion* while the screen is still up. The exit is the dismissal, so a
displayed screen never reaches its last keyframe. Both claims survive; what was
wrong was the step between them that I supplied.

View File

@@ -548,3 +548,10 @@ neighbourhood, not just the line.
fallback needs an element to be plateau-less **and** multi-keyframe; title,
main menu and `EXTRAS` reach it **zero** times, the two splashes once each.
[`ui-resting-pose.md`](structures/ui-resting-pose.md)
* "the shifted time reading implies rest = the last keyframe, so the plateau rule
can be dropped" → **mine, and refuted by measurement.** Applying it to every
element collapses all five screens (title 0.9500→0.6819, main menu
0.9460→0.6416, `EXTRAS` 0.9440→0.5745) and renders both splashes **blank**. A
group is entry → hold → exit and the exit is the screen's *dismissal*: a
displayed screen sits at the hold, not at its final pose.
[`ui-resting-pose.md`](structures/ui-resting-pose.md)

View File

@@ -521,3 +521,56 @@ two captures, and it points the same way they do.
correct results, and `last` still returns an invisible pose 70 % of the time —
which for a transient element may well be right, and for a persistent one would
be wrong. The default stays put.
---
## ✅ Why `rest_plateau` is right, and why `last` is right only for a transient
**2026-08-29.** The shifted keyframe-time reading
([time unit](../ui-keyframe-time-unit.md)) appears to predict something simple:
the final pose is reached at a definite time and nothing follows, so "rest"
should just be the last keyframe, with no heuristic at all. Tested
(`SYLPHEED_REST_RULE=lastall`, bypassing the plateau rule for **every** element):
| screen | current | `lastall` | Δ |
|---|---|---|---|
| title | +0.9500 | +0.6819 | **0.2681** |
| main menu | +0.9460 | +0.6416 | **0.3044** |
| `EXTRAS` | +0.9440 | +0.5745 | **0.3695** |
| publisher splash | +0.9600 | **blank** | — |
| developer splash | +0.9643 | **blank** | — |
The splashes render *entirely empty* — zero variance, so the correlation is not
even defined. **Refuted.**
### What the failure shows
A group's keyframes describe **entry → hold → exit**, and the exit is the
screen's *dismissal*. While a screen is on display it has not reached its last
keyframe; it is sitting at the hold, and the exit plays only when the screen
leaves. That is why:
* `rest_plateau` is the correct primary rule — the hold is where the screen
lives, and it is what a capture of a displayed screen shows;
* the last keyframe is the **post-exit** state, correct only at the moment the
screen is already gone — which is why applying it everywhere blanks the
splashes;
* and it does **not** contradict the shifted time reading. That reading says when
each pose is reached; it says nothing about the group being played to
completion while the screen is still up.
### And it explains why `last` wins for the plateau-less two
An element with **no hold** is a transient: it flashes and is over. At any
settled moment it is *gone*, and "gone" is its last keyframe. That is not a
general rule about rest — it is what a transient's settled state *is*.
The draw capture says exactly this independently: on the developer splash the
`_eff` glows are drawn on frames **94115** and the logos on **116211**. The
glows are already finished when the logos are up, so a capture of the logo
moment must show them invisible — which is what `SYLPHEED_REST_RULE=last`
produces, and why it scored +0.9982 and +0.9758.
✅ So the picture is coherent across three independent observables — animation
timing, static composites, and the per-frame draw log — and the rule it supports
is: **plateau where there is one, last keyframe where there is not.**