Files
Sylpheed/docs/re/structures/build-ordinal-vs-entry.md
sylph-decoder c364cde476 re: sweep the disc for the ordinal foot-gun -- GP_TITLE was the mildest case
Last iteration I retracted three claims because `--build 10/11` on GP_TITLE are
entries 12/15, and named the untested remainder in my own report: how much else
in the corpus used a build ordinal as an entry index. This is that sweep.

`screen --build N` indexes a predicate-filtered list, so every rejected entry
shifts every later ordinal. Disc-wide: 21 of 24 build-bearing archives diverge,
18 of them at ordinal 0 -- `--build 0` is entry 108 in each GP_MAIN_GAME_*2D,
24/26 in GP_HANGAR_ARSENAL/GP_READY_ROOM. GP_TITLE is the ONLY archive whose
first ten ordinals are the identity, which is the sole reason 207 of the
corpus's 226 build citations are safe. Second foot-gun: `--all` swaps the
predicate and renumbers 18 archives, so `--build N` and `--build N --all` are
not the same object.

The instrument failed its control first. A version using parse_build as the
predicate reported GP_TITLE as 16 builds, ordinal == entry throughout -- it
would have certified the exact bug it was built to find. The shipped version
uses the same predicates screen_builds() uses and reproduces `screen list` on
GP_TITLE exactly.

Audited all 226 citations. One real defect: a five-row table in
ui-keyframe-time-unit.md headed "declared element (build 11)" spans builds 10
and 11 -- palogo_sqex is in 10. All five placements re-verified and correct, so
the linear-ramp measurement is untouched; only the label was wrong. Fixed with a
per-row bundle column. GP_DIALOG --build 0 and GP_DEBRIEFING_PILOTLOG --build 10
re-run and reproduce.

Refutation attempted: sylpheed-port's corrected mid-ramp test rests on
ptlogo_all_eff holding a=127 from t=112 to t=246. Their quote is exact and it is
a plateau. The refutation fails; their correction stands.

METHOD already carried the rule I broke, and ui-splash-addressing already said
the splashes need --all. The failure was not missing knowledge -- it was
addressing a bundle by index without grepping for the index first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-30 13:20:41 +00:00

7.2 KiB
Raw Blame History

--build N is an ordinal into a filtered list — and on 21 of 24 archives it is not the entry

Status: decoded, disc-wide, instrument controlled against the CLI's own output. The object decoded is the addressing, not a file field: how sylpheed-cli screen --build N resolves, and where that number stops agreeing with the pak entry index a reader will assume it means.

Why this was swept

Last iteration I rendered --build 10 and --build 11 of GP_TITLE believing they were the two splash screens, wrote three claims on the output, and every downstream number validated. They are entries 12 and 15 — the loading screens. I retracted it, and named the untested remainder in my own report: "how much else in the corpus used --build as an entry index — not swept." This is that sweep.

The mechanism

crates/sylpheed-cli/src/main.rs:394 builds the list:

fn screen_builds(pak: &Path, all: bool) -> Result<Vec<(usize, Vec<u8>)>> {
    for (i, e) in ar.entries().iter().enumerate() {
        let keep = if all { ui_layout::is_composable(&bytes) }
                   else    { ui_layout::is_build(&bytes) };
        if keep { out.push((i, bytes)); }          // (entry, bytes)
    }
}

--build N indexes out, so N counts only entries that passed a predicate. Every entry the predicate rejects shifts every later ordinal down by one.

🔴 The result: GP_TITLE is the mildest case on the disc

data/ordinal-entry-map.txt — all 24 archives holding builds:

  • 21 of 24 diverge. Only GP_MOVIE_THEATER, GP_SYSTEM and GP_TUTORIAL have ordinal == entry throughout.
  • 18 of the 21 diverge at ordinal 0--build 0 is not entry 0. The worst are the six GP_MAIN_GAME_*2D paks, where [0] is entry 108, and GP_HANGAR_ARSENAL / GP_READY_ROOM, where [0] is entry 24 / 26.
  • GP_TITLE is the only archive whose first ten ordinals happen to be the identity. It diverges at ordinal 10 and nowhere earlier.

So the corpus was not lucky in general — it was lucky in the one archive almost all of it is about, and unlucky in exactly the two indices I used.

⚠️ Second foot-gun: --all renumbers, on 18 archives

--all swaps the predicate, which changes the list, which changes the ordinals. --build N and --build N --all are not the same object on 18 of 24 archives — including GP_TITLE, where --build 10 is entry 12 but --build 10 --all is entry 10. Any citation of a build index that does not also record whether --all was passed is under-specified.

