re: the title's light arc is five transient flashes, not a tone or rotation error

Records the settle-time decode, and corrects two claims it overturns.

docs/re/structures/ui-settle-time.md is the finding: a settled screen is one
instant every element is posed at, the disc names it (the midpoint of the
longest keyframe-free interval), and the title's arc closes from 33.22 to
11.79 with the clipped-pixel count landing on the console's 1459 within 0.5%.
Includes the disc-wide reach -- only 30% of bundles have a window wide enough
to trust -- and a three-way figure.

Withdrawn in ui-rotation-implemented.md:

  * "Flat. No minimum." was not a property of rotation. `at` posed LEAVES ONLY,
    so the scan moved the light sweeps and never touched the top-level flashes.
  * "our renderer does not draw ptlogo1/ptlogo2 at all" is wrong. Both are
    drawn; the four elements the diagnostic named are kind-0x4 ghosts sharing
    their template's name. Hiding the real ones makes the error WORSE by
    +5.20 and +7.47.
  * Its 10.92 baseline is not reproducible -- 14.07 at its own pre-change tag
    and 14.07 today -- so the "1.7% better" verdict rests on an unrecorded
    recipe. Flagged in title-residual-tone-vs-geometry.md too.

METHOD.md gains two traps: a shared CARGO_TARGET_DIR makes a `git worktree`
build silently replace the binary you run next (it cost three renders here,
caught only because a missing flag was a hard error rather than a wrong
number); and an aggregate computed per-element is not a state of the system.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
sylph-decoder
2026-08-29 19:29:12 +00:00
parent d0735d2c25
commit 38b80302b7
6 changed files with 264 additions and 0 deletions

View File

@@ -1786,3 +1786,56 @@ loop during development:
them is wrong and the disagreement is worth reporting back. them is wrong and the disagreement is worth reporting back.
* `docs/re/captures/` — framebuffer captures of the real screens, for anything * `docs/re/captures/` — framebuffer captures of the real screens, for anything
that has to be checked against the game rather than against our renderer. that has to be checked against the game rather than against our renderer.
## 2026-08-29 — the settled screen is one instant (`settle_units` is decodable)
**decoded.** You author `settle_units` per screen. The disc gives it:
`UiBuild::settle_time()` returns the midpoint of the **longest keyframe-free
interval** in a build, and `settle_window()` returns the interval so you can judge
it. Computed from the keyframe table alone — no capture involved.
**Why it matters beyond saving an authored constant.** Our renderer posed each
element at `Element::rest()`, its last *hold* keyframe, chosen independently of
every other element. For a transient that is exactly wrong: `ptlogo_back2eff1` is
a two-frame flash (`a=0` until t52, `255` at t5456, `0` by t58), so its last hold
*is* the peak and it burned forever. `GP_TITLE` build 4 has **five** such flashes
— one light sweep drawn as five staggered frames, all out by t110 — and drawing
them at once saturated the light arc behind the logo.
Against `live-title-build4-no-plate.png`, posing at the predicted t=198:
| | mean abs diff | arc band | pixels at clipping |
|---|---|---|---|
| console capture | — | — | **1 459** |
| `rest()` | 14.07 | 33.22 | 8 581 |
| **`--at 198`** | **12.06** | **11.79** | **1 452** |
The clipped count is unfitted and lands within 0.5 % of the console's.
⚠️ **Scope it.** Of the 1 758 composable bundles with ≥ 2 keyframe times, only
**30 %** have a window ≥ 30 units; **42 %** have one under 10 units. The latter are
mostly `loop*` fragments — they are *meant* to be in motion and have no settled
pose. Check `settle_window()`'s width before taking the midpoint.
🔴 **A retraction you should act on.** I previously told you Reborn "does not draw
`ptlogo1` / `ptlogo2` at all", and that our two renderers were therefore not
comparable on the title. **Both are wrong.** Build 4 declares *six* ptlogo
elements: indices 0 and 1 are kind `0x0`, alpha 255, and are drawn; indices 25
are kind `0x4` ghost instances at (116,7) and (437,508), alpha 0, skipped
deliberately. Hiding element 0 makes the error *worse* by +5.20 whole-frame and
+7.61 in the band. The renderers are comparable, and the ptlogos were never the
residual.
🔴 **And `ComposeOptions::at` posed leaves only** — my own fix for a bug I
mis-diagnosed. That is why the earlier rotation pose scan was flat over t = 0…600:
it moved the sweeps and never touched the top-level flashes. `at` now poses
everything; `at = None` is byte-identical (verified with `cmp`) and the
pre-rotation tag renders identically at rest, so nothing regressed.
**Not settled:** the remaining 12.06, which is broad and level-like rather than
spatial — consistent with the tone term. And the **10.92** baseline quoted in
`ui-rotation-implemented.md` is **not reproducible**: the same command gives 14.07
at that document's own pre-change tag and 14.07 today. Treat conclusions resting
on it as unverified.
Detail, controls and census: [`docs/re/structures/ui-settle-time.md`](../re/structures/ui-settle-time.md).

