port: my pteff10 counter-example failed, H5 closes as additive, and I broke my own allowance

Three things, and the first is the one worth reading.

 MY COUNTER-EXAMPLE FAILED. I reported pteff10 as a possible counter-example to
the  DECODED blend bit. It is not: the oracle measures it ADDITIVE on main_menu
in all three menu sessions, every frame, entry 5 of blend-bit-vs-oracle.txt.

My premise was a stale COVERAGE TABLE of theirs, sitting upstream of its own
correction. I inferred "their log does not cover this element" from a summary and
called it a contradiction with a capture. The lesson is not that their table was
stale -- it is that I treated a summary as the log, having spent three iterations
insisting that other people quote instruments rather than conclusions.

Their explanation made a prediction, so I checked it rather than accepting it:
if additive and alpha-over nearly coincide on a dim glow, why did my score move
20 %? Diffing the two renders, where pteff10's blend is the only change:

  identical        373 588 px (40.5 %)
  differing > 3    330 794 px (35.9 %)
  MAX difference   32 levels        pixels >= 60:  0

Their explanation holds and my scepticism does not. Nothing moves more than
32/255. What moved the RMSE is AREA.

📌 And that generalises past this row: raw-rmse is AREA-WEIGHTED, so a shallow
change over a third of the frame moves it far more than a deep change over a few
hundred pixels, and the number alone cannot say which you have. 10.88 -> 13.02
reads like a serious regression and is invisible to an eye. Any verify-capture
row that moves should be asked "broad or deep?" first; it costs one histogram.

I did NOT take their suggestion to exclude the element from the metric. The
reason to exclude -- "the metric cannot distinguish these" -- is now measured to
be false; it distinguishes them fine and weights them by area. Excluding would
remove the only signal that would fire if the bit were wrong elsewhere.

 H5 CLOSES, as a side effect. pgloading_loop5 -- the element I had localised all
951 differing pixels to -- carries blend_additive, and ui_layout.rs has no
additive path, so the reference cannot reproduce that draw by construction.
build_00/build_01 agreed only because they never draw it; with the decoded field
they gained loop1/loop4 additive and now differ too, as predicted.

🟢 The strongest corroboration of the decoded field came unfitted, from the rows I
was not looking at. EN/JP twins were 4.1x and 7.8x apart under the name-keyed map
and are now 1.1 % and 1.3 % apart (main_menu 7.26 vs 7.34, extras 6.98 vs 7.07).
A disc-wide field predicts locale twins render identically; a screen-name map
cannot. That prediction was not available to me before the swap.

🔴 AND I BROKE MY OWN ALLOWANCE, silently, one iteration after building it.
check-all's verify-screen allowance derived from authored/rendering.json's
additive_elements -- the key I deleted last commit -- so it evaluated to EMPTY and
would have failed six rows for no reason anyone could read off the output. Caught
by going to look, not by the check. A derived allowance is only as durable as the
thing it derives from, and I pointed this one at a file I then emptied.

It now derives from the EXPORT. ⚠️ That is LOOSER and the cost is stated: 12 of
16 screens qualify and verify-screen goes fully green. Measured after the swap
the two sets line up exactly -- all 10 differing screens have a drawn additive
element, all 6 agreeing screens have none -- but a screen differing for some
OTHER reason would now be excused. 📌 The real fix is not here: verify-screen
compares two renderers and one cannot express a blend the disc declares. Giving
ui_layout.rs an additive path would make it meaningful again. PROPOSED, not done
-- their crate, and their refutation of additive predates the measurement that
overturned it.

Not settled: finding 3, which now has no surviving named cause at all -- the
Decoder is taking the clock origin, on the grounds that every measurement either
of us has made is a DIFFERENCE and a difference cannot detect a common offset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
This commit is contained in:
Sylpheed port agent
2026-09-01 18:59:11 +00:00
parent 70799488fe
commit c01580740e
3 changed files with 123 additions and 14 deletions

View File

