port: the -324 was the old keyframe association, and the corrected one was available all along

The Decoder found it and it is not geometry: my leaf path pairs each pose with
the NEXT pose's time. Fed my pairing into its own interpolation it reproduces
-324 to the digit; corrected, t=355 gives centre 980.5 against 992.0 measured. So
there was no 1300 px pivot mystery, and saying both numbers rather than hunting
one is what surfaced it.

CORRECTING BLOCKED: this was never gated on a branch landing. The row said the
change "cannot be taken yet ... reaches the port only when that branch lands on
main". Wrong -- ui_layout.rs in this checkout carries the corrected association
behind SYLPHEED_KF_TIME_SHIFT=1 and has all along. I read that file twice this
session, once for rotation_deg and once for the leaf note, without noticing the
switch.

Ran the experiment. Re-exported under the shift and asked the oracle:

  publisher_logo   1.00% -> 0.75%   better
  developer_logos  0.39% -> 0.33%   better
  title      RMSE 21.07 -> 20.41    better (area 1.82 -> 1.86, worse)
  main_menu        0.06% -> 0.10%   worse
  extras           0.20% -> 0.24%   worse

NOT DECISIVE, and the reason is the point: under the shift the untimed keyframe
moves from LAST to FIRST, while `pose_at` is written around "the final keyframe
carries no t, so it is given a synthetic time exit_ramp_units after the last
timed frame". The numbers measure a renderer/association mismatch, not the
association. Adopting it means re-deriving exit_ramp_units, settle_units,
spin_period_units and the plate against a layout where the untimed frame is
first. Export reverted to the default; doing that work hastily is how a 1.8% gap
becomes five wrong ones.

THE METHODOLOGICAL POINT IS THE DECODER'S AND IS THE BEST THING HERE: its rule
matched because alpha at t=355 sits inside a long segment where a one-keyframe
shift barely moves it, while x sweeps 1560 px over the same span. It confirmed on
the insensitive quantity, I was wrong on the sensitive one, and neither of us saw
it until they were compared. CHECK A NEW INTERPRETATION AGAINST THE FASTEST-MOVING
FIELD YOU HAVE, NOT THE ONE THAT HAPPENS TO AGREE. That is a control chosen where
it could not fail, which is a different failure from the uncontrolled instruments
this session has collected.

An 11.5 px residual is left and is not to be fitted.

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 18:14:07 +00:00
parent 9f6959c7ca
commit c602bb3c47
2 changed files with 70 additions and 1 deletions

View File

@@ -128,7 +128,7 @@ HANDOFF.
| ~~P6 — runtime headroom~~ | ~~the Master bus clips~~ | — | 🟢 **withdrawn by the port, 2026-08-29 — it was my own overstatement.** Filed 🔴 twice on a peak reading of 0.0 dBFS. Measured properly: **43 samples at full scale in 5.9 s and 24 in 98.5 s, longest clamped run 0.25 ms** — the disc's own `confirm` cue touching the ceiling on a transient, possibly only in the recording's 16-bit conversion since Godot mixes in float. Not a defect, and nothing is changed: attenuating to buy headroom would be an unmeasured level decision of exactly the kind this port refused for the BGM loop point. **A peak reading is not a clipping measurement** — one sample at 0 dBFS and two seconds of square wave give the same number. |
| Modding — rule 4 | ~~base-and-overrides is unimplemented~~ | — | ✅ **implemented 2026-08-29, and it was not blocked on anybody.** `MODDING.md` calls it a constraint on the exporter *today*; nothing read `data/mods/` for eight milestones. `ExportTree.resolve` now shadows by path for every asset kind, each replacement is logged as it is read, and `.gitignore` excludes the directory's contents — a mod is usually an edited game asset, and that directory was the one place git would have taken one. ⚠️ The `export/` vs `data/base/` naming split between `PORT-MISSION.md` §3 and `MODDING.md` is **raised, not resolved**: only the human changes a mission. |
| P1P7 — the keyframe record layout | **every pose is timed, and `exit_ramp_units` becomes decodable** | — | 🔴 **large blast radius, NOT taken, and it cannot be taken yet.** The RE agent reports (branch `auto/no-disc-and-menu-captures`, sha `b28dfed`, tag `formats-pin-2026-08-29c`, `docs/re/ui-keyframe-record-layout.md`) that a pose's time is the word **before** it and pose 0's time is the group's lead-in, so **every** pose is timed including the last — and that *both* readings the corpus was arguing between were wrong. It touches `pose_at`, `settle_units`, `spin_period_units`, `exit_ramp_units` and the plate. `sylpheed-formats` is a **path** dependency, so this reaches the port only when that branch lands on `main`; merging another agent's topic branch is not mine to do. ✅ Two things that limit the risk, both from the same message: **`ramp: "linear"` stays** — do not add easing — and **static composites are byte-identical across the change on all 12 `GP_TITLE` builds**, which `docs/port/DECISIONS.md`'s new `verify-screen` baseline is exactly the control for. Re-run `tools/port/verify-screen` the day the pin lands: if a still frame moves, it is the port's bug and not the decoder's change. |
| P1P7 — the keyframe record layout | **adopt the corrected pose/time pairing** | — | 🟡 **AVAILABLE TODAY — this row's "cannot be taken yet" was WRONG.** It said the change reaches the port only when the Decoder's branch lands on `main`. It does not: `ui_layout.rs` in this checkout carries the corrected association behind **`SYLPHEED_KF_TIME_SHIFT=1`**, and has all along. I read that file twice in one session without noticing the switch. **Measured** by re-exporting under it and asking the oracle: `publisher_logo` 1.00 % → **0.75 %**, `developer_logos` 0.39 % → **0.33 %**, `title` RMSE 21.07 → **20.41** — but `main_menu` 0.06 % → 0.10 % and `extras` 0.20 % → 0.24 % **worse**. ⚠️ **Not decisive, and the reason matters**: under the shift the untimed keyframe moves from **last to first**, while `pose_at` is written around *"the final keyframe carries no `t`"*. So the numbers measure a renderer/association mismatch, not the association. Adopting it means re-deriving `exit_ramp_units`, `settle_units`, `spin_period_units` and the plate against a layout where the untimed frame is first — real work, not a flag flip. Export left on the default. |
| ~~P7 / naming — the four unnamed builds~~ | ~~which locale and variant is each of entries 0, 1, 12, 15?~~ | — | ✅ **answered 2026-08-29** (`docs/re/ui-title-build-map.md`): all four are the loading screen, two variants — plain (7 elements) and dressed (10) — decoded from their own `pgloading_*` element names. ⚠️ **Not adopted as names yet, for two reasons the RE agent gave and one the port found.** Theirs: the executable names exactly two, and *which* bundle takes which name is 🟡 undecided, so `LOADING`/`LOADING2` must not go in an asset path; and locale is 🟡 — the English member of a pair is the one in the first half of `GP_TITLE.p00`, 8/8 structurally but only 3/3 where a capture can check, and the three pairs that matter are the three no capture can check. Mine: **the message gives the bundles as "0/1 and 10/11", which is the `is_build` ordinal, and `authored/screen_names.json` is keyed by PAK ENTRY** — in entry space 10 and 11 are `palogo_sqex` and `palogo_gamearts`, the splashes. See the refutation section in `DECISIONS.md`. |
## Answered since this file was last written — no longer blocking