View File

@@ -167,3 +167,4 @@ files, which is how the same ground got covered twice.
| [`structures/slb-bank-header-not-a-wave.md`](structures/slb-bank-header-not-a-wave.md) | Why a music bank read as THREE sub-waves when the census says two | ✅ **decoded**: the third is the **bank header**, emitted by our own reader. `to_xma_riffs`'s hybrid branch derives a leading packet stream's start as `first_riff % 2048`, which is right only for a header shorter than one packet; a music bank's header is exactly **5 packets (10 240 B)**, so the modulus gave 0 and the whole header came back as sub-wave 0. The header states its own length at `+0x24` in blocks. Disc-wide over 9 519 `sound.pak` entries: **28** match the header signature at offset 0 (ids 10011023, 11011105), **28/28** end exactly at the first `RIFF`, **0** have a gap, **0** false positives — so a header at offset 0 and a leading packet stream never coexist. Decode control, same chain, same bank: the emitted region gives **0.009 s** against **87.744 s** for the real wave 0. Corroborated by the runtime XMA probe, which saw exactly two streams at the main menu. Fixed + 2 regression tests; the `VOICE_D_453` recovery is untouched (10/10 green) | | [`structures/slb-bank-header-not-a-wave.md`](structures/slb-bank-header-not-a-wave.md) | Why a music bank read as THREE sub-waves when the census says two | ✅ **decoded**: the third is the **bank header**, emitted by our own reader. `to_xma_riffs`'s hybrid branch derives a leading packet stream's start as `first_riff % 2048`, which is right only for a header shorter than one packet; a music bank's header is exactly **5 packets (10 240 B)**, so the modulus gave 0 and the whole header came back as sub-wave 0. The header states its own length at `+0x24` in blocks. Disc-wide over 9 519 `sound.pak` entries: **28** match the header signature at offset 0 (ids 10011023, 11011105), **28/28** end exactly at the first `RIFF`, **0** have a gap, **0** false positives — so a header at offset 0 and a leading packet stream never coexist. Decode control, same chain, same bank: the emitted region gives **0.009 s** against **87.744 s** for the real wave 0. Corroborated by the runtime XMA probe, which saw exactly two streams at the main menu. Fixed + 2 regression tests; the `VOICE_D_453` recovery is untouched (10/10 green) |
| [`title-plate-delay-measured.md`](title-plate-delay-measured.md) | How long the boot title shows build 4 before the `PRESS Ⓐ` plate | ✅ **decoded after a refutation**: build 2 and build 4 run on **one clock started together**, and the plate's own `ptbtn00` reaches `a=255` at `t=238`; the last build-in ramp ends at `t=118`, so the interval is a declared **120 units = 2.000 s**. 🔴 The instruction that shipped first — "wait 2.13 s after build 4 settles" — was **refuted by the port** with disc arithmetic and is corrected in place; 🔴 `rest.t` is **not** when a screen settles (it is the last hold keyframe before the exit: `ptlogo1` rests at `t=251` and stops moving at `t=42`). ⚠️ The wall-clock 2.13 s is 6.7 % long because Canary presents at **28.06 / 28.14 fps** against a nominal 30, matching the corpus's independent **28.5 fps**; author the 120 units. ✅ **measured**, two independent boots: **2.138 s** and **2.132 s** from the frame build 4 settles (glyph = its no-plate 154, motion → 0). Agreeing to **6 ms**. So the boot title's end state is **not** plate-free and a compositor must draw **two builds at once**. ⚠️ Measure from *settled*, not from first pixels — "first drawn → plate" is 3.78 s vs 4.26 s across the same two runs, because the build-in animation's own duration varies with emulator frame pacing. Plate pulse re-measured at 2.12/2.19/2.34/2.31 s (mean 2.24), replicating the corpus's ≈2.3 s. ✅ black hold between screens bracketed at **0.140.30 s**, consistent with the declared 12 units. 🔴 the Ⓐ→menu latency is still **not** available: both runs freeze one frame for ~1.4 s at surface mean **26.626** — agreeing between runs to 1e-6, and reproduced with stream restarts disabled — which is a guest **load stall**, not the capture path. Probe: 8.7 ms/frame, 7.97/7.98 fps against a requested 8, controls 9/9 + 4/4 | | [`title-plate-delay-measured.md`](title-plate-delay-measured.md) | How long the boot title shows build 4 before the `PRESS Ⓐ` plate | ✅ **decoded after a refutation**: build 2 and build 4 run on **one clock started together**, and the plate's own `ptbtn00` reaches `a=255` at `t=238`; the last build-in ramp ends at `t=118`, so the interval is a declared **120 units = 2.000 s**. 🔴 The instruction that shipped first — "wait 2.13 s after build 4 settles" — was **refuted by the port** with disc arithmetic and is corrected in place; 🔴 `rest.t` is **not** when a screen settles (it is the last hold keyframe before the exit: `ptlogo1` rests at `t=251` and stops moving at `t=42`). ⚠️ The wall-clock 2.13 s is 6.7 % long because Canary presents at **28.06 / 28.14 fps** against a nominal 30, matching the corpus's independent **28.5 fps**; author the 120 units. ✅ **measured**, two independent boots: **2.138 s** and **2.132 s** from the frame build 4 settles (glyph = its no-plate 154, motion → 0). Agreeing to **6 ms**. So the boot title's end state is **not** plate-free and a compositor must draw **two builds at once**. ⚠️ Measure from *settled*, not from first pixels — "first drawn → plate" is 3.78 s vs 4.26 s across the same two runs, because the build-in animation's own duration varies with emulator frame pacing. Plate pulse re-measured at 2.12/2.19/2.34/2.31 s (mean 2.24), replicating the corpus's ≈2.3 s. ✅ black hold between screens bracketed at **0.140.30 s**, consistent with the declared 12 units. 🔴 the Ⓐ→menu latency is still **not** available: both runs freeze one frame for ~1.4 s at surface mean **26.626** — agreeing between runs to 1e-6, and reproduced with stream restarts disabled — which is a guest **load stall**, not the capture path. Probe: 8.7 ms/frame, 7.97/7.98 fps against a requested 8, controls 9/9 + 4/4 |
| [`menu-idle-and-b-2026-08-29.md`](menu-idle-and-b-2026-08-29.md) | The main menu does not idle back to the title — and four durations that were a pipeline | ✅ **refuted**: no self-return in **≥ 60 s** untouched; the ~810 s idle belongs to the **title**. 🟡 Ⓑ→title ordering measured, latency not. 🔴 `classify_array` at **1503 ms/frame** drained an 8 fps stream at 0.64 fps and manufactured four latencies (24.66 s / 15.58 s / 25.60 s / 20.26 s) — all withdrawn; a backlog preserves ordering and destroys durations | | [`menu-idle-and-b-2026-08-29.md`](menu-idle-and-b-2026-08-29.md) | The main menu does not idle back to the title — and four durations that were a pipeline | ✅ **refuted**: no self-return in **≥ 60 s** untouched; the ~810 s idle belongs to the **title**. 🟡 Ⓑ→title ordering measured, latency not. 🔴 `classify_array` at **1503 ms/frame** drained an 8 fps stream at 0.64 fps and manufactured four latencies (24.66 s / 15.58 s / 25.60 s / 20.26 s) — all withdrawn; a backlog preserves ordering and destroys durations |
| [`structures/ui-settle-time.md`](structures/ui-settle-time.md) | Which instant a "settled screen" composite depicts | ✅ **decoded**: a settled screen is **one instant every element is posed at**, and the disc names it — the midpoint of the **longest keyframe-free interval** in the build (`UiBuild::settle_time` / `settle_window`). 🔴 `rest()` is *not* that: it picks each element's last hold **independently**, so a two-frame flash holds at its **peak** and burns forever. `GP_TITLE` build 4 has five staggered flashes (`ptlogo_back2eff1``eff5`, all extinguished by t110) that `rest()` draws simultaneously and permanently, saturating the light arc. Predicted t=198 from `[160,236]` **before scoring**: arc band **33.22 → 11.79**, clipped pixels **8 581 → 1 452** against the console's **1 459** (an unfitted statistic), whole frame 14.07 → 12.06; controls at t=100 and t=358 are far worse, and a hand-picked visibility list reaches the identical 12.06/11.79/1 452. Controls: `at=None` byte-identical (`cmp`), pre- and post-rotation tags both 14.07, 13 paint-order tests green. ⚠️ **Reach**: of 1 758 bundles with ≥2 keyframe times only **30 %** have a window ≥ 30 units and **42 %** under 10 — mostly `loop*` fragments that never settle; check the width. 🔴 Withdraws two claims in [`ui-rotation-implemented.md`](structures/ui-rotation-implemented.md) — its "Flat. No minimum." (`at` posed **leaves only**) and its "Reborn does not draw `ptlogo1`/`ptlogo2`" (both **are** drawn; only kind-`0x4` ghosts are skipped, and hiding the real ones makes the error *worse* by +5.20/+7.47). ❔ its **10.92** baseline is unreproducible — 14.07 at both tags |

