port: the main menu remembers its cursor -- a measured P5 defect, fixed and scoped

Measured by the Decoder today: (B) from the menu to the title and (A) back
returns to the item you left, not to a default; their control passed first, two
delivery-confirmed DOWNs moving the cursor exactly two items before the round
trip. The port reset to initial_focus on every entry, so a player who moved to
EXTRAS, pressed (B) then (A) landed back on NEW GAME. MenuFlow.enter() now
consults opening_focus(), and a new set_focus() writes the memory.

set_focus() exists because two call sites set focus -- a cursor move and (B)'s
restore -- and a memory updated at only one of them is right until the player
uses the other.

focus_persists is true on main_menu and nowhere else, and the scope is the
authored part. wrap generalised because it was measured on two screens; this was
measured on one. Here that is stronger than a preference: extras opens on MISSION
SELECT as a MEASURED initial focus, so a menu-wide memory would have silently
replaced a measured value with a derived one.

Both halves are in one artifact, because a one-sided test passes a port that
quietly generalised: the menu returns to ptbtn05 after the round trip, and extras
opens on ptbtn11 both times despite being left on ptbtn12. contract-check asserts
the pair -- on where measured, off elsewhere -- and fails its known negative.
Eleven checks.

Not assumed: whether the memory survives a reboot, or whether any other screen
has it. Their reach is one boot, one round trip, one direction. The finding also
reframes this morning's initial-focus warning without settling it -- if focus
persists, a reading not taken on a fresh boot's first entry is measuring history.
NEW GAME stays authored, on its own reasoning.

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 21:36:43 +00:00
parent edd5af5be5
commit 23dda95643
5 changed files with 384 additions and 248 deletions

View File

@@ -9,7 +9,7 @@ dies, which is what this file is for.
<!-- INDEX: generated by tools/port/index-decisions -- do not hand-edit -->
240 sections. Search this before re-deriving anything.
241 sections. Search this before re-deriving anything.
* [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28)
* [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28)
@@ -251,6 +251,7 @@ dies, which is what this file is for.
* [The walk is checked too, and "only the ring moves" tested against my own renderer](#the-walk-is-checked-too-and-only-the-ring-moves-tested-against-my-own-renderer)
* [The `+0x08` ask came back answered — and is not consumable yet](#the-0x08-ask-came-back-answered--and-is-not-consumable-yet)
* [The pin moves to `formats-pin-2026-08-30b`, and the port stops owning `+0x08`](#the-pin-moves-to-formats-pin-2026-08-30b-and-the-port-stops-owning-0x08)
* [The menu remembers its cursor — a measured P5 defect, fixed and scoped](#the-menu-remembers-its-cursor--a-measured-p5-defect-fixed-and-scoped)
<!-- /INDEX -->
## P0 — the exporter, 2026-08-28
@@ -12519,3 +12520,60 @@ independent of the crate's. Re-run at the new pin: unchanged, 7 exact and
📌 So the port now holds **one** copy of this reading instead of two, and it is
the copy whose job is to disagree.
## The menu remembers its cursor — a measured P5 defect, fixed and scoped
The Decoder measured it today: Ⓑ from the main menu to the title and Ⓐ back
returns to **the item you left**, not to a default. Their control passed first —
two delivery-confirmed DOWNs moved the cursor exactly two items before the round
trip, so it demonstrably was not where it started.
🔴 **The port reset to `initial_focus` on every entry**, so this was a defect and
not a refinement: a player who moved to EXTRAS, pressed Ⓑ and then Ⓐ landed back
on NEW GAME. `MenuFlow.enter()` now consults `opening_focus()`, and a new
`set_focus()` writes the memory.
**`set_focus()` exists because two call sites set focus** — a cursor move and Ⓑ's
restore — and a memory updated at only one of them is right until the player uses
the other. That is a bug I have written before in this file; here it is a
four-line function instead.
### 🔴 The scope is the authored part, and widening it would contradict a measurement
`focus_persists` is true on `main_menu` and **nowhere else**. The measurement is
of one screen. `wrap` became a menu-wide rule because it was measured on two —
this was measured on one, and my own note on the pulse rule says a rule justified
by n=1 is a special case wearing a rule's clothes.
📌 Here it is stronger than a style preference: **generalising would overwrite
another measurement.** `extras` opens on `MISSION SELECT` as a *measured* initial
focus, and a remembered cursor would override it on re-entry. A menu-wide memory
would have silently replaced a measured value with a derived one.
Both halves are checked in one artifact, because a one-sided test passes a port
that quietly generalised:
```
--script=down,down,down,down,cancel,accept
menu on main_menu, focus ptbtn01 → ptbtn05
menu on title …
menu on main_menu, focus ptbtn05 ← remembered
--script=down,down,down,down,accept,down,cancel,accept
menu on extras, focus ptbtn11 → ptbtn12
menu on main_menu, focus restored to ptbtn05
menu on extras, focus ptbtn11 ← NOT remembered, measured value wins
```
`contract-check` asserts the pair — on where it was measured, off everywhere
else — and fails its known negative. **Eleven checks now.**
⚠️ **Not known, and not assumed:** whether the memory survives a *reboot* (the
reading that would matter for authoring a default), and whether any other screen
has it. The Decoder marks the reach as one boot, one round trip, one direction.
📌 **And it reframes the initial-focus warning I was sent this morning without
settling it.** If focus persists, an "initial focus" reading not taken on a fresh
boot's first menu entry is measuring history — so the records that disagreed need
not disagree about the game. My `NEW GAME` stays **authored**, on its own
reasoning. Nothing here confirms it.