port: P5 end to end -- and the port's title never says PRESS (A)
The P5 gate walk starts on a screen. This runs the whole objective instead, and
it is the only thing that would have found what it found:
xvfb-run -a godot --path port -- --boot --play \
--script=accept,down,down,down,down,accept,cancel,cancel --shots=/tmp/e2e
publisher wordmark -> developer logos -> ADV (151.9 s) -> title -> (A) -> main
menu -> navigate -> (A) -> EXTRAS -> (B) with focus restored to ptbtn05 -> (B)
-> title. 166.76 s, exit 0, nine frames. Shared as 1788003274-e68367e787d5.
THE PORT'S TITLE DOES NOT TELL THE PLAYER TO PRESS (A). The boot's last step is
`title` = GP_TITLE build 4, and build 4 has NO `PRESS (A) BUTTON` plate. P5 has
just made (A) the only way off that screen.
Not a guess about the art -- both states are captured off the running game and
differ by exactly that plate (live-title-build4-no-plate.png vs
live-title-press-a.png), and the plate is ALREADY EXPORTED as `press_start`,
build 2, sitting in export/screens/title/ unused by anything.
RECORDED, NOT FIXED, and the distinction is the point. This is P3's gate that
P5 exposed, and fixing it needs two things the port does not have:
* WHICH state an idle post-boot title shows -- build 4 alone, build 4 with the
plate over it, or build 4 THEN the plate after a delay -- is BEHAVIOURAL.
The game demonstrably has both states and nothing says which follows the
intro. The port has no oracle for a sequence; that is the Decoder's.
* showing it means DRAWING TWO BUILDS AT ONCE, which this port has never done
-- every mode loads exactly one screen. That is a change to ScreenView, not
a line in flow.json, and it is not being smuggled in under a navigation
milestone on the strength of "it looks more right".
Filed in BLOCKED.md. P5's gate is (A) into a submenu and (B) back; both work.
Two smaller things the same run found, both fixed:
* the boot step's `why` still said "nothing takes the title's place until P5
gives it somewhere to go". P5 has. Now says what is true: `--boot` STOPS on
the title (a boot that ends by fading to black looks like a crash) and
`--play` HANDS THE HELD TITLE OVER -- the stop is not a bug and the handover
is not another boot step.
* an empty focus printed as a line that trailed off, reading like a value had
gone missing rather than like there is none. The title is a screen with no
`buttons` that still takes (A), so it now prints
"(none -- this screen has no focusable item)".
Also confirmed: entering a submenu directly (`--menu=extras`) and pressing (B)
enters the parent at its AUTHORED initial focus, not a restored one. There is no
history to restore and MenuFlow.cancel only claims a restored focus when the
stack agrees about where it is going.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtmUw5N5LJaMW1Njb8Ziey
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
},
|
||||
{
|
||||
"screen": "title",
|
||||
"why": "HANDOFF Q2/Q6: the boot reaches the title after the intro movie. The port holds here -- nothing takes the title's place until P5 gives it somewhere to go."
|
||||
"why": "HANDOFF Q2/Q6: the boot reaches the title after the intro movie. This is the LAST step, and a last step is where the sequence stops rather than fading out -- a boot that ends by fading to black looks like a boot that crashed. P5 gave the title somewhere to go, but that is a HANDOVER and not another boot step: `--boot` still stops here, and `--boot --play` hands the same held title to the menu flow, where (A) opens TITLE_MENU. Kept as a stop rather than folded into `screens` because what the boot does is authored from a measured sequence, and what (A) does is a separate measurement."
|
||||
}
|
||||
],
|
||||
"dwell": {
|
||||
|
||||
@@ -66,6 +66,7 @@ git log -1 --format=%h -- docs/port/HANDOFF.md # newer than 9ca1eb5? re-reconc
|
||||
| P6 audio | which BGM the menu plays | Q10 | ❔ **not on the disc.** All 32 banks are named `BGM_001`…`BGM_109` with no semantic name anywhere. The port is choosing a track, and that choice is authored. |
|
||||
| P6 looping | where a menu loop restarts | Q10 | ❔ `BGM_001` fades out at 167.663 s into 6.15 s of silence, and no loop-point field has been identified. A menu loop is authored. |
|
||||
| P5 focus marker | **the focus ring's spin PERIOD, and whether it loops** | Q1 + the 2026-08-28 *"groups hold"* answer | ❔ open, and the port is drawing a pose it knows is wrong. Derived at HANDOFF `9ca1eb5` **plus** `7eeae30`, which HANDOFF does not yet carry. `ptbtneff01` declares `t=120, rot 0` then an **untimed** `rot 360`. The port measured the two oracle captures at **~76°** and **~210°** — 134° apart, peak corr 0.968, null control 0.369 (`DECISIONS.md`) — so **0° is not a pose the game shows**, and the port draws 0° because a spin rate would be invented. Two unknowns, both the Decoder's: (a) under Q1's *replicated* reading `t=120` is when the **next** pose is reached, giving one revolution in 2.0 s, but this port's `pose_at` implements the other reading and switching it changes every screen's animation timing; (b) *"groups hold"* predicts a stop at 360 = 0, which contradicts both captures. **What settles it: two frames of one focused button a known time apart.** |
|
||||
| P3/P5 — the title screen | **does the idle post-boot title show the `PRESS Ⓐ` plate?** | Q2 | 🔴 the port's boot ends on `title` (build 4), which has **no plate**, and P5 has just made Ⓐ the only way off it. Both states are captured — `live-title-build4-no-plate.png` and `live-title-press-a.png` — so the art is not the question; the **sequence** is: build 4 alone, build 4 with build 2 over it, or build 4 *then* the plate after a delay. Behavioural, so the port has no oracle for it. `press_start` (build 2) is already exported and unused. ⚠️ Fixing it also means drawing **two builds at once**, which this port has never done — a change to `ScreenView`, not a line in `flow.json`. Not blocking P5. |
|
||||
| P5 — Ⓑ on the main menu | **is Ⓑ what returns to the title, or the idle timer?** | Q5 | 🟡 stated in HANDOFF, no capture behind it. The title self-returns after ~8–10 s idle, so one unrecorded observation cannot separate them. `authored/flow.json` implements it and marks it *authored — likely but UNPROVEN*. **Not blocking** — P5 shipped with it — but it is the only navigation rule on that screen with nothing under it. Settled by one run that presses Ⓑ well inside the idle window, timestamped. |
|
||||
|
||||
## Answered since this file was last written — no longer blocking
|
||||
|
||||
@@ -1138,3 +1138,84 @@ and both belong to the Decoder:
|
||||
|
||||
Filed in `BLOCKED.md` and asked over the message channel. What settles it is two
|
||||
frames of one focused button a known time apart.
|
||||
|
||||
---
|
||||
|
||||
## P5 end to end — and the title does not say `PRESS Ⓐ`, 2026-08-29
|
||||
|
||||
The gate walk above starts on a screen. This is the whole thing, unattended, in
|
||||
one run — the sequence PORT-MISSION names as the objective:
|
||||
|
||||
```sh
|
||||
xvfb-run -a godot --path port -- --boot --play \
|
||||
--script=accept,down,down,down,down,accept,cancel,cancel --shots=/tmp/e2e
|
||||
```
|
||||
|
||||
```
|
||||
screen publisher_logo … settles at t=235 (3.917 s)
|
||||
-> developer_logos at 4.70 s
|
||||
-> video ADV at 8.60 s
|
||||
video ended at 151.91 s
|
||||
-> title at 151.91 s
|
||||
boot sequence complete after 156.30 s, holding on title
|
||||
menu on title
|
||||
script[1] accept (A) -> main_menu
|
||||
…
|
||||
script[6] accept (EXTRAS) -> extras
|
||||
script[7] cancel (B) -> main_menu focus restored to ptbtn05
|
||||
script[8] cancel (B) -> title
|
||||
script complete after 166.76 s on title
|
||||
```
|
||||
|
||||
Publisher wordmark → developer logos → `ADV` → title → Ⓐ → main menu →
|
||||
navigate → Ⓐ → `EXTRAS` → Ⓑ (focus restored) → Ⓑ → title. Contact sheet shared.
|
||||
|
||||
Two smaller things this run found, both fixed here:
|
||||
|
||||
* the boot step's `why` in `authored/flow.json` still said *"nothing takes the
|
||||
title's place until P5 gives it somewhere to go"*. P5 has. Rewritten to say
|
||||
what is actually true — `--boot` still **stops** on the title, and `--play`
|
||||
**hands the held title over**; the stop is not a bug and the handover is not
|
||||
another boot step.
|
||||
* an empty focus printed as a line that trailed off, which reads like a value
|
||||
went missing rather than like there is none. The title is a screen with no
|
||||
`buttons` that still takes Ⓐ, so it prints
|
||||
`(none -- this screen has no focusable item)`.
|
||||
|
||||
Also confirmed on the way: entering a submenu **directly** (`--menu=extras`) and
|
||||
pressing Ⓑ enters the parent at its authored initial focus, not at a restored
|
||||
one — there is no history to restore, and `MenuFlow.cancel` only claims a
|
||||
restored focus when the stack agrees about where it is going.
|
||||
|
||||
### 🔴 The port's title does not tell the player to press Ⓐ
|
||||
|
||||
Found by running the objective end to end, which is the only thing that would
|
||||
have found it: the boot's last step is `title` (build 4), and **build 4 has no
|
||||
`PRESS Ⓐ BUTTON` plate**. P5 has now made Ⓐ the only way off that screen.
|
||||
|
||||
This is not a guess about the art. Both states are captured off the running
|
||||
game and they differ by exactly that plate:
|
||||
|
||||
| | capture |
|
||||
|---|---|
|
||||
| title **without** the plate | `title-builds/live-title-build4-no-plate.png` |
|
||||
| title **with** the plate | `title-builds/live-title-press-a.png` |
|
||||
|
||||
And the plate is already exported — `press_start`, `GP_TITLE` build 2 (HANDOFF
|
||||
Q2), sitting in `export/screens/title/` unused by anything.
|
||||
|
||||
**This is P3's gate, not P5's, and P5 is what exposed it.** Recording rather
|
||||
than fixing, for two reasons:
|
||||
|
||||
1. Which state an idle post-boot title shows — build 4 alone, build 4 with the
|
||||
plate over it, or build 4 *then* the plate after a delay — is **behavioural**,
|
||||
and the port has no oracle for a sequence. The game demonstrably has both
|
||||
states; nothing here says which one follows the intro movie. That is the
|
||||
Decoder's.
|
||||
2. Showing it would mean **drawing two builds at once**, which this port has
|
||||
never done — every mode loads exactly one screen. That is a real change to
|
||||
`ScreenView`, not a line in `flow.json`, and it should not be smuggled in
|
||||
under a navigation milestone on the strength of "it looks more right".
|
||||
|
||||
Filed in `BLOCKED.md`. Not blocking: P5's gate is Ⓐ into a submenu and Ⓑ back,
|
||||
and both work.
|
||||
|
||||
@@ -342,7 +342,7 @@ func _menu_enter(name: String, fresh: bool) -> void:
|
||||
_menu.enter(name, view.screen.get("buttons", []))
|
||||
view.focused_id = _menu.focus()
|
||||
view.queue_redraw()
|
||||
print(" menu on %s, focus %s" % [name, view.focused_id])
|
||||
print(" menu on %s, focus %s" % [name, _focus_label(view.focused_id)])
|
||||
if not _script.is_empty() and not _script_started:
|
||||
_script_started = true
|
||||
_run_script()
|
||||
@@ -366,7 +366,7 @@ func _menu_arrive() -> void:
|
||||
_menu.stack[_menu.stack.size() - 1]["focus"] = String(action["restore_focus"])
|
||||
view.focused_id = _menu.focus()
|
||||
view.queue_redraw()
|
||||
print(" menu on %s, focus restored to %s" % [name, view.focused_id])
|
||||
print(" menu on %s, focus restored to %s" % [name, _focus_label(view.focused_id)])
|
||||
else:
|
||||
_menu_enter(name, true)
|
||||
|
||||
@@ -393,6 +393,14 @@ func _has_display(flag: String) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
## How a focus reads in the log. The title has no focusable item at all -- it is
|
||||
## a screen with no `buttons` that still takes (A) -- and an empty string there
|
||||
## printed as a line that trailed off, which reads like the value went missing
|
||||
## rather than like there is none.
|
||||
static func _focus_label(id: String) -> String:
|
||||
return id if id != "" else "(none -- this screen has no focusable item)"
|
||||
|
||||
|
||||
func _capture(path: String) -> void:
|
||||
# Two frames: the first is the one this callback is still inside of.
|
||||
await RenderingServer.frame_post_draw
|
||||
@@ -483,7 +491,7 @@ func _run_script() -> void:
|
||||
return
|
||||
await _shoot("%02d_%s" % [i + 1, token])
|
||||
print("script complete after %.2f s on %s, focus %s"
|
||||
% [_elapsed, _menu.current(), view.focused_id])
|
||||
% [_elapsed, _menu.current(), _focus_label(view.focused_id)])
|
||||
get_tree().quit(0)
|
||||
|
||||
|
||||
@@ -530,4 +538,4 @@ func _shoot(label: String) -> void:
|
||||
push_error("cannot write %s" % tmp)
|
||||
return
|
||||
DirAccess.rename_absolute(tmp, path)
|
||||
print(" shot %s (%s, focus %s)" % [path, _menu.current(), view.focused_id])
|
||||
print(" shot %s (%s, focus %s)" % [path, _menu.current(), _focus_label(view.focused_id)])
|
||||
|
||||
Reference in New Issue
Block a user