View File

@@ -1025,3 +1025,37 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
test needs cross-correlation to align first and an agreed downmix, and only then test needs cross-correlation to align first and an agreed downmix, and only then
is a pass mark like ">40 dB down" meaningful. Reporting the 9 dB as a result is a pass mark like ">40 dB down" meaningful. Reporting the 9 dB as a result
would have been a confident wrong number. would have been a confident wrong number.
## A shared `CARGO_TARGET_DIR` makes a worktree build replace the binary you run
`CARGO_TARGET_DIR=/sylph-home/re/target-container` is set for the whole container,
so **every checkout shares one target directory**. Build anything in a
`git worktree` — the obvious way to render from an old tag as a control — and the
binary at `$CARGO_TARGET_DIR/release/` is now the *other* checkout's. Cargo then
considers your main tree fresh and does not rebuild it.
It cost three renders here that silently used a CLI with no `--at` flag, and the
only reason it was caught is that the missing flag was a hard error. **A stale
binary that merely produces slightly different numbers would have been believed.**
After any worktree build, `touch` a source file and rebuild before measuring
anything — and prefer building the control's binary to an explicit
`--target-dir` of its own.
## `rest()` is one element's last hold, not the settled screen
`Element::rest()` picks each element's last **hold** keyframe *independently of
every other element*, so a composite built from it is not the screen at any moment
in time — it is a per-element maximum. For a transient this is exactly wrong: a
two-frame flash's last hold is the flash **peak**, so `rest()` leaves it burning
forever.
Five such flashes stack on the title and saturate the light arc; the band's error
against the console was 33.22, and 8 581 pixels sat at the clipping level where
the console has 1 459. Posing every element at one shared instant instead — the
midpoint of the longest keyframe-free interval — takes those to 11.79 and 1 452.
The general trap: **an aggregate computed per-element is not a state of the
system.** Ask what instant a composite claims to depict, and check that every
element was asked the same question. See
[`structures/ui-settle-time.md`](structures/ui-settle-time.md).

