port: correct a check that asserted an absence of measurement as a finding

The pair I shipped this iteration -- focus_persists on for main_menu, off
everywhere else -- reported both halves as agreement with the contract. Nothing
measured that extras does not persist. The corpus has EXTRAS' opening item from
one entry and (B) restoring the PARENT's focus 4/4; neither says what a submenu's
own cursor does on re-entry. Caught by the Decoder.

It is the mirror of the trap it was written to avoid. I refused to let a derived
menu-wide rule overwrite a measured value, then let 'not measured here' become a
positive assertion of the negative. Both treat a gap in the corpus as if it
carried information and differ only in which direction they fill it. And the
failure mode was the bad one: if the game does persist EXTRAS, the check holds
the port to the wrong behaviour and passes while doing it.

check_focus_persists now asserts only the measured half. The scope became a
separate guard with its own outcome word -- 'only main_menu, AUTHORED DEFAULT,
unmeasured elsewhere' -- which still fails if widened, since that should be a
deliberate edit, but can no longer be read as the game being known to reset.
focus_persists_why records the correction rather than being rewritten.

It also weakens a label. EXTRAS' initial_focus is marked measured and was taken
on a single entry; now that the main menu is known to remember its cursor, a
one-entry reading of any screen may be measuring history rather than what the
screen opens on -- the same objection that reframed the TUTORIAL/NEW GAME
disagreement. The observation stands, its reading as an initial focus does not.
Caveat attached, kind left as measured with a note that it changes if EXTRAS
turns out to persist.

Not building on the non-persistence half until their EXTRAS re-entry run returns.

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:39:08 +00:00
parent 6e3347a338
commit b3da5c1d48
3 changed files with 127 additions and 12 deletions

View File

