sylpheed-port found the fifth member of our error family on their own side: their "visible" test counted any element with alpha > 0, which includes palogo_eff0. Verified from the disc rather than accepted -- entry 10 [0] palogo_eff0.prm 1 kf t=0 fade=0xff000000 scale=100x100 pos=(0,0) entry 11 same Alpha 255 over RGB 000000: full-screen opaque black, drawn from t=0 and showing nothing. So "any element drawn" reports these screens visible from t=0 while the frame is black -- "visible" read as "drawn". Worth having on its own: this verifies from the disc the premise behind `screen render --black`, which its own help states as "what the game composites over on a screen carrying its own background". On the splash builds that background is DECLARED, not assumed. METHOD gains their amendment, which is the sharpest formulation either of us reached this week: all five instances are a failure of a NOUN, not of a number. Extent, bounding box, duration, span, visible. The number was always correct FOR SOMETHING; what went missing was which thing. Every other check in that file tests whether a number is right, and not one tests whether it is a number of the thing you think. Also teaches fade_quads.py to address a PAK ENTRY directly (`e10`) rather than only a build ordinal. The splashes are entries 10/11 and are not screen builds, so no ordinal addresses them -- and writing `e10` states which index space is meant, which is the standing lesson of build-ordinal-vs-entry.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
217 lines
11 KiB
Markdown
217 lines
11 KiB
Markdown
# The boot's first ten seconds: publisher, then developer — and the movie has a logo card too
|
||
|
||
**Status:** ✅ `CONFIRMED`. The **order** is measured in three independent cold
|
||
boots and confirmed **by eye**, not only by a correlation. The **dwells** are
|
||
**decoded** — they are on the disc, and the running game reproduces them to
|
||
within the emulator's own frame pacing.
|
||
|
||
Raised by the port agent against `data/boot-timeline-2026-08-29.tsv`, whose
|
||
`label` column runs `splash_dev` *before* `splash_pub` — the opposite of what
|
||
`authored/flow.json` carries. If that ordering were real it would be a boot-order
|
||
bug in the port. **It is not real, and this page says why.**
|
||
|
||
## The order: publisher first
|
||
|
||