View File

@@ -1,5 +1,13 @@
# ✅ At least two thirds of the title's disagreement is GEOMETRY, not tone # ✅ At least two thirds of the title's disagreement is GEOMETRY, not tone
> ⚠️ **The 10.92 title baseline here could not be reproduced on 2026-08-29** —
> `screen render --build 4 --black` against the same capture gives 14.07 both at
> `formats-pin-2026-08-29c` and today. The *ratio* this document argues for may
> well survive; the absolute is in doubt. Separately, part of the title's residual
> is now known to be neither tone nor rotation but `rest()` posing five transient
> flashes at their peaks — [`ui-settle-time.md`](ui-settle-time.md) closes the
> light-arc band from 33.22 to 11.79.
**Classification: measured**, on committed captures. Answers a question the **Classification: measured**, on committed captures. Answers a question the
pending rotation decision needs: *how much would drawing the rotation actually pending rotation decision needs: *how much would drawing the rotation actually
buy?* buy?*

View File

@@ -1,5 +1,20 @@
# ✅ Option A implemented — the reference renderer rotates. ⚠️ It does not close the title. # ✅ Option A implemented — the reference renderer rotates. ⚠️ It does not close the title.
> 🔴 **Two claims below are withdrawn (2026-08-29), and the baseline is
> unreproducible.** See [`ui-settle-time.md`](ui-settle-time.md).
>
> * **"Flat. No minimum."** is explained, not a property of rotation:
> `ComposeOptions::at` was posing **leaves only**, so the scan moved the light
> sweeps and never touched the top-level flashes. No `t` could have helped.
> `at` now poses every element and the scan has a clear optimum.
> * **"our renderer still does not draw `ptlogo1` / `ptlogo2` at all"** is
> **wrong**. Both are drawn. The four elements the diagnostic reported are kind
> `0x4` ghost instances, skipped deliberately. Hiding the real ones makes the
> error *worse*.
> * ❔ The **10.92** baseline is **not reproducible** — the same command gives
> 14.07 at this document's own pre-change tag and 14.07 today — so the "1.7 %
> better" verdict rests on a recipe that was not recorded.
**Decision:** the human chose **Option A** (2026-08-29) — teach **Decision:** the human chose **Option A** (2026-08-29) — teach
`sylpheed-formats`' own renderer to draw `rotation_deg`, so it and the port stay `sylpheed-formats`' own renderer to draw `rotation_deg`, so it and the port stay
comparable and `verify-screen` keeps meaning *"someone is wrong"*. comparable and `verify-screen` keeps meaning *"someone is wrong"*.