View File

@@ -4054,3 +4054,72 @@ decode covers — while the exporter keeps flagging all 15.
Its `max` went 155 → 232 when they were drawn, and **that number is not evidence
in either direction.** Recognising it as uninformative is the reason it did not
become a justification.
## The 324 was the old keyframe association, and the corrected one is available **today**
The Decoder found it and it is not geometry: my leaf path pairs each pose with
the *next* pose's time. Fed my pairing into its own interpolation, it reproduces
**324 to the digit**. Corrected, t=355 gives top-left **781** and centre
**980.5** against **992.0** measured — so there was never a 1 300 px pivot
mystery to look for, and I am glad I said the two numbers instead of hunting one.
### 🔴 Correcting BLOCKED: this was never gated on a branch landing
`docs/port/BLOCKED.md` has carried the keyframe-record-layout row as *"NOT taken,
and it cannot be taken yet … `sylpheed-formats` is a path dependency, so this
reaches the port only when that branch lands on `main`."*
**That is wrong.** The corrected association is in this checkout, behind an
environment variable:
```rust
// Set `SYLPHEED_KF_TIME_SHIFT=1` to read `W[k-1]` as block `k`'s time
// ("the word is the time the NEXT pose is reached") instead of `W[k]`.
```
It has been switchable the whole time. **I read that file twice this session —
once for `rotation_deg`, once for the leaf note — and did not notice the switch.**
### The experiment, run: mixed, and not decisive for the reason that matters
Re-exported with `SYLPHEED_KF_TIME_SHIFT=1` and asked the oracle:
| screen | default | shifted |
|---|---|---|
| `main_menu` | 13.21 / 0.06 % | 13.81 / **0.10 %** — worse |
| `extras` | 13.38 / 0.20 % | 13.95 / **0.24 %** — worse |
| `title` | 21.07 / 1.82 % | **20.41** / 1.86 % — RMSE better, area worse |
| `publisher_logo` | 10.77 / 1.00 % | **9.05 / 0.75 %** — better |
| `developer_logos` | 9.37 / 0.39 % | **8.86 / 0.33 %** — better |
⚠️ **And it does not adjudicate the association, because the port's renderer is
built for the other one.** Under the shift the **untimed keyframe moves from last
to first** — the leaf reads `t=None` at x=639, then t=150, t=540 — while
`pose_at` is written around *"the final keyframe carries no `t`, so it is given a
synthetic time `exit_ramp_units` after the last timed frame."* So this measures a
**renderer/association mismatch**, not the association. BLOCKED said as much:
the change touches `pose_at`, `settle_units`, `spin_period_units`,
`exit_ramp_units` and the plate.
**Export reverted to the default.** Adopting the shift is a real piece of work —
re-deriving the exit ramp, the settle, the spin period and the plate against a
layout where the untimed frame is the *first* — and doing it hastily at the end of
an iteration is how a 1.8 % gap becomes five wrong ones.
### ⚠️ The methodological point, which is the Decoder's and is the best thing here
Its rule matched because **alpha at t=355 sits inside a long segment where a
one-keyframe shift barely moves it, while x sweeps 1 560 px over the same span.**
It confirmed on the insensitive quantity; I was wrong on the sensitive one; and
neither of us saw it until the two were compared.
> **Check a new interpretation against the fastest-moving field you have, not the
> one that happens to agree.**
That is a different failure from the ones this session has collected — not an
uncontrolled instrument, but a control chosen where it could not fail.
❔ A residual **11.5 px** (980.5 against 992.0) is left over and is **not** to be
fitted. A rotation about a declared pivot rather than the centre would displace by
roughly that; if it still matters once the association is adopted, it gets
measured rather than derived.