port: the title residual is a horizontal redistribution, and three of my own explanations are dead

`title` is the port's largest disagreement with the oracle at 1.82%, and last
iteration I attributed it to the moving ptloop sweeps without checking. Wrong,
and so were the two hypotheses I formed after it.

NOT THE SWEEPS. ptloop01/02 are 399x180 at (441,270) -- small and central -- and
their exported keyframes hold pos, scale and rotation constant. The difference
peaks at x~1088.

NOT AN OVER-HELD ELEMENT. Added `--no-hold` to render the alternative: playing
the title's groups past rest fades the screen to black by t=5.2 s, 30.97%
differing against 1.82% held. Holding at rest is right.

NOT A TIMING OFFSET. Sweeping the build-in gives 24.05% at t=1.6 falling
monotonically to 1.68% at t=4.18 and 1.82% settled. The capture is at the settled
end.

WHAT IT IS: a horizontal redistribution. Signed difference by cell shows the port
DARKER centre-left (-13.1, -8.3, -6.6) and BRIGHTER right (+16.0, +9.9), nearly
cancelling -- whole-frame means 63.8 against 62.5. Brightness in the wrong place,
not a level error or a tone ramp. It falls in the rows spanned by the two wide
elements ptlogo_back2 (1118x262) and ptlogo_back2eff (1133x280), with the column
profile falling off past x~1152 against their right edges at 1189 and 1197.

AND THE EXPORT CARRIES NO BLEND MODE. ptlogo_back2eff's keys are declared, id,
index, keyframes, kind_raw, layer, layer_source, pivot, rest, role, sprite --
there is no blend field, in this element or in FORMAT.md at all, and the port
composites everything with normal alpha. If the game draws `_eff` layers
additively, a wide gradient sprite would produce exactly this signature and
nothing in the export would reveal it. Asked, not assumed; I have not tested it,
and I am recording it because the three I could test are dead.

Recorded and NOT acted on: pteff02's rest.t is 46, where its fade is 25% black,
while its own group reaches 0x00000000 at t=236 -- so the port holds a black veil
the timeline removes. Third instance of rest.t naming a hold that is not the
settled state. It does not explain the residual: removing a darkening veil would
make the port brighter still, and it is already brighter where it disagrees.

`--no-hold`'s first version set the flag thirty lines before `view` exists and
silently rendered nothing, caught because the loop found no files rather than
because anything reported an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-29 17:53:36 +00:00
parent 14ec1c1df8
commit 8bb9238632
2 changed files with 83 additions and 0 deletions

View File

@@ -3855,3 +3855,74 @@ between the two of us this session.
**What it is not:** it identifies focus in *one frame*. It says nothing about
what *selects* focus; Q5's instability stands.
## The title's 1.82 % — three of my own explanations refuted, and the format has no blend mode
`title` is the port's largest disagreement with the oracle, and last iteration I
attributed it to the moving `ptloop` sweeps *without checking*. That attribution
is wrong, and so were the two hypotheses I formed after it.
**❌ Not the sweeps.** `ptloop01`/`ptloop02` are **399×180 at (441, 270)** — small
and central — and their exported keyframes hold `pos`, `scale` and `rotation`
constant, varying only alpha. The difference peaks at **x ≈ 1088**, nowhere near
them.
**❌ Not an over-held element.** `--screen` holds every element at its own
`rest.t`, so I added `--no-hold` to render the other answer. Playing the title's
groups past their rest **fades the whole screen to black by t = 5.2 s**
30.97 % differing against 1.82 % held. Holding at rest is right.
**❌ Not a timing offset.** Sweeping the build-in: 24.05 % at t=1.6, falling
monotonically to **1.68 % at t=4.18** and 1.82 % settled. The capture is at the
settled end; there is no earlier moment that fits better than marginally.
### What it actually looks like: a horizontal redistribution
Signed difference (port capture), by cell:
| | x=0 | x=320 | x=640 | x=960 |
|---|---|---|---|---|
| y=0 | +1.1 | **13.1** | 6.6 | **+16.0** |
| y=169 | +2.1 | **8.3** | +4.2 | **+9.9** |
| y=338 | +5.8 | 4.5 | 0.0 | +3.2 |
| y=507 | +3.9 | +3.3 | +2.0 | +2.3 |
**The port is darker centre-left and brighter right, and it nearly cancels**
whole-frame means 63.8 against 62.5. That is not a level error and not a tone
ramp; it is brightness in the wrong *place*. And it falls in exactly the rows
spanned by the two wide elements `ptlogo_back2` (1118×262 at 71,126) and
`ptlogo_back2eff` (1133×280 at 64,117), with the column profile falling off past
x≈1152 against their right edges at 1189 and 1197.
### 🔴 The export carries no blend mode, so the port cannot draw one
`ptlogo_back2eff`'s exported keys are `declared, id, index, keyframes, kind_raw,
layer, layer_source, pivot, rest, role, sprite`. **There is no blend field**, in
this element or in `FORMAT.md` at all. The port composites everything with normal
alpha.
If the game draws `_eff` layers **additively**, a wide gradient sprite would
produce precisely this signature — surplus where the sprite is bright, deficit
where the underlying art is brighter than the sum — and **nothing in the export
would reveal it.** That is a decoding question, not a port one, and it is asked
rather than assumed. ⚠️ It is a hypothesis I have not tested; I am recording it
because the three I could test are dead.
### A separate `rest.t` casualty, recorded and not acted on
`pteff02` is a full-frame primitive whose group runs `0x40000000` (25 % black) at
t=46 → `0xd4000000` at 76 → `0xcc000000` at 118 → **`0x00000000` at 236**. Its
`rest.t` is **46**, so the port holds a **25 % black veil the screen's own
timeline removes**. This is the third instance of `rest.t` naming a hold that is
not the settled state — after the loading screen's opaque quad and `ptlogo1`'s
creep. ⚠️ **It does not explain the residual** — removing a darkening veil would
make the port brighter still, and it is already brighter where it disagrees — so
it is recorded rather than fixed.
### And a new diagnostic
`--no-hold` plays a screen past its rest instead of clamping each element at
`rest.t`. Added because the question *"is the held pose what the idle game
shows"* could not be asked otherwise. ⚠️ Its first version set the flag thirty
lines before `view` exists and silently rendered nothing — caught because the
comparison loop found no files, not because anything reported an error.

View File

@@ -182,6 +182,18 @@ func _ready() -> void:
# own logic on purpose -- see `looping_focus` there for the census that says
# this cannot be a rule.
_looping = timing.get("looping_focus_records", {})
# `--no-hold` plays a screen's groups PAST their rest instead of clamping each
# element at its own `rest.t`. A diagnostic, not a mode: `rest.t` is the last
# HOLD keyframe before the exit, not the settled state, and the only way to
# ask "is the port's held pose what the idle game shows" is to be able to
# render the other answer. Added when the title's 1.8 % disagreement with the
# oracle could not be attributed without it.
#
# It goes HERE and not with the other flags: `view` does not exist until this
# point, and the first version set it thirty lines too early and silently
# rendered nothing.
if args.has("no-hold"):
view.holding = false
viewport.add_child(view)
view.looping_focus = _looping_for(name)