@@ -144,7 +144,24 @@
"",
"⚠️ WHAT IS NOT KNOWN: whether the memory survives a return to the BOOT",
"(as opposed to the title), and whether any other screen has it. Ask before",
"widening this."
"widening this.",
"",
"🔴 CORRECTED 2026-08-30, SAME DAY, by the Decoder: the paragraph above argued",
"the scope from `extras` having a MEASURED initial focus that a remembered",
"cursor would override. That is a good reason to be CAUTIOUS and NOT a finding",
"that `extras` resets. Nothing has measured what a submenu's own cursor does on",
"re-entry: the corpus has EXTRAS' opening item from ONE entry, and (B) restoring",
"the PARENT's focus 4/4, and neither answers it.",
"",
"So `focus_persists: false` everywhere else is THE PORT'S DEFAULT, not the",
"game's behaviour. It invents the least and it preserves the one measurement",
"there is. `tools/port/contract-check` asserts only the main-menu half against",
"the contract and reports the scope as a GUARD, because for one iteration it",
"asserted non-persistence as though it had been measured -- which would have",
"held the port to the wrong behaviour and passed while doing it.",
"",
"❔ The Decoder is measuring EXTRAS re-entry now. Do not build on the",
"non-persistence half until it returns."
],
"initial_focus_why": [
"AUTHORED, standing in for HANDOFF Q5, which measured that initial focus is NOT STABLE: four boots of the same harness opened on TUTORIAL, TUTORIAL, NEW GAME, NEW GAME. A port has to open on something. ptbtn01 (NEW GAME) is picked because it is one of the two states actually observed and it is the top item, so a reader can predict it. It is a CHOICE. Delete this the day the RE agent finds what selects it. CORROBORATED 2026-08-29, and still not decoded: the committed capture live-main-menu.png has NEW GAME focused. Identified by rendering all five focus states and taking the minimum difference -- 531 differing pixels against 6080-7094 for the others, an 11.5x margin -- with the method controlled on live-main-menu-options-focused.png, whose answer is in its filename and which it picks by 4.7x. That means the port's choice matches the state of one committed frame. It does NOT make focus stable: Q5's four boots gave TUTORIAL, TUTORIAL, NEW GAME, NEW GAME, and this identifies one frame rather than a rule. Delete this entry the day something says what SELECTS it. TIGHTENED 2026-08-29: Q5 now has SIX boots, and the shape is sharper than 'unstable' -- TUTORIAL x3, NEW GAME x3, and NO OTHER ITEM EVER OBSERVED. So it is not uniform over five buttons; whatever selects it has to explain a two-way split. That does not change this choice (NEW GAME remains one of exactly two observed states, and it is the state of the committed capture) but it does change what would REFUTE it: a boot opening on LOAD GAME, OPTIONS or EXTRAS would break the two-way shape, and a rule that predicts the split would delete this entry outright.",
@@ -223,7 +240,20 @@
"extras": {
"initial_focus": "ptbtn11",
"initial_focus_kind": "measured",
"initial_focus_why": "MEASURED, unlike the main menu's: EXTRAS opens focused on MISSION SELECT (live-extras.png). It is authored here only because there is nowhere else to put a measurement -- it is not a choice.",
"initial_focus_why": [
"MEASURED, unlike the main menu's: EXTRAS opens focused on MISSION SELECT (live-extras.png). It is authored here only because there is nowhere else to put a measurement -- it is not a choice.",
"",
"⚠️ WEAKENED 2026-08-30 -- the OBSERVATION stands, its reading as an INITIAL",
"focus does not. It was taken on a single entry. Now that the main menu is known",
"to remember its cursor across a round trip, a one-entry reading of any screen",
"may be measuring HISTORY rather than what the screen opens on -- the same",
"objection that reframed the main menu's TUTORIAL/NEW GAME disagreement.",
"",
"Kept as `measured` because the frame really does show MISSION SELECT focused,",
"and kept as the port's opening item because it is the only reading there is.",
"🔴 If EXTRAS turns out to persist, this becomes history and the kind must",
"change with it."
],
"on_cancel": {
"goto": "main_menu",
"goto_name": "TITLE_MENU",

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 -->
241 sections. Search this before re-deriving anything.
242 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)
@@ -252,6 +252,7 @@ dies, which is what this file is for.
* [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)
* [🔴 Correction, same day: I encoded an absence of measurement as a finding](#correction-same-day-i-encoded-an-absence-of-measurement-as-a-finding)
<!-- /INDEX -->
## P0 — the exporter, 2026-08-28
@@ -12577,3 +12578,58 @@ settling it.** If focus persists, an "initial focus" reading not taken on a fres
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.
## 🔴 Correction, same day: I encoded an absence of measurement as a finding
The check I shipped this iteration asserted a **pair** — `focus_persists` on for
`main_menu`, off everywhere else — and called both halves agreement with the
contract. The Decoder caught it: **nothing measured that `extras` does not
persist.** The corpus has EXTRAS' opening item from *one entry* and Ⓑ restoring
the *parent's* focus 4/4. Neither says what a submenu's own cursor does on
re-entry.
📌 **It is the exact mirror of the trap I had just congratulated myself on
avoiding.** I refused to let a derived menu-wide rule overwrite a measured value
— and then let *"not measured here"* become a positive assertion of the negative.
Both errors treat a gap in the corpus as if it carried information; they only
differ in which direction they fill it.
And the failure mode was the bad one: **if the game does persist EXTRAS, the
check holds the port to the wrong behaviour and passes while doing it.** A wrong
assertion that fails is a nuisance; a wrong assertion that passes is a
manufactured fact.
### What changed
* `check_focus_persists` now asserts **only the measured half** against the
contract.
* The scope is a separate **`guard`** with its own outcome word, printing
`only main_menu -- AUTHORED DEFAULT, unmeasured elsewhere`. It still fails if
someone widens it, because that should be a deliberate edit with a `why` — but
a passing run can no longer be read as the game being known to reset.
* `focus_persists_why` records the correction rather than being rewritten, and
says the non-persistence half is **the port's default, not the game's
behaviour**.
### And it weakens a `kind` label I had been leaning on
EXTRAS' `initial_focus: ptbtn11` is marked `measured`, and the same objection
applies to it: **it was taken on a single entry.** Now that the main menu is known
to remember its cursor, a one-entry reading of any screen may be measuring
*history* rather than what the screen opens on — the same argument that reframed
the main menu's TUTORIAL-versus-NEW-GAME disagreement this morning.
The observation stands; its *reading* as an initial focus does not. Left as
`measured` with the caveat attached, because the frame really does show MISSION
SELECT focused and it is the only reading there is — flagged so that if EXTRAS
turns out to persist, the label changes with it.
⚠️ **Not building further on the non-persistence half** until their EXTRAS
re-entry run comes back.
📌 Their sharpening of the pins point is the general form of all of this: **the
disciplines that fail this way are the ones that never visibly failed.**
Delivery-confirmation went to Ⓐ and Ⓑ because those broke once; the d-pad had
always quietly worked, so nothing directed attention at it. `kind: "measured"` is
the same shape — it has never visibly failed, so nothing has been checking what
each instance of it actually rests on.

View File

@@ -219,17 +219,46 @@ def flow_buttons(screen):
def check_focus_persists(h):
"""The menu remembers its cursor -- measured on the MAIN MENU, one screen.
"""The menu remembers its cursor -- MEASURED, on the main menu, one screen.
Checked as a pair: the behaviour must be on where it was measured and off
everywhere else. A one-sided check would pass a port that had quietly made it
a menu-wide rule, which would override `extras`' measured initial focus.
🔴 This checked a PAIR until 2026-08-30: on for `main_menu`, off everywhere
else. The second half asserted that `extras` does NOT persist, and **nothing
measured that**. What the corpus has is EXTRAS' initial focus from a single
entry and Ⓑ restoring the PARENT's focus 4/4 — neither says what a submenu's
own cursor does on re-entry. So one measured behaviour and one absence of a
measurement were being reported identically, and if the game does persist
EXTRAS the check would have held the port to the wrong behaviour AND PASSED.
The mirror of the trap it was written to avoid: refusing to let a derived
rule overwrite a measured value, then letting "not measured here" become a
positive assertion of the negative. Now only the measured half is asserted
against the contract; the scope is a guard, below.
"""
want = bool(re.search(r"the main menu remembers its cursor; re-entry is not a reset", h))
scr = (jload("authored/flow.json") or {}).get("screens") or {}
on = [n for n, v in scr.items() if isinstance(v, dict) and v.get("focus_persists")]
report("menu remembers its cursor", want or None, on == ["main_menu"] and on,
want and on == ["main_menu"])
got = (((jload("authored/flow.json") or {}).get("screens") or {})
.get("main_menu", {}).get("focus_persists"))
report("menu remembers its cursor", want or None, got, want and got is True)
def guard_focus_scope(_h):
"""NOT a contract check. A regression guard on an AUTHORED DEFAULT.
No screen but `main_menu` persists its cursor in this port, and that is the
port's choice, not a finding: it preserves EXTRAS' measured opening item and
invents the least. Guarded so that widening it is a deliberate edit with a
`why`, and labelled so a passing run cannot be read as the game being known
to reset.
"""
global FAIL
on = sorted(n for n, v in ((jload("authored/flow.json") or {}).get("screens") or {}).items()
if isinstance(v, dict) and v.get("focus_persists"))
if on == ["main_menu"]:
print(f" {'focus_persists scope':<30} guard only main_menu"
f" -- AUTHORED DEFAULT, unmeasured elsewhere")
else:
FAIL += 1
print(f" {'focus_persists scope':<30} 🔴 GUARD {on} -- widened past the"
f" one screen measured; needs a why and a measurement")
def check_menu_labels(_h):
@@ -295,7 +324,7 @@ def main():
for fn in (check_fade_quads, check_fade_out, check_plate_period,
check_bgm_window, check_black_hold, check_menu_bank,
check_splash_dwell, check_menu_labels, check_extras_labels,
check_wrap, check_focus_persists):
check_wrap, check_focus_persists, guard_focus_scope):
fn(h)
print()
print(" A passing run means the port agrees with the contract ON THESE VALUES.")