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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-30 11:23:05 +00:00
parent 7ecbed1d09
commit 7dc5117a09
4 changed files with 120 additions and 5 deletions

View File

@@ -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",

View File

@@ -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.

View File

@@ -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.

View File

@@ -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:<seconds>)"
@@ -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