port: --focus was ignored on the menu path; focus rendering now verified against the oracle
live-main-menu-options-focused.png -- the only capture of a known focus state -- was untestable because --focus= parsed, was stored, and was overwritten by the authored initial focus on every _menu_enter. Every run logged focus ptbtn01 whatever was asked for. Now pushed into the menu model so navigation continues from where it was forced. With it working, each capture picks out exactly one button: ptbtn04 at 0.1355% against 0.70-0.82% for the others on the OPTIONS capture, and ptbtn01 at 0.0705% against 0.72-0.84% on the plain one. 5x and 10x discrimination. First time the port's focus rendering has been checked against the game at all -- the existing main_menu row uses an authored focus and could never have caught a focus error. Records in flow.json that live-main-menu.png shows NEW GAME focused, so the authored initial_focus matches the one frame it can be checked against -- and that this does NOT overturn Q5's measured instability. It stays authored. Adds main_menu_options to verify-capture at 0.13%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -120,7 +120,21 @@
|
||||
"main_menu": {
|
||||
"initial_focus": "ptbtn01",
|
||||
"initial_focus_kind": "authored",
|
||||
"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.",
|
||||
"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.",
|
||||
"",
|
||||
"✅ CONSISTENT WITH THE ONE CAPTURE, measured 2026-08-30. Rendering each of the",
|
||||
"five buttons focused against `live-main-menu.png` gives 0.0705 % for ptbtn01",
|
||||
"and 0.72-0.84 % for the other four -- a 10x discrimination. So that capture",
|
||||
"shows NEW GAME focused, and the authored choice matches it.",
|
||||
"",
|
||||
"⚠️ THIS DOES NOT OVERTURN Q5. Q5 measured initial focus as UNSTABLE across",
|
||||
"four boots; one capture showing ptbtn01 is consistent with that and does not",
|
||||
"contradict it. What the measurement establishes is narrower and still worth",
|
||||
"having: the port's focus rendering is distinctive enough that a capture",
|
||||
"identifies which button is focused, and this authored value is not at odds",
|
||||
"with the only frame we can check it against. It stays AUTHORED."
|
||||
],
|
||||
"on_cancel": {
|
||||
"goto": "title",
|
||||
"goto_name": "TITLE_SCREEN",
|
||||
|
||||
@@ -6054,3 +6054,53 @@ row, which makes it the most sensitive regression detector in the harness.
|
||||
places it, found by sweeping. Choosing which frame to compare against is what
|
||||
every row here does, but the 0.00093 % is a floor for *that pose*, not a general
|
||||
statement of accuracy.
|
||||
|
||||
## `--focus=` did nothing on the menu path, and the corpus had an untested focus capture
|
||||
|
||||
Two unused live captures were sitting in `docs/re/captures/title-builds/`.
|
||||
`live-main-menu-options-focused.png` is the menu with **OPTIONS** focused — the
|
||||
only capture of a *known* focus state — and it was untestable, because
|
||||
`--focus=` **silently did nothing on the `--menu` path**.
|
||||
|
||||
The flag parsed, was stored in `_force_focus`, and was applied to
|
||||
`view.focused_id` at startup — and then `_menu_enter` overwrote it with the
|
||||
authored initial focus on every entry. Every run logged `focus ptbtn01` whatever
|
||||
was asked for, and all five buttons produced the same frame. It is now pushed
|
||||
into the **menu model**, not just the view, so navigation continues from where it
|
||||
was forced rather than jumping back on the first press.
|
||||
|
||||
### The port's focus rendering is right, measured against the oracle
|
||||
|
||||
Rendering each of the five buttons focused, against each capture:
|
||||
|
||||
| focused | vs `live-main-menu-options-focused` | vs `live-main-menu` |
|
||||
|---|---|---|
|
||||
| `ptbtn01` NEW GAME | 0.7352 % | **0.0705 %** |
|
||||
| `ptbtn02` LOAD GAME | 0.8204 % | 0.8378 % |
|
||||
| `ptbtn03` TUTORIAL | 0.7220 % | 0.7365 % |
|
||||
| **`ptbtn04` OPTIONS** | **0.1355 %** | 0.7449 % |
|
||||
| `ptbtn05` EXTRAS | 0.7029 % | 0.7236 % |
|
||||
|
||||
Each capture picks out exactly one button, by **5×** and **10×**. This is the
|
||||
first time the port's focus rendering has been checked against the game at all —
|
||||
the harness's own `main_menu` row uses an *authored* focus, so it could never
|
||||
have caught a focus error.
|
||||
|
||||
### What that settles, and what it does not
|
||||
|
||||
✅ The port draws focus on the right button, distinguishably.
|
||||
✅ `live-main-menu.png` shows **NEW GAME** focused, so the authored
|
||||
`initial_focus: ptbtn01` matches the one frame it can be checked against.
|
||||
|
||||
⚠️ **It does not overturn HANDOFF Q5**, which measured initial focus as *unstable
|
||||
boot to boot* across four runs. One capture showing `ptbtn01` is consistent with
|
||||
instability, not evidence against it. The value stays **authored**, with the
|
||||
agreement recorded beside it. Reading this as "initial focus is settled" would be
|
||||
exactly the over-generalisation from a single observation that this corpus keeps
|
||||
having to withdraw.
|
||||
|
||||
`main_menu_options` joins `verify-capture` at 0.13 %.
|
||||
|
||||
⚠️ Still unused: `live-attract-title-press-a-band.png`, a 1279×**120** strip
|
||||
rather than a full frame. It needs a banded comparison the harness does not do,
|
||||
so it is named here rather than quietly left out.
|
||||
|
||||
@@ -646,6 +646,27 @@ func _menu_enter(name: String, fresh: bool) -> void:
|
||||
return
|
||||
if fresh:
|
||||
_menu.enter(name, view.screen.get("buttons", []))
|
||||
# `--focus=` wins over the authored initial focus, and it did NOT before.
|
||||
#
|
||||
# 🔴 The flag parsed, was stored, and was applied to `view.focused_id` at
|
||||
# startup -- and then this line overwrote it on every `_menu_enter`. So on
|
||||
# the `--menu` path `--focus=` did nothing at all, silently: the run logged
|
||||
# `focus ptbtn01` whatever was asked for.
|
||||
#
|
||||
# That mattered because it made an oracle capture untestable.
|
||||
# `live-main-menu-options-focused.png` is the menu with OPTIONS focused --
|
||||
# the only capture in the corpus of a MEASURED focus state, where the
|
||||
# harness's own `main_menu` row uses an AUTHORED initial focus standing in
|
||||
# for a measurement that says initial focus is unstable (HANDOFF Q5). There
|
||||
# was no way to ask the port for the state the capture shows.
|
||||
#
|
||||
# It is pushed into the MENU MODEL, not just the view, so that navigation
|
||||
# continues from where it was forced rather than jumping back on the first
|
||||
# press.
|
||||
if _force_focus != "" and not _menu.stack.is_empty():
|
||||
var buttons: Array = view.screen.get("buttons", [])
|
||||
if buttons.has(_force_focus):
|
||||
_menu.stack[_menu.stack.size() - 1]["focus"] = _force_focus
|
||||
view.focused_id = _menu.focus()
|
||||
view.queue_redraw()
|
||||
# AUTHORED, and the weakest thing in P6: HANDOFF Q10 says nothing on the disc
|
||||
|
||||
@@ -52,6 +52,14 @@ CAPS=docs/re/captures/title-builds
|
||||
MAP=(
|
||||
"main_menu:$CAPS/live-main-menu.png:menu"
|
||||
"extras:$CAPS/live-extras.png:menu"
|
||||
# The only capture of a MEASURED focus state, and it was unusable until
|
||||
# `--focus=` was made to work on the `--menu` path (it parsed, was stored, and
|
||||
# was overwritten by the authored initial focus on every `_menu_enter`).
|
||||
#
|
||||
# It discriminates: rendering each of the five buttons focused against this
|
||||
# capture gives 0.1355 % for `ptbtn04` and 0.70-0.82 % for the other four. The
|
||||
# port's focus rendering identifies the right button by a factor of five.
|
||||
"main_menu_options:$CAPS/live-main-menu-options-focused.png:focus:ptbtn04"
|
||||
# ⚠️ THE TITLE IS POSED AT t=357.7 UNITS, NOT AT ITS SETTLE, and the time is
|
||||
# MEASURED rather than chosen. The two `ptloop` sweeps are a continuous
|
||||
# animation whose leaf group ends at t=600 with the quads parked off-screen at
|
||||
@@ -104,10 +112,14 @@ echo "REGION -- a missing or misplaced element is a large connected blob."
|
||||
echo
|
||||
printf '%-17s %-9s %-7s %-22s %s\n' screen raw-rmse diff region note
|
||||
for row in "${MAP[@]}"; do
|
||||
IFS=: read -r name cap pose <<<"$row"
|
||||
IFS=: read -r name cap pose forced <<<"$row"
|
||||
if [ ${#want[@]} -gt 0 ] && ! printf '%s\n' "${want[@]}" | grep -qx "$name"; then continue; fi
|
||||
[ -f "$cap" ] || { printf '%-17s %s\n' "$name" "no capture"; continue; }
|
||||
if [ "$pose" = menu ]; then
|
||||
if [ "$pose" = focus ]; then
|
||||
godot --path port --resolution 1280x720 -- "--menu=main_menu" "--focus=$forced" \
|
||||
--script=wait "--shots=$OUT/$name" >"$OUT/$name.log" 2>&1 || true
|
||||
[ -f "$OUT/${name}_00_start.png" ] && cp "$OUT/${name}_00_start.png" "$OUT/$name.render.png"
|
||||
elif [ "$pose" = menu ]; then
|
||||
godot --path port --resolution 1280x720 -- "--menu=$name" --script=wait \
|
||||
"--shots=$OUT/$name" >"$OUT/$name.log" 2>&1 || true
|
||||
[ -f "$OUT/${name}_00_start.png" ] && cp "$OUT/${name}_00_start.png" "$OUT/$name.render.png"
|
||||
|
||||
Reference in New Issue
Block a user