port: P7 -- the new-game intro plays, and the two screens it skips are named out loud
S00A has been exported since P4; what P7 needed was something to play it and a
defined place to land. Both are here, and the interesting part is the gap.
The real chain is NEW GAME -> DIFFICULTY -> SELECT DATA -> (A) on a save slot ->
~4.5 s -> S00A. DIFFICULTY and SELECT DATA are MEASURED destinations that are not
GP_TITLE builds, so no screen file exists to go to. The port jumps from NEW GAME
to the one thing in that chain it has -- and the whole design is about not
letting that read as a sequence:
* MenuFlow.accept returns a new kind, `video`, rather than folding this into
`blocked`, because the caller has to announce the skip and a distinct kind is
what forces it to;
* the runtime prints the skipped screens by name on every run;
* flow.json carries `skipped_chain` as DATA, so what is missing lives beside
the decision instead of inside a GDScript string.
After the movie the port returns to the title. Authored, and it has to be: the
game goes into mission 1 and gameplay is out of scope. The ~4.5 s before the
movie is left EMPTY on purpose -- GP_TITLE does carry a loading screen and 4.5 s
is about the right shape for one, which is exactly why that belongs in BLOCKED.md
and not in flow.json.
`--script`'s 20 s per-step timeout would have killed every movie run at step 1.
Raising the constant would have been wrong the other way: a movie stuck at frame
0 would then hang the job, and a job that waits is worse than one that fails. The
test is now LIVENESS -- while get_stream_position() advances the deadline moves
with it, and a stalled movie still trips the same 20 s.
Found while looking: GP_TITLE's four unnamed builds (entries 0, 1, 12, 15) are
LOADING screens -- every element in all four is pgloading_*, and LOADING is one of
the three names the decoder read out of the title part's state function. NOT
renamed here: which member of each pair is which locale is an inference, and a
name stops being questioned once written. Handed over.
One of them is a second casualty of the rest.t problem, and a worse one:
pgloading_eff00.prm rests OPAQUE BLACK at t=38, so anything drawing that screen
at its declared rest paints a black rectangle over all of it. The title's case
only dimmed a frame.
REFUTATION, attempted and SURVIVED: HANDOFF says "exactly the six screen builds
carry the black .prm quad while the six overlays do not". Counting bundles with a
full-screen black primitive gives 8 and 4 -- build_12/15 carry one too. But
theirs runs black -> held -> clear where the transition quad runs black -> clear
-> black, so read strictly as "the quad whose group is the transition" the claim
holds. Recorded anyway: there are two kinds, and the naive census over-counts.
Gate: NEW GAME -> S00A plays 93.75 s against a declared 93.9 -> title, with
98.453 s recorded off the Master bus. What that does NOT show is that S00A's own
audio is in the mix -- bed and movie were not separated in this run, and the
write-up says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC
This commit is contained in:
@@ -90,6 +90,10 @@ git log -1 --format=%h -- docs/port/HANDOFF.md # newer than 9ca1eb5? re-reconc
|
||||
| P3 — the plate's PULSE | **does the plate's focus record loop, and with what period?** | Q2 | ❔ **open, and the port's earlier reading of it was wrong.** The port had looked for the pulse in `ptbtn00`'s own group; `5b0a6e6` identifies it as the plate's **focus record** `ptbtn00f` — a glow ramping alpha `0x00`→`0x50` and back, t=6…105. Measured on the running game at 2.12 / 2.19 / 2.34 / 2.31 s, mean **2.24 s**. 🟡 **The port has not taken it.** Looping that record needs a period, and its group is 105 timed units plus the **authored** 24-unit exit ramp = 129 units = 2.15 s — composing an authored constant with a loop assumption to land on a measured number is tuning, not measuring. Separately: the port draws no focus record on `press_start` at all, because the screen has no `buttons` and nothing is focused, so *whether the game always draws it* is its own question. |
|
||||
| ~~P5 focus ring — implementation~~ | ~~the ring's spin period~~ | Q1 | ✅ **implemented 2026-08-29.** `ScreenView.spin_period_units` drives it: one turn per the element's own declared `t`, looping, from the screen clock. The period comes off the **disc**; what the RE agent supplied is that the turn repeats rather than stopping. Verified on the port's own render — the ring is bit-identical one period apart across the whole frame, differs by 3.6/255 inside its box at quarter-period steps, and conserves box luminance to **0.027 %** over eight phases, which is the same observable the RE agent used to separate rotation from a pulse. 🟡 **Direction is not measured** — the port turns 0°→+360°, which is the sign the disc declares, but the RE agent's angle estimators failed their controls and no signed angle was ever taken. 🟡 **Phase across a focus change is not measured** either: the port drives the ring off the screen clock, so it does not reset when focus moves. Settled by two frames straddling a focus change. |
|
||||
|
||||
| P7 / naming — the four unnamed builds | **which locale and variant is each of `GP_TITLE` entries 0, 1, 12, 15?** | — | 🟢 **found by the port, not blocking, and handed over.** All four are **loading screens**: every element in all four is named `pgloading_*` (`pgloading_processing.png`, `pgloading_circle1`, `pgloading_delta`, `pgloading_ring`), and `LOADING` is one of the three screen names the Decoder read out of `sub_821C6458`. They export today as `build_00`, `build_01`, `build_12`, `build_15`. Two variants: 0/1 carry 7 elements, 12/15 carry 10 (adding `pgloading_eff00`, `pgloading_loop5`, `pgloading_baseeff`). The archive's own pairing — adjacent for 2/3, `+3` for 4…9 and 10/13, 11/14 — suggests 0 is the twin of 1 and 12 the twin of 15, but **which member is which locale is an inference and the port has not named them on it**. Naming is cheap for the Decoder and a guess for the port. |
|
||||
| P7 — what fills the 4.5 s before `S00A` | **is the LOADING screen what appears between the save slot and the new-game movie?** | Q4 + Q9 | ❔ **not observed, and the port has not assumed it.** Q9 measures `S00A.wmv` starting ~4.5 s after Ⓐ on the save slot. The run that would have shown what is on screen for those 4.5 s hit the documented `sub_823070B0` cache crash after `SELECT DATA`. `GP_TITLE` carries a loading screen (row above) and 4.5 s is about the right shape for one, and that is **exactly why it is written here and not in `flow.json`**. Settled by one run that reaches the movie without crashing. |
|
||||
| P3 — a second `rest.t` casualty | **the loading screen's fade quad rests OPAQUE BLACK** | — | 🔴 **noted, not fixed.** `pgloading_eff00.prm` on entries 12/15 is a 1280×720 black quad whose group is `0xff000000` at t=38, `0xff000000` at t=48, then `0x00000000` on the untimed final — black, held, *then* clear. Its `rest.t` is **38**, where it is fully opaque. So a port that draws this screen at its `rest` draws **a black rectangle over the whole loading screen**. This is the same `settle_time()`/`rest.t` problem as the row below, in a form where it hides the entire screen rather than dimming it — and it will bite whoever first draws a loading screen. |
|
||||
|
||||
## Answered since this file was last written — no longer blocking
|
||||
|
||||
Q1 (keyframe time unit — linear ramp, 2 units per rendered frame, 1 unit = 1/60 s
|
||||
|
||||
@@ -1924,3 +1924,174 @@ printed, and the only symptom was a frame slightly darker than the previous
|
||||
run's. A gate artifact that silently drifts is the failure mode this project
|
||||
keeps meeting — and it was caught only because there was a previous capture to
|
||||
compare against.
|
||||
|
||||
## P7 — the new-game intro, 2026-08-29
|
||||
|
||||
`S00A.wmv` has been in `export/video/` since P4 (MISSION §6 put both movies in
|
||||
scope at once). What P7 needed was for something to *play* it and for the run to
|
||||
end somewhere defined.
|
||||
|
||||
### The port skips two measured screens, and says so on screen
|
||||
|
||||
The real chain is **`NEW GAME` → `DIFFICULTY` → `SELECT DATA` → Ⓐ on a save slot
|
||||
→ ~4.5 s → `S00A`** (HANDOFF Q4 measured the screens, Q9 decoded the movie and
|
||||
then measured its onset off the running game at 0.96–1.000 with a strictly
|
||||
monotone playhead over 25 consecutive 0.5 s samples).
|
||||
|
||||
`DIFFICULTY` and `SELECT DATA` are measured destinations that are **not
|
||||
`GP_TITLE` builds**, so no screen file exists to go to. The port therefore jumps
|
||||
from `NEW GAME` to the one thing in that chain it has.
|
||||
|
||||
That is a gap, not a sequence, and the whole design here is about not letting it
|
||||
read as one:
|
||||
|
||||
* `MenuFlow.accept` returns a **new kind**, `video`, rather than folding this
|
||||
into `blocked`. The caller has to announce the skip, and a distinct kind is
|
||||
what forces it to.
|
||||
* The runtime prints it every time:
|
||||
`(NEW GAME) -> the real chain is DIFFICULTY -> SELECT DATA, then the movie.
|
||||
Neither screen is in this export.`
|
||||
* `authored/flow.json` carries `skipped_chain` as **data**, so the names of what
|
||||
is missing live beside the decision rather than inside a GDScript string.
|
||||
|
||||
A port that quietly jumped from `NEW GAME` to the intro would be showing a
|
||||
sequence the game does not have, with nothing on screen saying so. That is the
|
||||
exact failure this project keeps meeting from the other direction.
|
||||
|
||||
### What happens after the movie is authored, and had to be
|
||||
|
||||
The game goes into **mission 1**. Gameplay is out of scope (PORT-MISSION §7), so
|
||||
"returns to a defined state" is a decision, and P7's gate says as much. The port
|
||||
returns to the **title**: the boot's own end state, so a run that finishes the
|
||||
intro lands somewhere a player can start again from. Nothing measured says the
|
||||
game does this, and `after_video.kind` is `"authored"`.
|
||||
|
||||
### The 4.5 s gap is left empty on purpose
|
||||
|
||||
Q9 measures the movie starting ~4.5 s after Ⓐ on the save slot. What is on screen
|
||||
for those 4.5 s was never observed — the run that would have shown it hit the
|
||||
documented `sub_823070B0` cache crash after `SELECT DATA`.
|
||||
|
||||
`GP_TITLE` **does** carry a loading screen (below), and 4.5 s is about the right
|
||||
shape for one. That is precisely why it is in `BLOCKED.md` and not in
|
||||
`flow.json`: a plausible filler that nobody watched is the kind of thing that is
|
||||
indistinguishable from a measurement a month later.
|
||||
|
||||
### A script timeout that would have failed every movie
|
||||
|
||||
`--script`'s per-step timeout is 20 s, to stop an unattended run waiting forever
|
||||
on a screen that never settles. `S00A` is **93.9 s**, so the first scripted
|
||||
new-game run would have been killed at step 1 and reported as "never settled".
|
||||
|
||||
Raising the constant would have been wrong in the other direction: a movie stuck
|
||||
at frame 0 would then hang the job, and a job that waits is worse than a job that
|
||||
fails, because it does not look like a failure.
|
||||
|
||||
So the test is **liveness, not duration**: while `get_stream_position()`
|
||||
advances, the deadline moves with it; a stalled movie still trips the same 20 s.
|
||||
|
||||
### Found while looking: `GP_TITLE`'s four unnamed builds are LOADING screens
|
||||
|
||||
`build_00`, `build_01`, `build_12`, `build_15` have never had names. Every
|
||||
element in all four is `pgloading_*` — `pgloading_processing.png`,
|
||||
`pgloading_circle1`, `pgloading_delta`, `pgloading_ring` — and `LOADING` is one
|
||||
of the three screen names the RE agent read out of the title part's state
|
||||
function.
|
||||
|
||||
Two variants: 0/1 carry 7 elements, 12/15 carry 10.
|
||||
|
||||
**They are not renamed here.** The archive's own pairing (adjacent for 2/3, `+3`
|
||||
for 4…9 and for 10/13, 11/14) suggests 0 is 1's twin and 12 is 15's, but which
|
||||
member of each pair is which **locale** is an inference, and a name is exactly
|
||||
the kind of thing that stops being questioned once written. Handed to the RE
|
||||
agent, who can answer it from a capture in one look. `BLOCKED.md` has the row.
|
||||
|
||||
⚠️ **And one of them is a second casualty of the `rest.t` problem.**
|
||||
`pgloading_eff00.prm` on entries 12/15 is a full-screen black quad whose group
|
||||
runs `0xff000000` at t=38 → `0xff000000` at t=48 → `0x00000000` untimed: black,
|
||||
held, *then* clear. Its `rest.t` is **38**, where it is fully opaque. A port that
|
||||
draws that screen at its declared rest draws **a black rectangle over the entire
|
||||
loading screen**. The title's case only dimmed a frame; this one hides
|
||||
everything. Filed with the `settle_time()` row it belongs to.
|
||||
|
||||
### Refutation — attempted on the fade-quad census; it survives, with a caveat
|
||||
|
||||
**The claim** (HANDOFF, on transitions): *"in `GP_TITLE` exactly the six screen
|
||||
builds carry it while the six overlays do not"*, where "it" is the full-screen
|
||||
black `.prm` quad *whose keyframe group is the transition*.
|
||||
|
||||
**The test**, over the whole export: count builds carrying a full-screen
|
||||
primitive with black in its keyframes.
|
||||
|
||||
```
|
||||
16 builds exported; 12 carry one.
|
||||
Of the 12 `is_build` bundles (excluding the 4 authored splashes): 8 carry, 4 do not.
|
||||
carry: title, title_jp, main_menu, main_menu_jp, extras, extras_jp,
|
||||
build_12, build_15
|
||||
do not: press_start, press_start_jp, build_00, build_01
|
||||
```
|
||||
|
||||
8 and 4, not 6 and 6. But the two extras are `build_12` / `build_15`, and their
|
||||
quad is a **different shape**:
|
||||
|
||||
| | transition quad (`pteff00.prm`, title) | loading quad (`pgloading_eff00.prm`) |
|
||||
|---|---|---|
|
||||
| | `0xff000000` t=16 | `0xff000000` t=38 |
|
||||
| | `0x00000000` t=261 | `0xff000000` t=48 |
|
||||
| | `0xff000000` untimed | `0x00000000` untimed |
|
||||
| shape | black → clear → **black** | black → held → **clear** |
|
||||
|
||||
The transition quad returns to black on exit; the loading quad does not. Read
|
||||
strictly — the quad *whose group is the transition* — the claim holds.
|
||||
|
||||
**Verdict: survives.** The refinement is worth recording anyway, because the
|
||||
naive test over-counts by two and somebody will run the naive test. There are
|
||||
**two kinds** of full-screen black `.prm` in `GP_TITLE`, and only one of them is
|
||||
a transition.
|
||||
|
||||
## P7 gate
|
||||
|
||||
```
|
||||
godot --path port -- --menu --script=accept --audio=…/p7.wav
|
||||
```
|
||||
|
||||
```
|
||||
menu on main_menu, focus ptbtn01
|
||||
script[1] accept
|
||||
(NEW GAME) -> the real chain is DIFFICULTY -> SELECT DATA, then the movie.
|
||||
Neither screen is in this export.
|
||||
-> video S00A at 1.18 s (/work/export/video/S00A.ogv)
|
||||
video ended at 94.93 s
|
||||
-> title (authored: authored)
|
||||
menu on title, focus (none -- this screen has no focusable item)
|
||||
script complete after 99.28 s on title
|
||||
recorded 98.453 s of Master bus (driver Dummy)
|
||||
```
|
||||
|
||||
The movie ran **93.75 s** against a declared 93.9 s, the run ended on the title,
|
||||
and the Master bus recorded 98.453 s: `pcm_s16le`, 44.1 kHz stereo, RMS
|
||||
**−22.2 dBFS**.
|
||||
|
||||
**What this does not show, stated because it would be easy to imply otherwise:**
|
||||
the recording contains the menu bed *and* the movie together, and this run did
|
||||
**not** separate them. So it establishes that the engine reached an output for
|
||||
98 s of a run whose middle 94 s was a movie — not that `S00A`'s own audio track
|
||||
is in the mix. Separating them wants the P6 null-control method (a paired run
|
||||
that differs only in the movie), and that is not done here.
|
||||
|
||||
🔴 **Peak 0.0 dBFS again.** The same runtime-headroom problem P6 filed: per-file
|
||||
levels are the disc's and are fine, the Master bus has no headroom, and the port
|
||||
has set no bus balance because nothing measures one.
|
||||
|
||||
### One more file read while it was being written
|
||||
|
||||
`ls` reported the recording as **3 702 828 B**; `ffprobe` on the finished file
|
||||
reports **17 367 084 B / 98.452608 s** — a factor of 4.7. `ffprobe` is right and
|
||||
the `ls` caught it mid-flight.
|
||||
|
||||
`AUDIO-VERIFICATION.md` opens by naming this failure and the port has had the
|
||||
temp-name-then-rename discipline since P6, which is what makes it worth writing
|
||||
down rather than shrugging off: **the discipline protects a reader who opens the
|
||||
path, and it does not protect a reader who stats it at the wrong moment.** Size
|
||||
on disk is not a measurement of a file somebody else is still writing. Ask the
|
||||
decoder, not the directory entry.
|
||||
|
||||
Reference in New Issue
Block a user