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.
7.4 KiB
✅ Does the derived paint order reproduce the measured ones? Mostly — and the gap is bounded
Status: ✅ checked, with numbers. compose uses a paint order measured
from the running game for the builds that have one and falls back to
derived_paint_order — a sort on each sprite's layer key — everywhere else. The
code's doc comment claimed the derived order "reproduces both measured orders up
to ties". That was a hedge with no measurement behind it, and it was stale:
there are three measured orders now, not two.
Tool: cargo run -p sylpheed-formats --example paint_order_audit -- dat/GP_TITLE.pak.
Output committed at data/paint-order-audit.txt.
The claim holds, and the exception is all ties
| build | derived == measured | inverted pairs | of which same-key ties |
|---|---|---|---|
| main menu (entries 5, 8) | YES | 0 | — |
| developer splash (11, 14) | YES | 0 | — |
| title (entry 4) | NO | 8 | 8 — every one |
Four of the five measured bundles reproduce exactly. The title's eight disagreements are all between elements that share a layer key, which the sort cannot order and breaks by declaration index.
⚠️ Two of those ties are total occlusions
The tied family is the five ptlogo_back2eff glows, all key 32899:
measured paints: 14, 15, 18, 16, 17
derived paints: 14, 15, 16, 17, 18
Two pairs flip, and they are not near-misses:
| pair | overlap | |
|---|---|---|
back2eff5 vs back2eff3 |
82 824 px² | 100 % of the smaller |
back2eff5 vs back2eff4 |
152 047 px² | 100 % of the smaller |
back2eff5 is 1133×280 and fully contains both. Derived paints it on top of
two glows it completely covers; the game paints it underneath. So a tie-break by
declaration index is not cosmetic — where it is wrong, it can be wrong by a whole
layer. ✅ The title is unaffected in practice, because it has a measured order.
✅ The port's actual exposure is two element pairs
Per build, counting tied pairs and how many of them overlap (only those can paint visibly differently):
| entry | the port's screen | order used | tied pairs | overlapping |
|---|---|---|---|---|
| 4 | title | measured | — | — |
| 5 | main menu | measured (derived agrees exactly) | 0 | 0 |
| 6 | EXTRAS |
derived | 15 | 2 |
| 10 | publisher splash | derived | 0 | 0 |
| 11 | developer splash | measured | — | — |
So of the five screens, one rests on an unverified derived order, and its risk is two overlapping tied pairs — not the 15 the raw tie count suggests. The publisher splash's derived order is fully determined (no ties at all).
🟡 For completeness, outside the port's set: entry 7 (the Japanese title) is the worst on the disc at 37 tied pairs, 16 overlapping.
Reach
- This checks the derived order against the orders measured from the running game; it is not an independent derivation of what the game does. Where no measured order exists, agreement cannot be checked at all — only the tie exposure can, which is what the table above reports.
- Overlap uses
pivot × 2as the element's size (documented as the sprite's own dimensions for a.t32) at its resting placement, so scaled or rotated elements are approximated.
❔ The tie-break is undecodable from the bundle — searched, with reach
The audit above leaves one question: the layer key orders elements, but what orders elements that share a key? On the title that tie-break decides two total occlusions, so it is not academic.
The game paints the five tied ptlogo_back2eff glows in the order
eff1, eff2, eff5, eff3, eff4. Three static structures were searched for
anything that reproduces it.
1. The declaration table. Entries 14–18 are byte-identical apart from the pivot, which is just half the sprite's own size:
14 ptlogo_back2eff1 00000000 ffffffff ffffffff 00000000 ffffffff 0000004e 0000003c 00000000
15 ptlogo_back2eff2 00000000 ffffffff ffffffff 00000000 ffffffff 00000074 0000005b 00000000
16 ptlogo_back2eff3 00000000 ffffffff ffffffff 00000000 ffffffff 000000aa 0000005b 00000000
17 ptlogo_back2eff4 00000000 ffffffff ffffffff 00000000 ffffffff 00000149 0000005b 00000000
18 ptlogo_back2eff5 00000000 ffffffff ffffffff 00000000 ffffffff 000001fb 0000007e 00000000
2. The T8aD headers. All five carry identical +0x04 (0x8832) and
identical +0x08/+0x0A (32899, the layer key itself). They differ only in
position and tile count. Searched exhaustively: every offset 0x00–0x7f, at
u8, u16 and u32 width, sorted both ascending and descending —
| fields sorting to the measured order | 0 |
| fields sorting to the declaration order (control) | 64 |
The control matters: 64 fields can be found that reproduce a known ordering, so the scan is capable of finding an ordering field when one exists. It finds none for the order the game uses.
3. The RATC child order — the bundle's second element list, which is
genuinely a different permutation from the declaration table on other screens.
For this family it reads eff1, eff2, eff3, eff4, eff5: declaration order again.
The answer
❔ Undecodable from the bundle, with reach. All three static orderings give
eff1…eff5; the game gives eff1, eff2, eff5, eff3, eff4. That is consistent
with what ui-screen-runtime already concluded from the
other direction — the game builds a reordered child list at load time and
paints that.
So Q3 resolves as: the layer key is ✅ decoded and orders 4 of the 5 measured 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 584–686, y 318–450.
The capture is consistent with our order
Correlating our render against
live-extras.png:
| region | correlation |
|---|---|
| the contested overlap (x 584–686, y 318–450) | +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.