re: the port was still being told SE audio is undecodable -- it is not

A resolve-check on HANDOFF's own rows. Q8 read "SE audio is undecodable
from the disc -- no XACT container exists anywhere". menu-audio-cues.md
retracted exactly that ("### Retracting 'cannot be extracted'") and
locates three cues in Static.slb that decode to PCM: d-pad move 0x1ec0
(4 packets), (B) back 0x0ec0 (2), (A) confirm 0x5d6c0 (6), all mono
48 kHz. The retraction landed in docs/re/ and the page the port reads
kept the superseded text -- the fourth time in this corpus.

Writing the rule down has not worked, so there is a tool now.
handoff_lint.py flags every HANDOFF line making a strong negative claim
that links a doc containing retraction language. First run: found the Q8
row, plus one benign false positive (Q3 links a doc whose retraction is
about a sprite count, not about the tie-break -- checked, and HANDOFF
repeats none of the retracted figures). The lint also caught its own bug
first: it reported existing docs as missing because it joined a guessed
repo root, so it now resolves links relative to the file as markdown does.

Separately, EXTRAS's paint-order risk narrows twice more. Of its 15 tied
pairs only 2 overlap, and of those, ptloop01 x ptloop02 are loop*
animations compose skips by default -- so exactly ONE tie can be drawn:
ptframe3 x ptframe4, overlapping 102x132 px. Against live-extras.png that
contested region correlates +0.9622, better than the whole frame (+0.9440)
and inside the range of regions where order cannot matter (+0.8502 /
+0.9903). Consistent with our order, not proof: correlation cannot see a
swap between locally similar art.

15 -> 2 -> 1 -> consistent is now the whole paint-order risk on the five
screens, and HANDOFF says so.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:31:37 +00:00
parent 9ff9e8e633
commit 3eba09aae0
6 changed files with 138 additions and 5 deletions

View File

