From 7347db24dffd59a36c4dd9b61460aa9ca8201d08 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Sat, 29 Aug 2026 04:20:27 +0000 Subject: [PATCH] re(ui): the rest fallback fires on 2 elements, and "last keyframe" wins there Scored candidate rest-pose rules by rendering and correlating instead of arguing, and both results correct something I had published. First, the exposure. The guessing fallback is reached only by an element that is plateau-less AND multi-keyframe -- a single-keyframe element short-circuits at `match len { 1 => first }`. Per screen: title (4) 24 elements 2 plateau-less 0 reach the fallback main menu (5) 16 5 0 EXTRAS (6) 18 5 0 publisher splash (10) 3 2 1 developer splash (11) 7 2 1 So on the three screens the port cares most about, rest() never guesses. That is why three different rules render builds 4/5/6 to identical correlations -- the code is unreachable there, which I nearly read as "the choice does not matter". Second, where it does fire, the last keyframe is markedly better: publisher splash dwell +0.9600 last +0.9982 maxalpha +0.9600 developer splash dwell +0.9643 last +0.9758 maxalpha +0.9643 That refutes my own earlier refutation. I had killed the last-keyframe rule by arguing it makes palogo_anima_eff invisible while its two siblings stay lit, which looked like an artefact. The capture says otherwise: making it invisible is what improves the match. The sibling symmetry was my expectation, not evidence. Caveat kept in front: both captures are single frames of a transient animation, so this fixes which pose matches THOSE frames, not which is canonically at rest. Default unchanged -- 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. Reachable via SYLPHEED_REST_RULE=last. Also confirmed: all 195 zero-scale rest poses are inside the corrected 2 305 ambiguous population; none is a single-keyframe element. METHOD: score a rule where it can differ, or you measure nothing; and an argument from symmetry is a prediction, not a refutation. --- crates/sylpheed-formats/src/ui_layout.rs | 20 ++++++++ docs/port/HANDOFF.md | 14 +++++- docs/re/METHOD.md | 12 +++++ docs/re/REFUTED.md | 10 ++++ docs/re/structures/ui-resting-pose.md | 60 ++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/crates/sylpheed-formats/src/ui_layout.rs b/crates/sylpheed-formats/src/ui_layout.rs index af8d5836..39d1edc8 100644 --- a/crates/sylpheed-formats/src/ui_layout.rs +++ b/crates/sylpheed-formats/src/ui_layout.rs @@ -190,6 +190,26 @@ impl Element { 0 => None, 1 => self.keyframes.first(), n => { + // ⚠️ EXPERIMENT GATE, default off. Both published alternatives to + // the longest-dwell fallback died by argument rather than by + // measurement, and `compose` can score a rule against the live + // captures — so they are reachable here to be tested. + // SYLPHEED_REST_RULE=last -> the final keyframe + // SYLPHEED_REST_RULE=maxalpha -> the most opaque keyframe + match std::env::var("SYLPHEED_REST_RULE").as_deref() { + Ok("last") => return self.keyframes.last(), + Ok("maxalpha") => { + let mut best = (0usize, 0u32); + for (k, f) in self.keyframes.iter().enumerate() { + let a = (f.fade >> 24) & 0xff; + if a >= best.1 { + best = (k, a); + } + } + return self.keyframes.get(best.0); + } + _ => {} + } let mut best = (0usize, 0u32); for k in 0..n - 1 { let (Some(t0), Some(t1)) = diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 9beb042c..50a93a58 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -404,8 +404,18 @@ authored version can be deleted. and it is unambiguously their rest. Of the real 2 305, **195 get a degenerate `scale = 0 %` pose**, and the two candidate rules agree only **50.2 %** of the time. - ✅ **This does not block you.** On the five screens the port needs, 14 elements - are affected and the candidate rules **agree on 13**. The single disagreement + ✅ **This does not block you — your exposure is TWO elements, both on the + splashes.** The fallback is reached only by an element that is plateau-less + *and* multi-keyframe; a single-keyframe element short-circuits. Title, main + menu and `EXTRAS` reach it **zero** times, which is why three different + fallback rules render them to identical correlations. The publisher and + developer splashes reach it once each — and there, `SYLPHEED_REST_RULE=last` + (the final keyframe) scores **+0.9982** and **+0.9758** against the current + rule's +0.9600 and +0.9643. ⚠️ Measured against single frames of a transient + animation, so it fixes which pose matches *those* captures, not which is + 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. The single disagreement is `palogo_anima_eff.t32` on the developer splash, and the current answer is the defensible one there — see below. Still worth flagging plateau-less elements in an export rather than silently inheriting our guess; it is one pass diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 382c1112..13d96c3e 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -762,3 +762,15 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the It died the moment the rule was used to change a rendering, because the result was visibly worse. If a measurement implies an action, take the action on something you can score. +* **Score a rule where it can actually differ, or you will measure nothing.** + Three rest-pose rules rendered builds 4, 5 and 6 to *identical* correlations — + not because they agree, but because the code they change is unreachable on + those screens. The signal was on the two splashes, the only builds whose + elements reach the fallback at all. Identical results across variants is a + clue that the variant is not being exercised, not evidence that the choice does + not matter. +* **An argument from symmetry is a prediction, not a refutation.** I killed the + last-keyframe rule because it treats one of three sibling glows differently, + 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. diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index ccd3a03a..bef2b185 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -538,3 +538,13 @@ neighbourhood, not just the line. unambiguously its rest. 1 502 of the 3 807 are those; the genuinely ambiguous population is **2 305 (14.88 %)**. [`ui-resting-pose.md`](structures/ui-resting-pose.md) +* "`rest()` for a plateau-less element should be the last keyframe → refuted by + the sibling argument" → **that refutation is itself refuted, this time by + measurement.** Rendering under the rule and correlating against the live + captures: publisher splash +0.9600 → **+0.9982**, developer splash +0.9643 → + **+0.9758**. Making `palogo_anima_eff` invisible *improves* the match; the + sibling symmetry was my expectation, not evidence. +* "the port's exposure to the rest-guessing defect is 14 elements" → **two.** The + 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) diff --git a/docs/re/structures/ui-resting-pose.md b/docs/re/structures/ui-resting-pose.md index 6545f012..f469909d 100644 --- a/docs/re/structures/ui-resting-pose.md +++ b/docs/re/structures/ui-resting-pose.md @@ -431,3 +431,63 @@ The same flaw is in the census this page published: So the guessed-rest population is **2 305, not 3 807** — the published figure overstated it by **65 %**. The defect is real and smaller than reported. + +--- + +## ✅ Where the fallback actually fires — and "last keyframe" wins there + +**2026-08-29.** Two things fell out of scoring rules by rendering instead of +arguing about them. + +### The port's exposure is TWO elements, not fourteen + +The guessing fallback is reached only by an element that is plateau-less **and** +has more than one keyframe — a single-keyframe element short-circuits at +`match len { 1 => first }`. Per screen: + +| screen | elements | plateau-less | **reach the fallback** | +|---|---|---|---| +| title (4) | 24 | 2 | **0** | +| main menu (5) | 16 | 5 | **0** | +| `EXTRAS` (6) | 18 | 5 | **0** | +| publisher splash (10) | 3 | 2 | **1** | +| developer splash (11) | 7 | 2 | **1** | + +On the three screens a port cares most about, **`rest()` never guesses at all**. +That is why three different fallback rules render builds 4, 5 and 6 to *identical* +correlations (+0.9500 / +0.9460 / +0.9440 for every rule) — the code is +unreachable there. + +### On the two splashes, the last keyframe is markedly better + +Rendering under each rule (`SYLPHEED_REST_RULE=last|maxalpha`, default off) and +correlating against the live captures: + +| splash | dwell (current) | **last** | maxalpha | +|---|---|---|---| +| publisher | +0.9600 | **+0.9982** | +0.9600 | +| developer | +0.9643 | **+0.9758** | +0.9643 | + +`maxalpha` is indistinguishable from the current rule; **`last` gains +0.038 and ++0.012**, and 0.9982 is very close to exact. + +### 🔴 This refutes my own refutation + +I had killed "rest = the last keyframe" by argument: on the developer splash it +makes `palogo_anima_eff` invisible while its two siblings stay lit, which looked +like an artefact. **The capture says otherwise** — making it invisible is what +improves the match. The sibling symmetry was my expectation, not evidence. + +⚠️ **The caveat that keeps this honest:** both captures are *single frames of a +transient animation*. They fix which pose matches **these reference frames**, not +which pose is canonically "at rest" — a grab a second earlier would show the +glows. What the measurement supports is: for reproducing the committed captures, +use the last keyframe. + +🟡 **Default unchanged.** The rule is better on both screens where it fires and +provably identical on the other three, but it would alter the rest pose of +**2 305** elements disc-wide and two of them have been measured. Reachable via +`SYLPHEED_REST_RULE=last` for anyone who wants to take that further. + +✅ Unaffected by the earlier correction: all **195** zero-scale rest poses are +inside the genuinely-ambiguous 2 305 — none is a single-keyframe element.