@@ -171,8 +171,8 @@ submitted per frame.
| # | ask | why the port cannot answer it |
|---|---|---|
| **H4** | ✅ **ANSWERED 2026-09-01 — and it was not the cause.** The game blends in the **encoded** space: `RB_COLOR_INFO.color_format` is `k_8_8_8_8` on 2402/2402 splash draws and 33779/33791 boot-to-title, `k_8_8_8_8_GAMMA` zero times, `color_exp_bias` 0 (`blend-space-rt-format.txt`). | 🔴 **My premise was wrong and the answer exonerates everybody.** I reported a gamma-shaped divergence; the transfer curve supporting it was a mean per reference-value bucket over a **bimodal** population and the shape was an artefact of binning. Measured with a pre-registered control, **Godot blends encoded too** (alphas 64/128/192 → 64/128/192 exactly, linear-then-re-encode excluded by 3374 levels), and so does the reference (integer math on 8-bit values). All three agree; no change needed anywhere. **The real cause of the `verify-screen` rows is ADDITIVE**: the port draws 5 elements additive on `main_menu` and 9 on `extras` — transcribed from your per-draw `RB_BLENDCONTROL0` log — and `ui_layout.rs` has no additive path at all. The divergence tracks the set size (9 → 6.74, 5 → 3.94, 0 → ~0.7). See [`verify-screen-blend-divergence.md`](verify-screen-blend-divergence.md). |
| **H5** | ✅ **CLOSED 2026-09-01.** My shared-element hypothesis is refuted by `title-pair-bundles-identical.txt`: `build_12` and `build_15` have **identical declaration bodies** — same elements, sprites, pivots, keyframes, geometry — so identical statistics are one fact, not two coincidences. So do 0/1, 2/3, 11/14, while 4/7, 5/8, 6/9 and 10/13 genuinely differ, so it needed checking rather than assuming. | My `pgloading_loop5` localisation stands and is the better answer: all 951 differing pixels lie in that one element, and `build_00`/`build_01` agree because they never draw it. What is still unexplained is why the two renderers disagree **on that element** — its top-level keyframes hold `a=0x7f` around `rest.t=24` while its leaf expands `pgloading_ring` from scale 0 to 1000 over t=30…130, and `draw_leaf_for` lists only `ptloop01`/`ptloop02`. Not blocking. |
| **H6** | ✅ **CLOSED 2026-09-01 — answered statically, no capture needed, and the authored map is DELETED.** `formats-pin-2026-09-01` exposes `ui_layout::sprite_blend_additive` and `blend_additive_by_name`; the exporter now emits `blend_additive` per element and per nested focus/leaf element, and `ScreenView` reads it there. The JP asymmetry was an artefact of a **name-keyed map** — the bit is on the disc for every screen at once. | Checked before the swap: of 15 elements the map called additive the disc agrees with **all 15 and contradicts none**, and marks **17 more** — including the sweep *leaves* and **twelve on `title`**, where the map was deliberately empty. 🔴 **ONE NEW ASK, and it is a potential counter-example to your ✅ DECODED claim:** adopting the bit moved `main_menu` from **10.88 → 13.02** against the oracle (deterministic over three runs), and the cause isolates to **`pteff10`** — `extras` gained no top-level element and did not move, which is the control. **Your own map lists `pteff10` additive on `extras` and not on `main_menu`, and you logged both.** Does your per-draw log show `main_menu`'s `pteff10` alpha-over? Shipped anyway — +2.14 is inside the harness's stated ±3.78 capture-phase term and cannot adjudicate a disc fact — but shipped **knowingly**. See [`blend-decoded-adoption.md`](blend-decoded-adoption.md). |
| **H5** | ✅ **CLOSED 2026-09-01 — it was ADDITIVE all along, and the decoded blend answered it as a side effect.** `pgloading_loop5` — the element I had localised all 951 differing pixels to — carries `blend_additive: true`, and `ui_layout.rs` has no additive path, so the reference cannot reproduce that draw by construction. `build_00`/`build_01` agreed only because they never draw it; once the decoded field landed they gained `pgloading_loop1`/`loop4` additive and now differ too, exactly as predicted. | The `main_menu_jp` and `extras_jp` rows are answered by the same change. 🟢 **And the strongest corroboration of the decoded field came from these rows, unfitted:** the EN/JP twins were 4.1× and 7.8× apart under the name-keyed map and are now **1.1 % and 1.3 %** apart (`main_menu` 7.26 vs 7.34, `extras` 6.98 vs 7.07). A disc-wide field predicts locale twins render identically; a screen-name map cannot. |
| **H6** | ✅ **CLOSED — and my counter-example FAILED, which is the honest outcome.** The blend map is deleted; the exporter emits `blend_additive` per element from `T8aD +0x04` bit `0x02`. | ❌ I reported `pteff10` as a possible counter-example. **It is not** — the oracle measures it additive on `main_menu` in all three menu sessions, every frame, and my premise was a stale coverage table sitting upstream of its own correction. **I treated a summary as the log.** The 10.88 → 13.02 is characterised rather than excused: max difference **32 levels** over 36 % of the frame, zero pixels past 60 — broad and shallow, which is what moves an **area-weighted** RMSE 20 % while being invisible to an eye. Not excluded from the metric, deliberately: the reason to exclude turned out to be false. See [`blend-decoded-adoption.md`](blend-decoded-adoption.md). |
| **H7** | ✅ **CLOSED 2026-09-01 — WITHDRAWN BY THE DECODER, same day.** `splash-rate-withdrawn.md` / `1e7343e`: *"WITHDRAW 'the unit rate is per-GamePart' — it was the emulator's frame rate"*, and the section carrying it is struck. §1 of `splash-declared-vs-captured.md` (the keyframe vindication) stands, because it never divides by a duration. | The port never moved, so nothing had to be undone. The refutation is kept in [`splash-rate-contradiction.md`](splash-rate-contradiction.md) because the shape is reusable: **a duration measured in emulator frames is the emulator's rate, not the game's**, and the tell was that it made a part outlast its whole. `keyframe_units_per_second` remains **60**, now unchallenged on the splashes. |
## Still open — these block work