@@ -34,7 +34,7 @@ authored version can be deleted.
| Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 2× `NEW GAME`); ⬆⬇ one step, **wraps both ends**; ⬅➡ do nothing; Ⓑ returns to the parent **with focus restored**; Ⓑ on the main menu → title; Ⓑ on the title → nothing — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) |
| Q6 | boot sequence + what drives it | ✅ answered | sequence **measured** end to end; the driver is **code, not data** — four search spaces closed, so the port **authors** the sequence — [`boot-config-and-gamepart-registry.md`](../re/boot-config-and-gamepart-registry.md) |
| Q7 | transitions | ✅ answered | a **fade through black**, drawn by the screen's own last-painting `.prm` quad. Fade-in ramp is **decoded** from its keyframes; the ~0.4 s fade-out is **measured** (not in the file) — [`screen-transitions.md`](../re/screen-transitions.md) |
| Q8 | menu audio bindings | ✅ answered | cue vocabulary + bank **decoded**; event binding is a **name match** (the authors' own event names); SE audio is **undecodable from the disc** — no XACT container exists anywhere — [`menu-audio-cues.md`](../re/menu-audio-cues.md) |
| Q8 | menu audio bindings | ✅ answered | cue vocabulary + bank **decoded**; event binding is a **name match** (the authors' own event names). ✅ **You CAN have the SE audio** — ⚠️ an earlier version of this row said it was "undecodable from the disc"; that was **retracted** and the row was stale. Three cues are located in `Static.slb` and **decode to PCM**: d-pad move `0x1ec0` (4 packets), Ⓑ back `0x0ec0` (2), Ⓐ confirm `0x5d6c0` (6), all mono 48 kHz. The bank is a packed run of XMA waves with no delimiter, so a wave is only (offset, packet count) — and ⚠️ the file order is **not** cue-id order, so the index cannot be counted out — [`menu-audio-cues.md`](../re/menu-audio-cues.md) |
| Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE``ADV.wmv` (boot intro *and* attract are one asset), `MS00A``S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. ✅ **one Ⓐ skips a movie** (title at 57 s vs a 193 s baseline) — [`movie-binding.md`](../re/movie-binding.md) |
| Q10 | music-bank sub-wave roles (intro+loop?) | ✅ answered | **two stems of one performance, played together** — sample-synchronous, equal duration, 32/32 banks. **Concatenating is wrong.** Not a seamless loop either — [`structures/bgm-two-stems.md`](../re/structures/bgm-two-stems.md) |
| S1 | Ready Room go/no-go | ✅ **no-go** | it is 2D and enumerates fine (60 builds), but `GP_READY_ROOM.pak` holds **briefing/tactical-map** content, not the six-button Ready Room menu — [`ready-room-probe.md`](../re/ready-room-probe.md) |
@@ -267,10 +267,16 @@ authored version can be deleted.
in practice because it has a measured order.
Per screen: title **measured**, main menu **measured**, developer splash
**measured**, publisher splash derived but with **0 ties** (fully determined),
and **`EXTRAS` derived with 15 tied pairs of which only 2 overlap**. That pair
count is the whole risk — a tie-break by declaration index can be wrong by a
whole layer where it is wrong, so if `EXTRAS` ever looks off, those two pairs
are where to look.
and **`EXTRAS` derived with 15 tied pairs of which only 2 overlap**.
**That narrows again to ONE, and the capture is consistent with it.** Of the
two overlapping pairs, `ptloop01`×`ptloop02` are `loop*` animations that
`compose` skips by default, so their tie is unreachable. The remaining pair is
`ptframe3`×`ptframe4`, overlapping 102×132 px — and against `live-extras.png`
that contested region correlates **+0.9622**, *better* than the whole frame
(+0.9440) and inside the range of regions where order cannot matter (+0.8502 /
+0.9903). 🟡 Consistent with, not proof — correlation cannot see a swap between
locally similar art. **15 → 2 → 1 → consistent** is the whole paint-order risk
on your five screens.
[`structures/ui-paint-order-derived-check.md`](../re/structures/ui-paint-order-derived-check.md)
* **Menu order is geometric.** Buttons sorted top-to-bottom by resting Y. This is

View File

@@ -571,3 +571,17 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
returned **64** hits, proving the scan finds ordering fields when they exist.
Only then is the zero a finding. The control costs four extra lines and turns a
silence into a negative with reach.
* **A stale row on the handoff page is worse than a missing one.** The port was
told SE audio was "undecodable from the disc" while the linked doc had already
retracted that and located three cues decoding to PCM. The finding landed in
`docs/re/` and the page the port reads kept the superseded text — the fourth
time in this corpus. Writing the rule down has not worked, so there is now a
tool: `tools/re-capture/handoff_lint.py` flags every HANDOFF line that makes a
strong negative claim and links a doc containing retraction language. It found
this row, and one benign false positive, on its first run.
* **Narrow a risk before reporting it, then narrow it again.** "15 tied pairs on
`EXTRAS`" became 2 (only overlapping ties can paint differently), then 1 (one
of those two is a `loop*` animation the compositor skips), then "consistent
with the capture". Each step is a filter that costs one query and changes the
number by more than an order of magnitude in total. A raw count is almost never
the number a consumer needs.

View File

@@ -464,3 +464,8 @@ neighbourhood, not just the line.
header (exhaustive 0x000x7f, u8/u16/u32, both directions — 0 matches against
64 for the declaration-order control) all give `1,2,3,4,5`.
[`ui-paint-order-derived-check.md`](structures/ui-paint-order-derived-check.md)
* "SE audio is undecodable from the disc — no XACT container exists anywhere"
(as it stood on the **handoff page**) → **stale**: `menu-audio-cues.md` had
already retracted it and located three cues in `Static.slb` that decode to PCM.
The retraction never reached the row the port agent reads. Handoff row fixed;
`tools/re-capture/handoff_lint.py` now checks for this class.

View File

@@ -126,3 +126,44 @@ So Q3 resolves as: the layer key is ✅ **decoded** and orders 4 of the 5 measur
bundles exactly; the **tie-break within a key is ❔ undecodable**, and a consumer
must either use a measured order or accept declaration index as an arbitrary
stand-in.
---
## ✅ `EXTRAS`'s exposure narrows to ONE pair — and the capture is consistent with it
The 15 tied pairs on `EXTRAS` reduce twice.
**2 of the 15 overlap** (only an overlapping tie can paint differently):
```
[0] ptframe3.t32 x [1] ptframe4.t32 key 32848 overlap 102x132
[14] ptloop01.rat x [15] ptloop02.rat key 32784 overlap 400x180
```
**1 of those 2 can be drawn.** `ptloop01`/`ptloop02` are `loop*` sprite
animations, which `compose` skips unless `--animated` is passed, so their tie is
unreachable in a default render. The real exposure is `ptframe3` × `ptframe4`,
overlapping **102 × 132 px** at x 584686, y 318450.
### The capture is consistent with our order
Correlating our render against
[`live-extras.png`](../captures/title-builds/live-extras.png):
| region | correlation |
|---|---|
| **the contested overlap** (x 584686, y 318450) | **+0.9622** |
| `ptframe3` alone, outside the overlap | +0.8502 |
| `ptframe4` alone, outside the overlap | +0.9903 |
| whole frame | +0.9440 |
The contested region matches **better than the frame as a whole** and sits inside
the range of regions where order *cannot* matter. Were our tie-break wrong, that
region should correlate worse than its neighbours.
🟡 **Consistent with, not proof.** Correlation cannot see a swap if the two
frames happen to be locally similar. But it is a real check, and it removes the
"entirely unverified" character of the one tie the port can actually hit.
**So the chain is: 15 tied pairs → 2 overlapping → 1 drawable → consistent with
the capture.** That is the whole paint-order risk on the port's five screens.