View File

@@ -0,0 +1,153 @@
# The settled screen is one instant, not one hold per element
**Classification: decoded.** The value comes from the keyframe table alone —
no capture is consulted to compute it — and it is checked disc-wide. The
verification against the console capture is a *test* of the decode, not its
source.
## The claim
`Element::rest()` returns an element's last **hold** keyframe, chosen for that
element independently of every other element. A composite built from `rest()` is
therefore not a screen at any moment in time; it is a per-element maximum.
For an element that ends the screen settled, that is the same thing. For a
**transient** it is exactly wrong — a flash's last hold *is* the flash peak, so
`rest()` leaves it burning forever.
The settled screen is instead **one instant that every element is posed at**, and
the disc says which instant: gather every keyframe time in the build and take the
**longest interval containing none of them**. Inside that gap nothing has an
inflection, so every element is either holding or midway along a single linear
ramp. That is what "the screen has stopped changing" means, expressed in the only
vocabulary the file has.
`UiBuild::settle_time()` returns the midpoint of that interval;
`UiBuild::settle_window()` returns the interval, whose width is how much
confidence the midpoint deserves.
## The case that found it
`GP_TITLE` build 4. Seven elements share the light-arc band behind the logo:
| element | timeline | at rest | at t=198 |
|---|---|---|---|
| `ptlogo_back2eff1.t32` | a=0 → **255 at t5456** → 0 by t58 | **255** | 0 |
| `ptlogo_back2eff2.t32` | a=0 → **255 at t5860** → 0 by t62 | **255** | 0 |
| `ptlogo_back2eff3.t32` | staggered, same shape | **255** | 0 |
| `ptlogo_back2eff4.t32` | staggered, same shape | **255** | 0 |
| `ptlogo_back2eff5.t32` | 255 at t6466 → 192 at t74 → **0 by t110** | **255** | 0 |
| `ptlogo_back2eff.t32` | 255 at t66, **holds to t238**, exits t244 | 255 | 255 |
| `ptlogo_back2.t32` | 255 at t80, **holds to t243**, exits t249 | 255 | 255 |
The five numbered ones are a single light sweep travelling left to right across
the logo, drawn as five staggered two-frame flashes. They are all extinguished by
t110. `rest()` draws them **simultaneously and permanently**, and five stacked
white glows (`ptlogo_back2eff5.t32` decodes to a mean opaque RGB of exactly
255,255,255) drive the arc to saturation.
## What it looks like
![the light arc: console, rest(), and --settle](../captures/title-builds/title-arc-rest-vs-settle.png)
The console's arc is a **thin white outline with a pink hooked tail**. `rest()`
renders a fat solid white blob that swallows the tail completely — five white
glows stacked. `--settle` reproduces the console.
⚠️ A measurement trap worth recording: sampling the *brightest 3 %* of that band
gives (252,245,239) for the console against (255,255,255) for `rest()` — nearly
neutral, and it reads as "no hue difference". That statistic samples the white
outline in both and never touches the pink tail. What it *did* expose was the
count: at a 97th-percentile threshold the console has 1 459 pixels above it and
`rest()` has 8 581, i.e. a saturated plateau. **The pixel count carried the
signal that the mean colour hid.**
## The measurement
Against
[`live-title-build4-no-plate.png`](../captures/title-builds/live-title-build4-no-plate.png),
whole frame and over the arc band (y 95215, x 8301230). The third column counts
pixels at or above the band's 97th-percentile luminance — a *saturation* statistic,
independent of the error being minimised, and not fitted.
| | mean abs diff | arc band | clipped px |
|---|---|---|---|
| **console capture** | — | — | **1 459** |
| `rest()` — the default | 14.07 | 33.22 | 8 581 |
| **`--at 198` — predicted from the disc** | **12.06** | **11.79** | **1 452** |
| `--at 100` — control, before the window | 19.13 | 20.46 | 1 468 |
| `--at 358` — control, past every exit | 28.80 | 53.37 | 1 447 |
**t=198 was computed before the render was scored.** `settle_window()` on build 4
returns `[160, 236]`; 198 is its midpoint. A separate sweep of t in 40…300 finds a
flat optimum over t ∈ [180, 238] at 12.06 / 11.79 / 1 452, which contains the
prediction.
⚠️ The clipped-pixel count discriminates `rest()` from *any* single instant — the
two controls also land near 1 459 — so it identifies the **blow-out**, not the
time. The time is identified by the mean, and by the disc.
## The controls
***`at = None` is byte-identical.** `compose` with no `at` produces the same
bytes before and after this change (`cmp`, exact). The default path is untouched.
***The change did not regress the resting composite.** Rendering build 4 at
`formats-pin-2026-08-29c` (pre-rotation, pre-leaf) and at the current tree both
give **14.07 / 33.22**.
***A hand-picked visibility list reaches the same answer.** Leave-one-out over
all 24 elements, then hiding exactly `eff1``eff5` and keeping the two holders,
gives 12.06 / 11.79 / 1 452 — identical to `--at 198`. The principled rule
reproduces the hand-picked one with nothing hand-picked.
***Disc-wide self-consistency.** For every bundle with a window, no element has
a keyframe strictly inside it, and the midpoint lies within it —
`tests/ui_settle_time_disc.rs`.
## 🔴 Reach: this does not apply to every bundle
Of the **1 758** composable bundles carrying two or more keyframe times:
| | count | share |
|---|---|---|
| settle window ≥ 30 units (0.5 s) | 524 | 30 % |
| settle window < 10 units | 731 | 42 % |
| mean window | 49 units | — |
The 42 % are mostly `loop*` animation fragments, which are **meant** to be in
motion and have no settled pose to find. **Check the window width before trusting
the midpoint.** A narrow window is the data saying "this bundle never settles",
not a settle time with a small error bar.
## What this corrects elsewhere in the corpus
* 🔴 [`ui-rotation-implemented.md`](ui-rotation-implemented.md) records the pose
scan as **"Flat. No minimum."** over t = 0…600. The cause is now known:
`ComposeOptions::at` was posing **leaves only**, so the scan moved the light
sweeps and never touched the top-level flashes. No `t` could have helped. `at`
now poses every element.
* 🔴 The same document blames the residual on our renderer **"not drawing
`ptlogo1` / `ptlogo2` at all (four elements)"**. That is **withdrawn**. Build 4
declares *six* ptlogo elements: indices 0 and 1 are kind `0x0` at (184,193) and
(137,308), alpha 255, and are **drawn**; indices 25 are kind `0x4` ghost
instances at (116,7) and (437,508), alpha 0, and are skipped deliberately.
Hiding element 0 makes the error **worse** by +5.20 whole-frame and +7.61 in the
band; element 1 by +7.47. They are drawn and correctly placed.
* ❔ The **10.92** baseline in that document's table is **not reproducible**.
`screen render --build 4 --black` against that capture gives 14.07 at the
pre-change tag and 14.07 now. Some element of that recipe was not recorded.
Conclusions resting on 10.92 — including the "1.7 % better" verdict on rotation
— should be treated as unverified until the recipe is recovered.
## What is not settled
❔ The remaining **12.06**. It is broad and level-like rather than localised,
which is consistent with the tone term that
[`title-residual-tone-vs-geometry.md`](title-residual-tone-vs-geometry.md)
measures. The arc band is no longer where the error lives.
## Reproducing
```bash
cargo run -q -p sylpheed-cli -- screen render --build 4 --black --at 198 \
"$SYLPHEED_DISC/dat/GP_TITLE.pak" /tmp/title.png
SYLPHEED_DISC=/disc cargo test -p sylpheed-formats --test ui_settle_time_disc
```