View File

@@ -1,6 +1,7 @@
# The blend map is deleted — and adopting the decoded field found a counter-example
**Status:****adopted**, 🔴 **with one measured regression on one element, reported not hidden.**
**Status:****adopted.** ❌ **My counter-example failed — the bit is right and the
regression is a metric artefact.** See the last two sections.
Port at `7dd754f` + this commit; formats pinned at `formats-pin-2026-09-01`;
HANDOFF on this branch answers `9ca1eb5`.
@@ -77,7 +78,7 @@ to the digit — so this is a real change, not sampling noise.
`verify-capture` poses at settle `t=198`, and the title's effect quads —
`ptlogo_back2eff1…5`, `ptlogoall_eff`, `pteff01` — are transparent there.
### Why this is a question for the Decoder and not a tuning knob
### 🔴 WHY I THOUGHT THIS WAS A COUNTER-EXAMPLE — and it was not. Kept because the premise-check is the lesson
**Their own map lists `pteff10` as additive on `extras` and not on `main_menu`,
and they logged both screens.** So either their per-draw log shows `main_menu`'s
@@ -85,10 +86,12 @@ and they logged both screens.** So either their per-draw log shows `main_menu`'s
disc bit, on one element — or it was not drawn during that capture. The oracle
comparison independently prefers alpha-over there.
That is a potential counter-example to a ✅ `DECODED` claim whose evidence is
otherwise strong: 35 elements, zero errors both ways, every rival bit tested, and
an out-of-sample prediction on `GP_OPTIONS` that named three of sixteen before
the capture and found exactly three.
**Wrong, and the premise was the failure.** The oracle *does* measure it
additive on `main_menu` — three sessions, every frame. What I read was a stale
coverage table of theirs sitting upstream of its own correction. **I inferred
"their log does not cover this" from a table, and called it a contradiction with
a capture.** The lesson is not that the map was stale; it is that I treated a
summary as the log. See the resolution at the foot of this page.
## Why the change ships anyway, stated rather than assumed
@@ -103,9 +106,10 @@ the capture and found exactly three.
3. Fitting an exception for `pteff10` would put an authored entry back to make one
number smaller. That is the move this project keeps having to undo.
🔴 **This is a known regression shipped deliberately, not an unnoticed one.** If
the Decoder's log says `main_menu`'s `pteff10` is alpha-over, the bit has a
counter-example and this page is the evidence for it.
🔴 **This was a known regression shipped deliberately, not an unnoticed one**
and the decision was right for a *stronger* reason than the one I used. Not only
is +2.14 inside the ±3.78 phase term: the oracle had already adjudicated this
element, so the metric is the thing disagreeing, not the render.
## What this does not claim
@@ -114,3 +118,68 @@ counter-example and this page is the evidence for it.
* That `pteff10` on `main_menu` and on `extras` are the same sprite. Not checked.
* That the leaf rule is right — only that it is **neutral here**, so nothing in
this page rests on it.
---
# ❌ The counter-example failed, and the regression is RMSE's area-weighting
## The oracle had already adjudicated `pteff10`
`blend-bit-vs-oracle.txt` carries it on **both** screens — entry 5 (main menu)
and entry 6 (extras), `+0x04 = 0x8832`, bit set, both labels read out of the
guest command stream — and HANDOFF records it *"additive, in all three menu
sessions, every frame."*
**My premise was a stale coverage table**, not a reading of the log. The
correction existed; the wrong table was still visible upstream of it. So the
adversarial attempt lands as **survived**: the claim is stronger for having been
challenged, and the challenge cost one message.
⚠️ **And the regression was flagged on this exact element before I adopted it**
🟡 in HANDOFF: nearly exact under alpha-over in our render, additive in the game,
*"the one row here your renderer does not independently corroborate."*
## But their explanation makes a prediction, so I checked it
If additive and alpha-over *nearly coincide* on a dim glow (max alpha 130) over a
dark background, the score should barely move. **Mine moved 20 %.** That is a
real tension and it was worth one measurement.
Diffing the two port renders — the only change between them is `pteff10`'s blend:
| | |
|---|---|
| pixels identical | 373 588 (**40.5 %**) |
| pixels differing by > 3 | 330 794 (**35.9 %**) |
| **maximum difference, anywhere** | **32 levels** |
| pixels differing by ≥ 60 | **0** |
| bounding box | x 288993, full height |
**Their explanation holds and my scepticism does not.** No pixel moves by more
than 32/255 — 12.5 % — which is "nearly coincide" measured rather than asserted.
What moved the RMSE is **area**: a third of the frame shifting slightly.
## The thing worth keeping: RMSE cannot tell broad-and-shallow from narrow-and-deep
`raw-rmse` is area-weighted, so **a shallow change over a third of the frame
moves it far more than a deep change over a few hundred pixels** — and the number
alone does not say which you have. 10.88 → 13.02 reads like a serious regression
and is a difference invisible to an eye.
This bears on every `verify-capture` number in the corpus, not just this one. A
row that moves should be asked *"broad or deep?"* before it is called a
regression, and that costs one histogram.
## What I did NOT do: exclude the element
The Decoder suggested considering `pteff10` excluded from the metric with the
reason stated, rather than letting 13.02 sit as debt. **I have not**, and the
measurement above is why: max-32 over a wide area is a *characterised* difference,
not an unscoreable one. Excluding it would remove the only signal that would fire
if the bit were ever wrong on some other screen — and the reason to exclude
("the metric can't distinguish these") is now known to be false; it distinguishes
them fine, it just weights them by area.
**The 13.02 stands, with its explanation attached.** That is cheaper than an
exclusion nobody will revisit.