Audit of every build citation in docs/

226 citations of a build index across docs/ (this file excluded). The 207 that name an ordinal 09 of GP_TITLE are safe by the accident above. The 19 that name an ordinal ≥10, or a non-GP_TITLE archive, are the ones that can be wrong, so each was opened and checked rather than counted:

# citations verdict
6 carry --all, where ordinals 10/11 are entries 10/11 correct
4 inside last iteration's retraction, already marked void n/a
2 GP_TITLE --build 10 bare — ui-title-build-map.md:85 correct: it names what comes back, the loading screen pgloading_str.t32
2 GP_DIALOG --build 0 ([0] is entry 2) re-run, reproduces
2 GP_DEBRIEFING_PILOTLOG build 10 ([10] is entry 131) re-run, reproduces
2 prose about an unfinished sweep / the renumbering warning itself n/a
1 ui-keyframe-time-unit.md:59 🔴 wrong, and fixed

The two re-runs

Neither claim asserted an entry number — both cite the output of a command, so a reader running it gets the same object the author had. Confirmed by running them, not by arguing it:

$ sylpheed-cli screen info --build 0 --geometry /disc/dat/GP_DIALOG.pak
4  pceff03.t32  0: a=0 r=90  8: a=128 r=30  12: a=192 r=10  14: a=224 r=3  16: a=255
5  pceff04.t32  0: a=0 r=90  8: a=128 r=30  12: a=192 r=10  14: a=224 r=3  16: a=255

$ sylpheed-cli screen info --build 10 --geometry /disc/dat/GP_DEBRIEFING_PILOTLOG.pak
5  pjeff24a.t32  382x140  0: 335,49 210%,210% a=53 r=90        (one keyframe)

Both stand unchanged.

🔴 The one real defect the sweep found

ui-keyframe-time-unit.md headed a five-row table "declared element (build 11)". Its first row is palogo_sqex.t32 — and --all --build 11 does not contain it:

--all --build 10   palogo_sqex, palogo_sqex_eff
--all --build 11   palogo_gamearts{,_eff}, palogo_seta{,_eff}, palogo_anima{,_eff}

The rows span two bundles. All five placements re-verified and are correct — palogo_sqex.t32 666×68 @ (309,330) in build 10, palogo_gamearts_eff.t32 521×91 @ (379,154) in build 11 — so the measurement the table supports (the ramp is linear) is untouched. Only the label was wrong. Fixed: the table now carries a per-row bundle column.

That is the shape worth remembering: the index error did not corrupt the numbers, it corrupted the sentence around them, and the numbers kept validating.

⚠️ For the port: this is an addressing hazard, not a decoding one

If you address bundles by pak entry index — which ui-splash-addressing.md recommends for the splashes — and cross-reference a doc that says "build 6", those are different objects on 21 archives. When quoting an index, say which kind it is. Our docs now say ordinal or entry.

Refutation attempted — sylpheed-port's corrected mid-ramp test — survives

The port withdrew their own title_jp "separating case" this iteration, on the grounds that ptlogo_all_eff holds a=127 from t=112 to t=246 rather than ramping through it, so their old 0 < alpha < 255 test had counted a steady semi-transparent glow as a transition. Their whole correction — and the 5/5 result they say survives it — rests on the keyframes of that one element, which is disc data and therefore mine to check. Quoted against the disc:

$ sylpheed-cli screen info --build 7 --geometry /disc/dat/GP_TITLE.pak
29  ptlogo_all_eff.t32  538x255  0: a=0  76: a=0  112: a=127  246: a=127  258: a=0
                                  (kind 0x3000, 200%,200%, position constant)

Their quote [0:a0 76:a0 112:a127 246:a127 258:a0] is exact, and a=127 is held flat across 134 units with nothing else moving. It is a plateau. The refutation fails and their correction stands — including the part that costs them, since it removes the one case that would have separated their hypothesis from mine.

⚠️ Note what this does not establish: that a=127 is a glow. That reading is theirs and rests on kind 0x3000 and the 200 % scale, neither of which I have tested against the running game. What I checked is the keyframes.

What this does not settle

  • Whether anything outside docs/ — scripts under tools/, committed test fixtures — hard-codes a build ordinal for a diverging archive. I swept the prose, not the code.
  • The three identity archives are identity today. Nothing enforces it; a change to is_build moves every ordinal on the disc. This is a property of a predicate, not of the format.