|
||
|
||
*Brightened ×6 — these frames have a surface mean of ≈5/255. Top-left and
|
||
top-right are the two splashes; the bottom row is what comes next and is NOT a
|
||
splash.*
|
||
|
||
| run | `SQUARE ENIX` (publisher) | `GAME ARTS`/`SETA`/`studio anima` (developer) |
|
||
|---|---|---|
|
||
| 1 | 3.046 → 7.343 s | 7.683 → 11.191 s |
|
||
| 2 | 1.180 → 5.784 s | 6.051 → 9.554 s |
|
||
| 3 | 1.192 → 5.562 s | 5.929 → 9.295 s |
|
||
|
||
Three cold boots, `t = 0` at process launch, a ~0.2–0.3 s black hold between
|
||
them. **Publisher first, every time**, and the top row of the contact sheet is
|
||
what the two segments actually show. `docs/game/navigation.md` §1 and
|
||
[`ui-title-build-map.md`](ui-title-build-map.md) stand.
|
||
|
||
## Why the committed TSV says otherwise — the probe attached late
|
||
|
||
`data/boot-timeline-2026-08-29.tsv` opens at `t = 0.692` with twelve
|
||
**byte-identical** rows: mean `5.642`, `splash_dev` `+0.8707`, `splash_pub`
|
||
`+0.0294`, to four decimals. Twelve identical samples over 1.27 s are one
|
||
observation of a held screen, not twelve.
|
||
|
||
Those exact numbers appear in my run 1 at **8.42 – 10.94 s** — same mean, same
|
||
two correlations, same four decimals. So that capture's `t = 0` is roughly
|
||
**7.7 s into the guest's boot**: the publisher splash had already been and gone
|
||
before the stream opened, and the developer splash was simply the first thing the
|
||
probe ever saw.
|
||
|
||
**The label column is right about what each frame is. It is wrong about what came
|
||
before the file starts.** Nothing in the TSV is retracted; its *reach* is.
|
||
|
||
## The trap that makes this worse: `ADV.wmv` opens with a SQUARE ENIX card
|
||
|
||
After the developer splash there is a black hold and then a screen that scores
|
||
**0.59 – 0.75** against `live-splash-publisher.png` — above the classifier's
|
||
threshold, so it is labelled `splash_pub` a second time. In all three runs:
|
||
|
||
| run | second "splash_pub" |
|
||
|---|---|
|
||
| 1 | 17.793 → 20.301 s |
|
||
| 2 | 16.930 → 20.664 s |
|
||
| 3 | 14.669 → 17.308 s |
|
||
|
||
The bottom row of the contact sheet is that screen. It is a **blurred, bloomed
|
||
SQUARE ENIX wordmark, lower in the frame** — the intro movie's own opening title
|
||
card, i.e. `ADV.wmv` ([`movie-binding.md`](movie-binding.md)) already playing.
|
||
The real splash's wordmark is sharp and centred; the movie's is soft and sits
|
||
below centre.
|
||
|
||
⚠️ **So `splash_pub` is not a safe label once the movie has started.** A boot
|
||
classifier keyed on `live-splash-publisher.png` will fire twice per boot. The
|
||
discriminators that do work: the real splash holds *perfectly still* (identical
|
||
frame statistics to four decimals for seconds at a time) and scores **0.93–0.94**;
|
||
the movie card drifts continuously and never exceeds **0.76**.
|
||
|
||
## The dwells are on the disc — do not author them
|
||
|
||
Both splash bundles declare their whole life. Read with the corrected keyframe
|
||
record layout ([`ui-keyframe-record-layout.md`](ui-keyframe-record-layout.md))
|
||
and Q1's `1 unit = 1/60 s`:
|
||
|
||
| | declared | visible span | measured (run 1 / 2 / 3) |
|
||
|---|---|---|---|
|
||
| publisher, `palogo_sqex.t32` | α `0@15 → 255@30 → 255@235 → 232@239 → 32@251 → 0@255` | `15 → 255` = 240 u = **4.000 s** | 4.297 / 4.604 / 4.370 s |
|
||
| developer, `palogo_gamearts.t32` (`seta`, `anima` identical) | α `0@15 → 255@30 → 255@190 → 232@194 → 32@206 → 0@210` | `15 → 210` = 195 u = **3.250 s** | 3.508 / 3.503 / 3.366 s |
|
||
|
||
Measured ÷ declared, over all six spans: **1.074, 1.151, 1.093, 1.079, 1.078,
|
||
1.036** — mean **1.085**. A 30 Hz timeline stretched by 8.5 % is the game
|
||
presenting at **27.6 fps**, which is the rate this corpus has measured
|
||
independently three times (27.6 on the boot splash, 28.3 and 28.8 on the idle
|
||
title — [`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md)).
|
||
|
||
✅ **Classified decoded.** The port reads **240 units** for the publisher and
|
||
**195 units** for the developer and authors nothing.
|
||
|
||
## 🔴 WITHDRAWN — "the game runs them ~8.5 % long". I measured one element's span and called it the screen's.
|
||
|
||
**Retracted the same day.** `sylpheed-port` said the port plays the **full group**
|
||
— 255 and 210 units — not my 240/195, and they are right for a reason sharper than
|
||
either of us first had. The `_eff` elements ramp **α 0 → 255 over t=0..15**, while
|
||
the main logo is still fully transparent there:
|
||
|
||
```
|
||
palogo_sqex.t32 0:a=0 15:a=0 30:a=255 235:a=255 239:a=232 251:a=32 255:a=0
|
||
palogo_sqex_eff.t32 0:a=0 15:a=255 30:a=212 45:a=0
|
||
```
|
||
|
||
So **the screen is visible from within t=0..15**, and its visible span is 0→255
|
||
and 0→210 — the full group. My "240 units visible" was **one element's** visible
|
||
span, computed while another element of the same build was already on screen.
|
||
|
||
Recomputed against the screen:
|
||
|
||
| | ratios | |
|
||
|---|---|---|
|
||
| publisher (255 u = 4.250 s) | 1.011, 1.083, 1.028 | |
|
||
| developer (210 u = 3.500 s) | 1.002, 1.001, **0.962** | one **below** unity |
|
||
| **mean** | **1.0145** | against 1.085 before |
|
||
|
||
A 1.5 % mean with a measurement below unity is not a clock running at 54 u/s.
|
||
**The systematic is gone and Q1 stands unqualified.**
|
||
|
||
⚠️ **And the consequence I drew was wrong too.** "A port playing 240 units at 60
|
||
shows the publisher splash 0.42 s less" — it plays 255, so the gap is 0.174 s, and
|
||
on the developer splash the port runs *longer* than my mean. There is no direction
|
||
to correct in, and nothing here supports moving `keyframe_units_per_second`.
|
||
|
||
🟡 **What survives, weakly:** against the full group the publisher still runs long
|
||
in all three boots (1.011, 1.083, 1.028) while the developer sits at unity. Three
|
||
boots per screen is thin and it is not a systematic.
|
||
|
||
### ✅ And the splash builds carry their own OPAQUE BLACK backdrop — verified
|
||
|
||
`sylpheed-port` found the fifth family member on their own side while confirming
|
||
the above: their "visible" test counted **any element with alpha > 0**, which
|
||
includes `palogo_eff0.prm`. Checked against the disc
|
||
(`tools/re-capture/fade_quads.py e10 e11`):
|
||
|
||
```
|
||
entry 10 [0] palogo_eff0.prm 1 kf t=0 fade=0xff000000 (alpha 255) scale=100x100 pos=(0,0)
|
||
entry 11 [0] palogo_eff0.prm 1 kf t=0 fade=0xff000000 (alpha 255) scale=100x100 pos=(0,0)
|
||
```
|
||
|
||
Alpha 255 over RGB `000000`: **full-screen opaque black, drawn from t=0 and
|
||
showing nothing.** So "any element drawn" reports these screens visible from t=0
|
||
while the frame is black — **"visible" read as "drawn"**, the fifth member.
|
||
|
||
📌 **Worth having on its own:** this verifies, from the disc, the premise behind
|
||
`screen render --black` — *"what the game composites over on a screen carrying its
|
||
own background, and so what a framebuffer capture must be compared against"*. On
|
||
the splash builds that background is **declared**, not assumed.
|
||
|
||
📌 **This is the fourth instance of one family** — pivot anchor read as drawn
|
||
extent, centre track as bounding box, cycle length as motion duration, and now
|
||
**one element's visible span read as the screen's**. Every one is a number used as
|
||
though it described the thing beside it.
|
||
|
||
`sylpheed-port` reported their `verify-dwell` at 4.28 s / 3.58 s and called it
|
||
agreement with these boots. Checking the arithmetic rather than the impression:
|
||
|
||
| | declared | at Q1's 60 u/s | measured (3 cold boots) | ratio |
|
||
|---|---|---|---|---|
|
||
| publisher | 240 u | **4.000 s** | 4.297 / 4.604 / 4.370, mean **4.424** | **1.106** |
|
||
| developer | 195 u | **3.250 s** | 3.508 / 3.503 / 3.366, mean **3.459** | **1.064** |
|
||
|
||
**All six ratios exceed 1** — 1.074, 1.151, 1.093, 1.079, 1.078, 1.036 — mean
|
||
**1.085**. The implied unit rate is **54.3** and **56.4 units/s**, and the port's
|
||
own two numbers imply **56.1** and **54.5**. Four estimates, none at 60.
|
||
|
||
⚠️ **The obvious explanation does not work.** A detector triggering early and
|
||
late would lengthen the interval — but the declared span *is* `15 → 255`, and
|
||
outside it the alpha is **0**, so there is nothing on screen to trigger on. An
|
||
8.5 % overshoot is 20 units, ten rendered frames, which a threshold cannot
|
||
manufacture from a blank screen.
|
||
|
||
🟡 **So this is a real systematic and its cause is not established.** Either the
|
||
title's timeline advances at ~55 units/s rather than Q1's 60, or the boot inserts
|
||
something between the declared span and what a capture sees. ⚠️ It is **not** the
|
||
same discrepancy as the sweep leaf's (which runs ~50 % slow, not 8.5 %), and I am
|
||
recording it as an open qualification on Q1 rather than a correction to it — three
|
||
boots per screen is thin, and Q1 was measured on a different quantity.
|
||
|
||
📌 **For the port:** these dwells are still *decoded* and still should not be
|
||
authored — but a port that plays 240 units at exactly 60 u/s will show the
|
||
publisher splash for **0.42 s less** than the game does.
|
||
|
||
⚠️ **Reach of the ±:** a correlation crossing a threshold during a fade is not a
|
||
sharp edge, so an individual span is good to roughly ±0.2 s. The developer's
|
||
first two runs agree to **5 ms**, which is the instrument at its best; run 2's
|
||
publisher span (4.604 s) is the outlier and its onset at 1.180 s is early enough
|
||
to be a stream still filling. The *order* does not depend on any of this.
|
||
|
||
## Method
|
||
|
||
`tools/re-capture/boot_timeline_probe.py`, whose `--control` was run first and
|
||
passed **11/11** on the content classifier and **4/4** on the plate detector,
|
||
including `live-splash-publisher.png → splash_pub` and
|
||
`live-splash-developer.png → splash_dev` with each rejecting the other at
|
||
**0.035**. So a 0.87 reading against the developer reference is a real match and
|
||
not an artefact of two dark images: the two references are both dark and they do
|
||
not correlate with each other.
|
||
|
||
Both splash references are themselves committed captures whose identity is
|
||
independent of any correlation — `live-splash-publisher.png` was matched to
|
||
`GP_TITLE` entry 10, whose elements are literally named `palogo_sqex`, and
|
||
`live-splash-developer.png` to entry 11, `palogo_gamearts` / `palogo_seta` /
|
||
`palogo_anima`.
|
||
|
||
Boots were cold: `/dev/shm/xenia_*` cleared, no pad input at any point.
|
||
|
||
## What this does not say
|
||
|
||
* Nothing here is about the **movie's** length or the attract loop; the runs were
|
||
cut at 26–45 s. That half is in `data/boot-timeline-2026-08-29.tsv`, whose
|
||
*intervals* are unaffected by the attach offset even though its absolute `t` is.
|
||
* The 0.2–0.3 s black hold between the two splashes was not separately timed
|
||
against the declared `palogo_eff0.prm` backdrop; it is consistent with the
|
||
0.14–0.30 s black hold already measured between screens
|
||
([`title-plate-delay-measured.md`](title-plate-delay-measured.md)) but that is
|
||
a consistency note, not a measurement of this particular gap.
|