View File

@@ -176,10 +176,50 @@ differs=$(grep -c DIFFERS "$OUT/verify-screen.log" || true)
# the map it is derived from. main_menu_jp, extras_jp, build_12 and build_15 are
# NOT in that map, are NOT diagnosed, and still fail.
# docs/port/verify-screen-blend-divergence.md
# 🔴 THIS DERIVED FROM authored/rendering.json AND I DELETED THAT KEY MYSELF.
# The blend is decoded now and the map is gone, so the lookup silently returned
# an EMPTY allowance -- which would have failed main_menu and extras too, six
# rows instead of four, for no reason anyone could have read off the output. A
# derived allowance is only as durable as the thing it derives from, and I
# pointed this one at a file I then emptied one iteration later.
#
# It now derives from the EXPORT, which is what the port actually draws from: a
# screen may differ if any of its elements -- or any nested focus/leaf element --
# carries `blend_additive: true`, because `ui_layout.rs` has no additive path at
# all and cannot reproduce those draws by construction.
#
# ⚠️ THIS ALLOWANCE IS LOOSER THAN THE ONE IT REPLACES AND THAT IS A REAL COST.
# The old map covered 3 screens because it was a transcription of what somebody
# had driven the game to; the bit is disc-wide, so 12 of 16 screens now qualify
# and verify-screen goes fully green. Measured after the swap, the two sets line
# up exactly -- all 10 screens that DIFFER have a drawn additive element, and all
# 6 that agree have none -- so nothing is being excused that does not have the
# cause. But a screen that starts differing for some OTHER reason will now be
# excused if it happens to carry an additive element anywhere, and this check
# will not say so.
#
# 📌 THE REAL FIX IS NOT HERE. `verify-screen` compares two renderers and one of
# them cannot express a blend mode the disc declares, so the comparison is
# structurally incapable on 12 screens. Giving `ui_layout.rs` an additive path
# would make it meaningful again -- that is the Decoder's crate and their
# refutation of additive predates the RB_BLENDCONTROL0 measurement that overturned
# it, so it is PROPOSED here and not done.
additive_screens=$(python3 -c "
import json
d=json.load(open('authored/rendering.json')).get('additive_elements') or {}
print('\n'.join(k for k,v in d.items() if v))" 2>/dev/null)
import json, glob, os
out = []
for p in sorted(glob.glob('export/screens/*/*.json')):
d = json.load(open(p))
def any_add(els):
for e in els:
if e.get('blend_additive'):
return True
for k in ('focus', 'leaf'):
if any_add((e.get(k) or {}).get('elements', [])):
return True
return False
if any_add(d.get('elements', [])):
out.append(os.path.basename(p)[:-5])
print('\n'.join(out))" 2>/dev/null)
allow_args=(-e title -e title_jp)
for sc in $additive_screens; do allow_args+=(-e "$sc"); done
printf ' %-24s allowing %s (additive set + 2 legacy)\n' verify-screen \