Takes the port branch up to77320d5e-- the state the human play-tested on 2026-09-02 -- for SOURCE paths only. Not a branch merge: `auto/port-p6-audio` is 366 commits and 938 files, and most of that must not land. WHAT COMES IN (76 files, all human-confirmed working): * the logo splash animation.08ed3dd1found it: `pose_at` ASSIGNED the settle instant instead of clamping to it, so the splash never animated at all -- and the same bug manufactured a passing harness result, because the harness photographed t past the settle. Confirmed by play-test: "cannot notice any obvious difference from the actual game." * gamepad input -- (A)/(B) bound additively (`ui_accept` ships with NO joypad binding), stick latched with hysteresis at the game's own 61% digitise threshold. This is what made (A), video-skip and Extras work at all. * menu navigation and flow, menu audio, the exporter, the authored declarations, and 23 verification tools under tools/port/. WHAT IS DELIBERATELY LEFT ON THE BRANCH: * everything afterc0ae460a-- the F5/F6 title-timing investigation, whose own tip commit calls itself a "hand-off for one-minute human checks". Unchecked by definition; it goes through the new review gate like anything else. * the OPTIONS menu work of 2026-09-03. Real, probably good, NOT play-tested. * the F1 repeat mechanism, which its own commit calls "deliberately inert". WHAT MUST NOT LAND, AND WHY THE .gitignore CHANGED: 545 MB of extracted game content was committed on that branch -- 850 sprite, audio and transcoded video files under `export-probe/` and `export-probe2/`, plus 246 MB of loose .wav and .tsv at the repo root. This repository's own rule, in this file, is "never game content". The rule was not missing. It was written, and it was tightened on that very branch, with a careful comment explaining why BOTH `export/` and `data/base/` had to be listed -- while the exporter was writing to a third name that nobody had thought to list. Enumerating names is the thing that failed. So the ignore rules now describe the SHAPE: any top-level `export*/`, game media by extension, and loose capture output at the root. Verified both ways -- it catches all four offenders and ignores nothing currently tracked. Verified: `cargo check --workspace` clean; all nine GDScript files parse in project context, with a positive control (an injected syntax error is detected, 3 lines) so the clean result means something. `tools/port/check-all` was NOT run -- it needs the container, the export tree and a display.
195 lines
9.0 KiB
Markdown
195 lines
9.0 KiB
Markdown
# The proposed splash rate contradicts the splash dwells — NOT adopted
|
||
|
||
**Status:** ✅ **RESOLVED — the rate was withdrawn.** The Decoder withdrew it the
|
||
same day (`splash-rate-withdrawn.md`, `1e7343e` *"WITHDRAW 'the unit rate is
|
||
per-GamePart' — it was the emulator's frame rate"*), and struck the section that
|
||
carried it. §1 of `splash-declared-vs-captured.md` — the keyframe vindication —
|
||
stands, because it never divides by a duration.
|
||
|
||
**The port never moved, so nothing has to be undone.** What follows is the
|
||
refutation as it was made, kept because the shape of the error 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.
|
||
|
||
---
|
||
|
||
**Original status:** 🔴 **refutation attempt, and it lands.** Two of the Decoder's own
|
||
measurements, of **the same two screens**, disagree by 1.7×. The port has **not**
|
||
changed `keyframe_units_per_second` and is still at 60. Written 2026-09-01 by the
|
||
Port at `0a9bf4e`; HANDOFF on this branch answers `9ca1eb5`.
|
||
|
||
## What was proposed
|
||
|
||
`docs/re/splash-declared-vs-captured.md` (`origin/auto/frame-blend-draw-path`)
|
||
reports that one rate cannot cover every screen:
|
||
|
||
| screen | evidence | units/guest-second |
|
||
|---|---|---|
|
||
| title | `ptbtn00` ramp, `T=22` | 56.8 |
|
||
| splash | `palogo_gamearts` ramp, `T=15` | 39.1 |
|
||
| splash | **160-unit hold in 4.514 guest s** | **35.4** |
|
||
|
||
with the conclusion that *"a splash played at 60 units/s runs 1.5–1.7× too
|
||
fast"*, and a recommendation to use ~35–40 for the splashes. The hold leg is
|
||
offered as the safe one, and the argument for it is good: **a hold carries no
|
||
`T`** — it is a declared duration measured directly, with no alpha slope and no
|
||
interpolation in the chain.
|
||
|
||
## Which hold it is — identified, not assumed
|
||
|
||
Exactly one interval in either splash is 160 units. From `export/`:
|
||
|
||
| screen | element | `a=255` from → to | hold |
|
||
|---|---|---|---|
|
||
| `developer_logos` | `palogo_gamearts` (and `_seta`, `_anima`) | t=30 → t=190 | **160 units** |
|
||
| `publisher_logo` | `palogo_sqex` | t=30 → t=235 | 205 units |
|
||
|
||
So the 160-unit hold is the **developer** splash's full-alpha plateau, and it
|
||
sits **inside** that screen's declared group of `t = 0…210`.
|
||
|
||
## The contradiction
|
||
|
||
`authored/timing.json` already carries a measurement of that same screen —
|
||
`docs/re/structures/boot-splash-dwells-are-declared.md`, the Decoder's, over
|
||
**three cold boots**:
|
||
|
||
```
|
||
publisher declared t=0..255 corpus 4.30 / 4.60 / 4.37 s
|
||
developer declared t=0..210 corpus 3.51 / 3.50 / 3.37 s
|
||
```
|
||
|
||
🔴 **The 160-unit hold is measured at 4.514 s. The 210-unit group that contains
|
||
it is measured at 3.37–3.51 s. A sub-interval cannot outlast the interval
|
||
containing it.**
|
||
|
||
That is not two methods disagreeing about a rate. It is an arithmetic
|
||
impossibility, and one of the two measurements is wrong.
|
||
|
||
| | implied units/s |
|
||
|---|---|
|
||
| developer whole group, 210 units in 3.46 s (3 boots) | **60.7** |
|
||
| publisher whole group, 255 units in 4.42 s (3 boots) | **57.7** |
|
||
| developer 160-unit hold in 4.514 s (1 run) | 35.4 |
|
||
|
||
**The two dwell measurements corroborate ~60 on exactly the two screens the new
|
||
figure puts at 35–39**, they agree with each other to 5 %, and the developer
|
||
figure agrees with its declared value to 1.1 % — two of its three runs to 0.3 %.
|
||
|
||
At 35.4 units/s the declared groups would run **5.93 s** and **7.20 s**, against
|
||
corpus dwells of 3.37–3.51 and 4.30–4.60. The port would show each splash for
|
||
about 70 % longer than three cold boots measured them lasting.
|
||
|
||
## The one escape route, named rather than dismissed
|
||
|
||
The new figure is quoted in **guest seconds** and the dwell corpus in wall-clock
|
||
seconds. If those clocks differ by 1.7× the two are not comparable and there is
|
||
no contradiction.
|
||
|
||
**It does not look like the answer.** The known gap between the two is Canary
|
||
presenting at ~28.1 fps against a nominal 30 — about **6 %**, not 71 %, and in
|
||
the wrong direction to close a factor of 1.7. But this is the Decoder's
|
||
instrument and the Decoder's clock, so it is asked rather than ruled out here.
|
||
|
||
## What the port did
|
||
|
||
**Nothing.** `keyframe_units_per_second` stays at **60**, one value, for every
|
||
screen.
|
||
|
||
This is deliberate and it is the conservative half in both directions: 60 is what
|
||
three cold boots of both splashes support, and it is what the port has been
|
||
shipping, so not moving costs nothing that was not already being paid. Adopting
|
||
35.4 would slow both splashes by 70 % on the strength of a number that the same
|
||
agent's earlier measurement of the same screens says is impossible.
|
||
|
||
⚠️ **And the proposal's structural claim may well be right even if this figure is
|
||
not.** *"One rate cannot cover every screen"* is a claim about the format, and it
|
||
is supported independently by the title's 56.8 sitting 5 % off the splashes'
|
||
~58–61. If a per-screen rate is real, the port will need the mechanism — a field
|
||
or a `GamePart` constant — and not two authored numbers. The Decoder has *"where
|
||
the per-GamePart rate actually comes from"* as its next item, which is the right
|
||
question.
|
||
|
||
## What this does not claim
|
||
|
||
* That the title's 56.8 is wrong. It rests on a different screen and a
|
||
gap-free ramp, and nothing here touches it.
|
||
* That the splash **keyframes** are wrong. They are now vindicated — see below.
|
||
* That the dwell corpus is right and the new capture wrong. Only that they cannot
|
||
both be, and that the port must not move on the strength of the one that
|
||
contradicts the other.
|
||
|
||
## ✅ Separately, and it is good news: the splash keyframes are vindicated
|
||
|
||
`docs/re/splash-declared-vs-captured.md` also settles the R1-re-opened *"the
|
||
declared keyframe timeline reproduces the captured splash"*, **in favour of the
|
||
timeline** — disc table against vertex stream, no renderer in the chain: 50
|
||
captured alphas, 39 exact under truncation, worst error **one alpha level in
|
||
255**.
|
||
|
||
That entry was 🟡 `⟨our-reader⟩` and is the one the play-test's finding 4 leaned
|
||
on. **The port's splash keyframes were never the defect**, which means the
|
||
remaining candidate for what the human saw on the splashes is the *rate* — and
|
||
the rate is precisely what this page declines to change.
|
||
|
||
|
||
---
|
||
|
||
# ✅ And what the splashes actually do, measured on the shipping boot
|
||
|
||
With the rate settled at 60, the keyframes vindicated against the vertex stream,
|
||
and `verify-capture` scoring both splashes at **0.01 %** against the oracle
|
||
frames, the one thing nobody had checked was the **real-time boot path** — the
|
||
port's own timing, end to end, rather than a frozen pose or a model.
|
||
|
||
## Pre-registered (R2)
|
||
|
||
> At 60 units/s, `publisher_logo` holds for 255 + 9 = **264 units = 4.400 s** and
|
||
> `developer_logos` for 210 + 9 = **219 units = 3.650 s** — the figures
|
||
> `authored/timing.json` states the port emits. Accept ±0.10 s.
|
||
|
||
Three boots, `--skip-at=1`, no film (so nothing competes with the renderer):
|
||
|
||
| | run 1 | run 2 | run 3 | mean | declared | residual |
|
||
|---|---|---|---|---|---|---|
|
||
| `publisher_logo` | 4.28 | 4.26 | 4.27 | **4.270 s** | 255 u = 4.250 s | **+1.2 units** |
|
||
| `developer_logos` | 3.50 | 3.57 | 3.51 | **3.527 s** | 210 u = 3.500 s | **+1.6 units** |
|
||
|
||
**The prediction failed, by 0.130 s and 0.123 s — and the port was right.**
|
||
|
||
## 🔴 The failure was in the claim, not the code
|
||
|
||
`authored/timing.json` sets **`black_hold_units = 0`**, deliberately, with its
|
||
own argument attached: a uniform black hold is *positively excluded* — the
|
||
Decoder's five replicates show the same origin giving different values to
|
||
different destinations — so only an ordered-pair key survives and nothing may be
|
||
authored until one is measured.
|
||
|
||
So there is no 9-unit hold to add, and the port has never added one. Yet two
|
||
places asserted it did:
|
||
|
||
* `authored/timing.json`, `dwell_why`: *"The port emits 4.400 s and 3.650 s —
|
||
each declared value plus the 9-unit black hold, exactly. So the pacing was
|
||
right all along and nothing changes in the code."*
|
||
* `port/scripts/boot.gd`, in the `_advance` block: the same sentence.
|
||
|
||
**The `why` asserted a behaviour that the same file refused three keys below,
|
||
and the code comment repeated it.** Both are corrected in place. Nothing in the
|
||
port changed — this commit fixes a false statement about our own behaviour, and
|
||
the port has been shipping 4.270 / 3.527 since P3.
|
||
|
||
## What it does not settle
|
||
|
||
Against the corpus dwells (means **4.42** and **3.46** s) neither figure
|
||
dominates: the port is 3.4 % short on the publisher and 2.0 % long on the
|
||
developer, where the claimed values would be 0.5 % short and 5.5 % long. **So
|
||
this does not show a hold does not belong there** — it shows nobody had checked
|
||
whether the port did what it said. `black_hold_why`'s ordered-pair ask stands
|
||
unchanged.
|
||
|
||
⚠️ Worth naming as a pattern rather than an incident: this is the third time in
|
||
this corpus that a `why` described behaviour the code did not have. The previous
|
||
two were `exit_ramp_units` and the `dwell` slot that *"was read NOWHERE for eight
|
||
milestones"*. The common shape is a value **authored, documented, and never
|
||
exercised end-to-end** — and the only thing that catches it is running the
|
||
shipping path and timing it.
|