From 7dc5117a0991ab0de84ec47f78eb094a89db065d Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Sun, 30 Aug 2026 11:23:05 +0000 Subject: [PATCH] port: the PRESS A plate comes back after B, and it did not here The Decoder measured that after B from the menu the plate is re-drawn (daf8f47). The port landed on a BARE title: _menu_arrive drops the overlay -- correct, the plate goes with the screen it was measured on -- but nothing put it back, since _overlay_spec is cleared the instant the overlay is raised and only the boot sequence ever set it. Confirmed by running it: ten title elements, no ptbtn00. _rearm_overlay_for reads the declaration out of authored/flow.json's boot step for that screen rather than naming press_start, so the plate returns by the same path and shared clock as on boot, with NO new constant. Controlled both ways: cancel raises it, entering EXTRAS (no overlay declared) raises nothing. Script steps now carry an elapsed timestamp, without which the port's own press latency could not be compared to a measured one. With it: B at 1.01 s, title at 1.37 s = 0.36 s, against their measured <= 0.4 s -- nothing was fitted to that. The plate's RETURN time does not agree: 4.33 s here against their 7.3 s to pulse, of which up to 2 s is pulse-detection phase. Not adjusted -- an authored delay in this same block was already refuted once. Recorded as an ask. auto_repeat and title/on_cancel upgraded to MEASURED on their run. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- authored/flow.json | 4 +-- docs/port/BLOCKED.md | 19 +++++++++++-- docs/port/DECISIONS.md | 60 ++++++++++++++++++++++++++++++++++++++++++ port/scripts/boot.gd | 42 ++++++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/authored/flow.json b/authored/flow.json index 0c86b49b..ae7e3c30 100644 --- a/authored/flow.json +++ b/authored/flow.json @@ -90,7 +90,7 @@ "input_during_transition": "ignored", "input_during_transition_why": "AUTHORED, and NOT measured -- nobody has watched what the game does with a button pressed mid-fade. Ignoring is the choice that invents the least: it cannot queue a press the game might have dropped. Ask the RE agent before relying on it.", "auto_repeat": false, - "auto_repeat_why": "AUTHORED, NOT MEASURED -- and it was previously not stated at all, which is worse: the port is edge-triggered (is_action_pressed in boot.gd _input), so holding a direction moves ONE item, and that behaviour was an unexamined consequence of how the handler was written rather than a claim anyone could check. HANDOFF Q5 prefixes its whole row '**measured**', but in the linked source (menu-navigation-semantics.md, Decoder 86a8ce7) the 'up / down' row -- 'one item per press, no auto-repeat at the durations tried' -- has an EMPTY evidence cell. Note the split: ONE ITEM PER PRESS is evidenced indirectly and well, because the wrap montage's count only comes out if each press moves one (4 presses from EXTRAS landing on OPTIONS). NO AUTO-REPEAT is the part with nothing behind it, and the source's own 'at the durations tried' hedges it. So the port keeps one-step-per-press on good evidence and holds auto_repeat false as a CHOICE: a repeat we did not implement cannot run a menu past the item the player wanted, whereas inventing one could." + "auto_repeat_why": "MEASURED 2026-08-30, Decoder daf8f47: a 2.0 s held (down) moves the cursor EXACTLY ONCE. Their counter passes its own control first -- a single 0.12 s tap gives exactly 1 spike, the hold gives 1, move spike 0.0202-0.0220 against a 0.0003-0.0038 floor. The port's edge-triggered _input already behaved this way; what changed is that it is now a MEASUREMENT rather than an unexamined consequence of how the handler was written. HANDOFF Q5's 'up / down' row is split at the source: one-item-per-press (evidenced by the 4-press wrap count) from no-auto-repeat (which had nothing until this run)." }, "screens": { "_": [ @@ -117,7 +117,7 @@ "why": "MEASURED, HANDOFF: (A) on the title opens the main menu, with (A) on the boot title as the control in the same run." }, "on_cancel": null, - "on_cancel_why": "AUTHORED, NOT MEASURED -- and this cell used to say 'MEASURED, HANDOFF Q5'. HANDOFF Q5 (9ca1eb5) prefixes its whole row '**measured**' and then lists six clauses, and the linked source (menu-navigation-semantics.md, Decoder 86a8ce7) gives this one an evidence cell reading 'none' and a yellow marker. The Decoder says so directly: their 2026-08-30 run CANNOT be counted, because the second B was delivered during the title's build-in, so the glyph 0 -> 154 that followed is the build-in completing rather than a response. A run that answers this has to wait for the title to SETTLE first. The VALUE is unchanged and stays null: doing nothing is the safe reading either way, so this correction moves no pixel -- it removes a MEASURED stamp from a claim whose own source records no evidence." + "on_cancel_why": "MEASURED 2026-08-30, Decoder daf8f47, docs/re/data/nav-autorepeat-and-settled-b.txt: twenty seconds after a delivery-confirmed B the screen is still the title with PRESS (A) BUTTON up. The run waited for the PLATE PULSE -- the title's own settled signature -- before pressing, which is exactly what the earlier confounded attempt did not. This cell briefly said 'MEASURED, HANDOFF Q5' on no evidence, then said AUTHORED once that was caught; it is now measured for real. Value unchanged throughout: null." }, "main_menu": { "initial_focus": "ptbtn01", diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index d174da32..05aee79c 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -753,7 +753,7 @@ maximum adjacent-sample step of **212** against a 99.9th percentile of **3 737** so the join is not a click. **It does not discriminate the two readings** — a cut near a zero crossing is smooth wherever it falls. -### Ⓑ on the title — what happens, actually measured +### ~~Ⓑ on the title — what happens, actually measured~~ — ANSWERED 2026-08-30 **Derived from HANDOFF `9ca1eb5`; Decoder branch `86a8ce7`.** @@ -770,7 +770,7 @@ request for a method. default and the port ships it either way, so nothing is blocked — this is a provenance repair, not a stall. -### Auto-repeat on a held direction +### ~~Auto-repeat on a held direction~~ — ANSWERED 2026-08-30: none **Derived from HANDOFF `9ca1eb5`; Decoder branch `86a8ce7`.** @@ -795,3 +795,18 @@ gives the quad's ramp as 10 units (0.17 s); 10 + your 10-14 = 20-24 units = constant is needed anywhere and my 9 is simply the wrong side of your boundary. The two readings differ in whether a screen is still drawing during the last 0.2 s. + +### How long after Ⓑ does the `PRESS Ⓐ` plate come back? + +**Derived from Decoder `daf8f47`.** + +They measured press 351.2 s -> pulse back 358.5 s, i.e. **7.3 s**. The port +raises the plate on arrival and its own group takes it opaque at t=238, giving +**press -> plate visible 4.33 s**. Pulse detection can lag first paint by up to +one 120-unit period (2 s), which closes the gap to ~6.3 s at most and leaves +roughly a second unaccounted for. + +**The ask:** is 358.5 s the plate's FIRST paint or the first pulse peak your +detector could see? If the latter, what is the first-paint time? I am not +authoring a delay to close this - a `2.13 s` authored delay in this same block +was already refuted once by arithmetic. diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 446effc8..9a6f107d 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -8448,3 +8448,63 @@ is wrong rather than tune until they match — and here the game measurement sho win over the draw-stream one on principle, but the gap is small enough that it could equally be where each puts the boundary between ramp and hold. It goes to `BLOCKED.md` as an ask, at the value it was measured at. + +## The plate came back in the game and not in the port + +The Decoder's Ⓑ run answered both my asks and threw in a third finding: **after Ⓑ +from the menu the `PRESS Ⓐ` plate is re-drawn** — pressed 351.2 s, pulse back +358.5 s (`daf8f47`). + +🔴 **The port did not do that.** Ⓑ landed on a *bare* title. `_menu_arrive()` +calls `_drop_overlay()` — correct, the plate goes with the screen it was measured +on — but nothing ever put it back: `_overlay_spec` is cleared the instant the +overlay is raised, and only the boot sequence ever set it. Confirmed by running +it, not by reading: the drawn list was the ten title elements with no `ptbtn00`. + +✅ Fixed. `_rearm_overlay_for(name)` looks the declaration up in +`authored/flow.json`'s **boot step for that screen** rather than naming +`press_start`, so the plate returns by the same code path and the same shared +clock as on boot, and a screen that gains an overlay later gets it on both paths +with no edit here. **No new constant** — the delay is not authored, it is +whatever the boot already does. + +Controlled both ways: Ⓑ → `overlay press_start raised`, drawing `ptbtn00`, +`ptbtn00f`; entering `EXTRAS`, which declares no overlay, raises **nothing**. + +### An independent agreement I did not tune for + +The script log had no press timestamp, so the port's own latency could only be +guessed from surrounding lines. Added one. With it: + +| | | +|---|---| +| Ⓑ pressed | 1.01 s | +| title arrives, overlay armed | 1.37 s | +| **port's press → title** | **0.36 s** | +| **their measured Ⓑ latency** | **≤ 0.4 s** | + +✅ That agreement is worth something because **nothing here was fitted to it** — +the port's transition timing comes from the screens' own fade keyframes, and this +is the first time the two numbers have been put beside each other. + +### 🟡 The plate's return time does not agree, and I am not adjusting it + +The plate is raised on arrival and its own group takes it opaque at t=238 +(3.97 s), so the port's **press → plate visible ≈ 4.33 s**. Theirs is **7.3 s to +the pulse**. The pulse has a 120-unit (2 s) period, so pulse *detection* can lag +first paint by up to 2 s — which closes it to ~6.3 s at most and leaves roughly +**a second unexplained**. + +⚠️ It would be easy to author a delay that makes 4.33 into 7.3. That is exactly +the tuning this corpus keeps warning about, and the previous authored delay in +this very block (`after_settle_seconds: 2.13`) was already refuted once by +arithmetic. Left alone; recorded as an ask. + +### Two stamps upgraded, both now measured for real + +* `navigation.auto_repeat` — a 2.0 s held ⬇ moves the cursor **once**, their + counter passing its control first. Was a consequence of edge-triggered + `_input`; now a measurement. +* `title/on_cancel` — Ⓑ on a **settled** title does nothing, twenty seconds + confirmed. This cell has now been `MEASURED` (wrongly), `AUTHORED` (honestly), + and `MEASURED` (truly), with the value `null` the whole way through. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index 234ac8c2..67890af5 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -731,6 +731,22 @@ func _menu_arrive() -> void: push_error(view.tree.error) get_tree().quit(2) return + # 🔴 THE PLATE COMES BACK IN THE GAME, AND IT DID NOT HERE. + # + # `_drop_overlay()` at the top of this function is right -- the plate goes + # with the screen it was measured on -- but nothing ever put it back, so Ⓑ + # from the main menu landed on a BARE title. `_overlay_spec` is cleared the + # moment the overlay is raised, and only the boot sequence ever sets it. + # + # MEASURED by the Decoder 2026-08-30 (branch `auto/no-disc-and-menu-captures`, + # `docs/re/data/nav-autorepeat-and-settled-b.txt`): after Ⓑ from the menu the + # plate IS re-drawn -- pressed at 351.2 s, its pulse back at 358.5 s. + # + # ⚠️ No new constant. The delay is not authored here and must not be: the + # overlay declaration is read back out of `authored/flow.json`'s boot step + # for this screen, so the plate re-appears by the SAME path, with the same + # shared clock, as it does on boot. Whatever the boot does, the return does. + _rearm_overlay_for(name) var buttons: Array = view.screen.get("buttons", []) if action.get("pop", false): # MEASURED, HANDOFF Q5: (B) restores the focus you came from. @@ -899,7 +915,11 @@ func _run_script() -> void: elif token == "wait": pass elif SCRIPT_ACTIONS.has(token): - print("script[%d] %s" % [i + 1, token]) + # The elapsed clock goes in the line because a press with no timestamp + # cannot be compared against a MEASURED latency. The Decoder's Ⓑ + # figures are press-to-effect times; without this the port's own + # press time had to be guessed from the surrounding lines. + print("script[%d] %s at %.2f s" % [i + 1, token, _elapsed]) _press(String(SCRIPT_ACTIONS[token])) else: push_error("--script: no such step %s (have %s, wait, wait:)" @@ -1107,6 +1127,26 @@ func _overlay_process(delta: float) -> void: _raise_overlay(String(_overlay_spec.get("screen", ""))) +## Re-arm the overlay a screen declares in the authored boot sequence. +## +## Used when the MENU arrives at a screen, not just when the boot walks onto it. +## It looks the declaration up rather than naming `press_start`, so a screen that +## gains an overlay in `authored/flow.json` gets it on both paths at once and +## this function needs no edit. +func _rearm_overlay_for(name: String) -> void: + if _flow == null or not (_flow as Dictionary).has("boot"): + return + for step: Dictionary in _flow["boot"]: + if String(step.get("screen", "")) != name: + continue + var spec: Variant = step.get("overlay", null) + if typeof(spec) == TYPE_DICTIONARY: + _overlay_spec = spec + _overlay_due = _elapsed + _overlay_process(0.0) + return + + ## Composite a second build over the first. ## ## It starts at `time_units = 0` and plays its OWN group, so the plate rises and