From c462496d94c8212aaeb6ec08a24bffd86993de02 Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Sun, 30 Aug 2026 05:44:39 +0000 Subject: [PATCH] port: audit BLOCKED.md against a running port; three rows were stale The page warns it goes stale 'within the hour. It has.' Nothing audits it. P3/P5 - the title screen was wrong in every factual claim: the boot does not end on a plateless build 4, press_start is not unused, and the port has drawn two builds at once for weeks. A row four lines below was already marked answered and TAKEN for the same question -- two rows, one struck, and the LIVE one was stale. P4 - skippable led with 'the port could not tell which bug that is'. It did: DECISIONS records that (A) does skip in this build and every boot prints it. P6 looping now carries the measured seam: 3.4 s of near-silence at 87.8 s. The split is clean: every prose section added since 2026-08-29 records the HANDOFF sha it derives from, every row in the two tables records none -- and all three stale rows were in those tables. Not back-dated: an invented sha would make an undated claim look checked. Both tables now say so and carry an audit date. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- docs/port/BLOCKED.md | 16 +++++++++--- docs/port/DECISIONS.md | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index 5f1a2fb4..c93b1931 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -86,6 +86,11 @@ git log -1 --format=%h -- docs/port/HANDOFF.md # newer than 9ca1eb5? re-reconc ## Still open β€” these block work +πŸ”΄ **This table's rows carry no derivation sha, and that is why they rot.** The standing instruction is to record the HANDOFF commit each row was derived from; every *prose* section added since does, and **every row in this table and the next does not**. On 2026-08-30 an audit found three stale rows here β€” one of them contradicted by a struck row four lines below it, claiming the boot ends on a plateless title when it had drawn the plate for weeks. The undated rows are exactly the ones that went stale, which is as close to a controlled experiment as this page is going to get. + +⚠️ Rows are **not** being back-dated: nobody knows when most were written, and inventing a sha would be worse than admitting there is none. New rows carry one. This table was last audited **2026-08-30** against a running port. + + **Re-checked at the voice export**, `HEAD` = `3a4c6ac` (merged with `origin/main` at `1b1a4df`). `git log -1 --format=%h -- docs/port/HANDOFF.md` still answers **`9ca1eb5`** β€” HANDOFF has not moved in four milestones. The second-half check, @@ -100,16 +105,16 @@ HANDOFF. | P4/P7 β€” the intro's dialogue | ~~why the intro has no voices~~ | Q9 | βœ… **answered and TAKEN 2026-08-29, and the obvious diagnosis was wrong.** Not a transcode fault: `ADV.wmv` carries music and effects only, and a cutscene's voice is a *separate* continuous XMA stream in `sound.pak` bound by the movie manifest. `audio::export_voice` now resolves it with `media::resolve_movie_voice_region` β€” never by filename, because `RT01A`'s voice lives inside `VOICE_ADV.slb` and a name match is right on exactly the two movies this port would have spot-checked. This is **decoded, nothing authored**. ⚠️ **This row's original text said the region's chunks are "concatenated (one continuous stream), not summed" β€” that was the first of three wrong readings and it is superseded**; see the incomplete-export row below for where it ended up. Left visible rather than silently rewritten, because the sequence of wrong readings is what makes the final one checkable. | | P4/P7 β€” the movie downmix | **is the exporter allowed to ship a matrix MISSION Β§6 did not pin?** | β€” | πŸ”΄ **with the HUMAN, not the Decoder, and now visible for the first time.** Β§6 pins the 5.1 fold as a human decision of 2026-08-29; `video.rs` has shipped that matrix scaled by **0.4142** since P4 β€” same weighting, **7.65 dB quieter** β€” and said so nowhere. Re-measured this iteration with the right instrument (float decode, whole file, count the samples that would clamp, not a peak reading): under the **pinned** matrix `ADV` peaks at **+4.26 dBFS** with **4 406** samples at or over full scale and 1 874 more than 1 dB over, while `S00A` peaks at βˆ’1.34 dBFS and **never clips**. So the pin overloads one movie and the exporter's constant is over-broad for the other. Smallest single scalar under which neither clamps: **0.612**, +3.39 dB on today. **Not changed** β€” the level of a mix is what Β§6 reserves. The export now carries a manifest warning with these numbers. | | ~~P4/P7 β€” a voice region's chunks~~ | ~~what is the leading chunk, and is the second one played?~~ | β€” | βœ… **CLOSED 2026-08-29, decoded disc-wide, and it cost this exporter three wrong readings in one session.** A region carries **three presentations of one take** β€” the Decoder counted stream starts inside every inter-descriptor span: 258 spans hold one, 28 hold three, nothing holds two (`auto/no-disc-and-menu-captures` at `801062c`). So `359 = 84.55 + 137.32 + 137.32`. My concatenation was wrong, my "two stems" reading was wrong (and had already been adopted into the Decoder's page before I tested it β€” withdrawn in both), and summing was wrong a third time because a take plus a 0.60Γ— copy of itself is ~4 dB louder and coloured. The exporter now keeps **one stream** and does no arithmetic on it. The leading chunk is this movie's own dialogue, and I measured it to be the **tail** of the kept stream (r=0.998 / 0.932, controls 1.000 and 0.289), so dropping it removes a duplicate. 🟑 **What is left open is which presentation to keep**: the selector is highest byte rate on the Decoder's recommendation, nothing on the disc says which the game plays, and on `ADV` it picks the quieter of two. Settled by a capture of the movie's dialogue level. ❔ Why the disc stores three at all is unanswered by either agent. | -| P4 β€” is an attract movie skippable at all? | **does the real game let β’Ά end `ADV`, or does it play through?** | Q9 | πŸ”΄ **a human play-test reports β’Ά does not skip the port's intro, and the port could not tell which bug that is.** It is *implemented*, not assumed: `authored/flow.json` carries `skippable: true` with a `why` citing Q9 as measured (title at 57 s against a 193 s baseline), and `boot.gd` `_unhandled_input` acts on it. What did not exist was any way to **test** it: `--script` structurally cannot press during a movie, because `_script_settled` waits while `_player != null`. `--skip-at=SECONDS` was added this iteration to close that hole. ⚠️ Two different questions sit behind the one symptom, and only the first is mine: (a) does the synthetic press reach `_unhandled_input` β€” measurable here; (b) does the **game** permit skipping an attract movie β€” `INDEX.md` still marks skippability 🟑 and only a capture settles it. If (b) is no, the port's skip path is deleted rather than debugged. Asked 2026-08-29. | +| P4 β€” is an attract movie skippable at all? | **does the real game let β’Ά end `ADV`, or does it play through?** | Q9 | 🟑 **(a) ANSWERED, (b) still open β€” corrected 2026-08-30.** The headline used to say *the port could not tell which bug that is*. It can and it did: `DECISIONS.md` records **β’Ά *does* skip the intro in this build**, and every boot run since prints `video skipped at …`. It is *implemented*, not assumed: `authored/flow.json` carries `skippable: true` with a `why` citing Q9 as measured (title at 57 s against a 193 s baseline), and `boot.gd` `_unhandled_input` acts on it. What did not exist was any way to **test** it: `--script` structurally cannot press during a movie, because `_script_settled` waits while `_player != null`. `--skip-at=SECONDS` was added this iteration to close that hole. ⚠️ Two different questions sit behind the one symptom, and only the first is mine: (a) does the synthetic press reach `_unhandled_input` β€” measurable here; (b) does the **game** permit skipping an attract movie β€” `INDEX.md` still marks skippability 🟑 and only a capture settles it. If (b) is no, the port's skip path is deleted rather than debugged. Asked 2026-08-29. | | ~~P5 β€” a real submenu cycle~~ | ~~is any submenu reachable without a new archive?~~ | Q2/Q4 | βœ… **already shipped at P5, and one premise of the ask is refuted by this repo.** `ptbtn05` (EXTRAS) β†’ screen `extras` (entries 6/9), and `extras`' `on_cancel` returns to `main_menu` with focus restored β€” a full main-menu β†’ submenu β†’ back cycle, in `GP_TITLE`, live since P5. ⚠️ **Build 8 is not a submenu.** It is `main_menu_jp`, the Japanese five-button main menu; `authored/screen_names.json` records that an earlier reading called 8 a submenu and that HANDOFF Q2 **withdrew it** against a capture. That coordinates identical to build 5 mean a language twin rather than a second menu is exactly the inference the port is not allowed to make on layout similarity β€” in either direction. The other four main-menu items really are blocked: `GP_SAVE_LOAD`, `GP_OPTIONS`, `GP_MISSION_SELECT` and the `DIFFICULTY`/`TUTORIAL_MENU` builds are not in this archive. | | Milestone | Needs | HANDOFF | State | |---|---|---|---| | ~~P6 audio~~ | ~~which cue fires on move / confirm / back~~ | Q8 | βœ… **answered 2026-08-28** β€” the RE agent retracted "cannot be extracted". The waves are located in `Static.slb` by playing them: **move `0x1ec0`** (8 192 B, 0.533 s), **confirm `0x5d6c0`** (12 288 B, 1.016 s), **back `0x0ec0`** (4 096 B, 0.344 s), and β¬…βž‘ play nothing. Move and back reproduce across two boots. 🟑 that the cursor's wave is the cue *named* `SE_UI_CURSOR` is still a name match, and β’Ά's wave is not separated between `SE_UI_DECIDE` and `SE_UI_SUB_WIN_OPN`. P6 can now export real audio; the exporter has to grow an SE path. **βœ… TAKEN at P6, 2026-08-29.** The three offsets now live in `authored/audio.json` `se.*` β€” *not* in the exporter β€” because MISSION Β§3 puts a measured value in `authored/` and a measured offset compiled into a Rust `const` is a measurement wearing the costume of a decoded field. `sylpheed_formats::media::se_wave_riff` does the assembly. | | ~~P6 audio~~ | ~~which BGM the menu plays~~ | Q10 | πŸ”΄ **THIS ROW WAS WRONG WHEN IT WAS WRITTEN, and the port acted on it.** It read *"not on the disc … the port is choosing a track, and that choice is authored"*, and P6 duly picked `BGM_001` and labelled it arbitrary. **The menu's music is `BGM_103`, and it is in HANDOFF at `9ca1eb5` β€” the exact commit this page says it was reconciled against.** Not stale: misread. HANDOFF's negative is bounded and the bound is the whole content of it β€” the *tables* (`SOUNDS`, `FILES`, bank headers) name no screen; `GamePart_Title`'s `sub_821C5580` carries `li r5, 1103`, cue 1103 is `BGM_103`, and `BGM_103.slb`'s two declared waves (3 876 864 / 3 930 112 B) are byte-for-byte the two streams the XMA probe saw decoding at the main menu. HANDOFF's own sentence: *"The port does not have to choose a track."* **The lesson is not "re-read HANDOFF" β€” this page's own staleness check passed.** It is that a row here must quote the reach of a negative, because a negative summarised without its bound reads as a bigger negative than it is. | -| P6 looping | where a menu loop restarts | Q10 | ❔ **still open, and the port shipped the ugly answer on purpose.** No loop-point field has been identified in any bank β€” `BGM_001` is the one characterised end to end (fades out at 167.663 s into 6.15 s of silence) and nothing suggests `BGM_103` differs in kind. `authored/audio.json` sets `loop: "restart"` β€” replay from sample 0 β€” so the listener hears the fade-out and the trailing silence at the seam. **Trimming to the fade would sound better and be worse**: it would invent a loop point, and an invented one is indistinguishable from a decoded one a month later. What settles it: a loop-point field, or a capture of the real menu looping. | +| P6 looping | where a menu loop restarts | Q10 | ❔ **still open, and the port shipped the ugly answer on purpose.** No loop-point field has been identified in any bank β€” `BGM_001` is the one characterised end to end (fades out at 167.663 s into 6.15 s of silence) and nothing suggests `BGM_103` differs in kind. `authored/audio.json` sets `loop: "restart"` β€” replay from sample 0 β€” so the listener hears the fade-out and the trailing silence at the seam. **Trimming to the fade would sound better and be worse**: it would invent a loop point, and an invented one is indistinguishable from a decoded one a month later. What settles it: a loop-point field, or a capture of the real menu looping. ⚠️ **The cost is now measured, 2026-08-30.** The bed loops at 87.8 s against the track's own 87.7 β€” `loop: "restart"` behaves exactly as authored β€” and the seam is **36 consecutive near-silent 50 ms windows, 84.40–87.80 s**: about **3.4 seconds of silence** after a fade from RMS 2057 to 431. Long enough to read as the music having stopped rather than looped. That does **not** license trimming to the fade, which would still invent a loop point; it is recorded so the missing field's price is a number rather than an adjective. | | ~~P5 focus marker~~ | ~~the focus ring's spin PERIOD, and whether it loops~~ | Q1 + *"groups hold"* | βœ… **answered 2026-08-29, and NOT ON `main` YET.** The Decoder pointed at it over the message channel and the pointer resolves: branch `auto/no-disc-and-menu-captures`, commit **`4fa3099`** (branch head `66e74d4`), file `docs/re/focus-ring-spin-measured.md`, frames under `docs/re/captures/focus-ring/`. **The ring spins continuously β€” period 2.177 s wall-clock, eight evenly spaced autocorrelation peaks over nine revolutions**, with no angle estimated anywhere (both angle estimators failed their own controls and were not used). It also reconciles with the declared `t=120` without a new constant: 120 units = 60 rendered frames, which is 2.00 s at a true 30 Hz and 2.08–2.17 s at the 27.6–28.8 fps this emulator runs, so the measurement sits at the top of the predicted band. 🟑 The Decoder is explicit that this is *consistency, not closure* β€” the guest frame rate was not measured in the same run. ⚠️ **Do not read `captures/focus-ring/ring-20s-mean-uniform.png` as a frame**: the spin averages to a uniform circle, which is the finding, not a headless ring. **The port has not implemented this yet** β€” it still draws 0Β°, which the same corpus says is a pose the game never shows. That is next iteration's work and it is no longer blocked. | -| P3/P5 β€” the title screen | **does the idle post-boot title show the `PRESS β’Ά` plate?** | Q2 | πŸ”΄ the port's boot ends on `title` (build 4), which has **no plate**, and P5 has just made β’Ά the only way off it. Both states are captured β€” `live-title-build4-no-plate.png` and `live-title-press-a.png` β€” so the art is not the question; the **sequence** is: build 4 alone, build 4 with build 2 over it, or build 4 *then* the plate after a delay. Behavioural, so the port has no oracle for it. `press_start` (build 2) is already exported and unused. ⚠️ Fixing it also means drawing **two builds at once**, which this port has never done β€” a change to `ScreenView`, not a line in `flow.json`. Not blocking P5. | +| ~~P3/P5 β€” the title screen~~ | ~~does the idle post-boot title show the `PRESS β’Ά` plate?~~ | Q2 | βœ… **STALE β€” struck 2026-08-30, and it had been wrong for weeks.** Every factual claim in it is now false: the boot does **not** end on a plateless build 4, `press_start` is **not** unused, and the port **has** drawn two builds at once since the plate-delay work. Verified this iteration β€” `boot ends on title + press_start`, `overlay press_start … drew 1: ptbtn00`, plate region mean **95.70** against 33.6 for the bare title. πŸ”΄ The row directly below it was already marked *answered and TAKEN* for the same question: two rows on one question, one struck and one live claiming the opposite, and the live one was the stale one. That is this page's own documented failure mode, caught by auditing it rather than by reading it. ⚠️ What remains open is a **different** question and has its own row: whether the plate *stays up* after its 8-unit window. | | P5 β€” β’· on the main menu | **is β’· what returns to the title, or the idle timer?** | Q5 | 🟑 stated in HANDOFF, no capture behind it. The title self-returns after ~8–10 s idle, so one unrecorded observation cannot separate them. `authored/flow.json` implements it and marks it *authored β€” likely but UNPROVEN*. **Not blocking** β€” P5 shipped with it β€” but it is the only navigation rule on that screen with nothing under it. Settled by one run that presses β’· well inside the idle window, timestamped. | | ~~P6 BGM β€” the sub-wave count~~ | ~~is a music bank's LEADING REGION a stem, or a decoder artefact?~~ | Q10 | βœ… **CLOSED 2026-08-29 β€” the census was right and the port was summing a bank header into the music.** Decoded and timed, sub-wave 0 of `BGM_103`, `BGM_102` and `BGM_001` is identical: **10 300 B β†’ 0.009 s, peak βˆ’inf**, i.e. digitally silent. 10 300 B is the 10 240-byte bank header (the Decoder's disc-wide census) plus a 60-byte RIFF wrapper. So it is not a stem, and `export_bgm` had been counting it in the divisor β€” putting every real stem at 1/3 instead of 1/2, **3.52 dB of attenuation on all menu music shipped since P6**. Dropping a *silent* input is arithmetic, not a decoding decision, so this closed on the port's side; measured after the fix, `main_menu.ogg` goes βˆ’7.69 β†’ **βˆ’4.20 dBFS**, +3.49 dB against 3.52 predicted. Corroborates the Decoder's `c1f3608` from the other direction. The export now reports 2 sub-waves and the manifest warning is gone. | @@ -266,6 +271,11 @@ not, something is still missing. The port cannot tell these apart from inside. ## Questions this port has raised +πŸ”΄ **This table's rows carry no derivation sha, and that is why they rot.** The standing instruction is to record the HANDOFF commit each row was derived from; every *prose* section added since does, and **every row in this table and the next does not**. On 2026-08-30 an audit found three stale rows here β€” one of them contradicted by a struck row four lines below it, claiming the boot ends on a plateless title when it had drawn the plate for weeks. The undated rows are exactly the ones that went stale, which is as close to a controlled experiment as this page is going to get. + +⚠️ Rows are **not** being back-dated: nobody knows when most were written, and inventing a sha would be worse than admitting there is none. New rows carry one. This table was last audited **2026-08-30** against a running port. + + ### ~~Does a keyframe group loop, or hold its last pose?~~ β€” answered **Answered 2026-08-28 by the RE agent: groups hold.** `ptloop01`/`ptloop02` park diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 26f9dfad..aecf4988 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -6445,3 +6445,58 @@ header says a DIFFERS means *"we moved apart, go find out which of us moved"* here neither moved from the game. It stays visible as a DIFFERS rather than being allowed, because an allowance is how the two `title` rows hid inside "six expected" in the first place. + +## Auditing `BLOCKED.md` found three stale rows, and the undated ones were all three + +`BLOCKED.md` opens by warning that it goes stale *"within the hour. It has."* +Nothing audits it. So I read every live row against a running port instead of +against my memory of it. + +### Three were wrong + +**`P3/P5 β€” the title screen`** was the worst, and had been for weeks. Every +factual claim in it is false: the boot does not end on a plateless build 4, +`press_start` is not unused, and the port has drawn two builds at once since the +plate-delay work. Verified this iteration β€” `boot ends on title + press_start`, +`overlay press_start … drew 1: ptbtn00`, plate region mean **95.70** against 33.6 +for the bare title. + +πŸ”΄ And a row **four lines below it** was already marked *answered and TAKEN* for +the same question. Two rows on one question, one struck and one live claiming the +opposite β€” and the live one was the stale one. A reader taking the page at face +value would have believed the struck row was the old news. + +**`P4 β€” is an attract movie skippable`** led with *"the port could not tell which +bug that is"*. It can and it did: `DECISIONS.md` records **β’Ά *does* skip the +intro in this build**, and every boot run since prints `video skipped at …`. +Corrected to 🟑 β€” (a) answered, (b) still needs a capture. + +**`P6 looping`** was not wrong, only thin. It now carries the measured cost: the +bed loops at 87.8 s against the track's 87.7, and the seam is **3.4 seconds of +near-silence**. The missing loop point now has a price expressed as a number +rather than as the adjective "ugly". + +### The undated rows are the ones that rotted + +Checking which sections record the HANDOFF commit they derive from β€” the standing +instruction, and the thing this page's own header demands β€” splits the file +exactly: + +| | sha | +|---|---| +| every prose section added since 2026-08-29 | βœ… all | +| **every row in the two tables** | πŸ”΄ **none** | + +And all three stale rows were in those tables. That is about as close to a +controlled experiment as this page will produce: the rows with no date are the +rows nobody could tell had aged. + +⚠️ I have **not** back-dated them. Nobody knows when most were written and an +invented sha is worse than an admitted absence β€” it would make an undated claim +look checked. Both tables now say so at the top, and carry the date they were last +audited against a running port. + +πŸ“Œ The general form, which is the eleventh of these: *a document that warns about +its own failure mode is not thereby protected from it.* This page has said "it +goes stale, it has" since P1, in a header everyone reads and nobody acts on. The +warning was doing the work of a check.