From a9ce94ec83a5bab91a87d9615a4e3d118735e8dc Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Wed, 26 Aug 2026 16:51:59 +0000 Subject: [PATCH] re: MISSION SELECT is not special -- NEW GAME freezes too Last iteration concluded the freeze is "entering MISSION SELECT" and made avoiding that screen the next experiment. Ran it; the conclusion was too narrow. First, a liveness metric that actually separates the states: two frames five seconds apart, percentage of pixels changed. The menu animates, so healthy is 99.80-99.97% and frozen is 0.00%. No navigation script needed, and no classifier. Committed as tools/re-capture/route_liveness_probe.sh; this is what should have been used from the first run. Then the menu's FIRST item, NEW GAME, which never touches MISSION SELECT: main menu 99.80% alive after A on NEW GAME 7.33% after the next A 0.00% -- frozen, and screen_id calls it "flight" with the same 134217728-byte AllocRange failure in the log. So the correct statement is broader: the game freezes on the first content load after the main menu, whichever item is taken. MISSION SELECT was just the route every earlier run used. "Avoid MISSION SELECT" is withdrawn -- there is nothing to avoid, and that also puts the memory account back at the centre, since ~379 MB live plus a 128 MB content load fails on any route. Worth repeating because it caught me twice: screen_id.py called a frozen frame "flight" on a run that never left the menus. Liveness first, classification second. --- docs/re/mission-freeze-heap-exhaustion.md | 56 +++++++++++++++++++++++ tools/re-capture/route_liveness_probe.sh | 37 +++++++++++++++ 2 files changed, 93 insertions(+) create mode 100755 tools/re-capture/route_liveness_probe.sh diff --git a/docs/re/mission-freeze-heap-exhaustion.md b/docs/re/mission-freeze-heap-exhaustion.md index 92f8cbe5..052158c1 100644 --- a/docs/re/mission-freeze-heap-exhaustion.md +++ b/docs/re/mission-freeze-heap-exhaustion.md @@ -512,3 +512,59 @@ this session has used goes EXTRAS → MISSION SELECT. The 2026-08-10 run that fl for 500 s did not necessarily; `newgame_path.sh` and `tutorial_launch.sh` reach a mission by other menus. Testing a non-MISSION-SELECT route is the next experiment, and it is now the most promising one available. + +--- + +# 🔴 MISSION SELECT is **not** special — corrected the same day + +The section above concluded the freeze is "entering MISSION SELECT" and made +avoiding that screen the next experiment. Ran it, and **that conclusion is too +narrow.** + +## A liveness metric that actually separates the two states + +`tools/re-capture/route_liveness_probe.sh`. Two frames five seconds apart, report +the percentage of pixels that changed. The menu animates, so the separation is +not marginal: + +| state | pixels changed in 5 s | +|---|---| +| main menu, healthy | **99.80 – 99.97 %** | +| frozen | **0.00 %** | + +This is the measurement that should have been used from the first run. It needs +no navigation script to be honest about where it is. + +## The NEW GAME route freezes too + +Walking the menu's **first** item — NEW GAME, which never touches MISSION SELECT: + +| step | liveness | `screen_id` says | +|---|---|---| +| main menu | **99.80 %** | menu | +| after Ⓐ on NEW GAME | 7.33 % | title | +| after the next Ⓐ | **0.00 %** | *"flight"* | + +Frozen two presses off the main menu, with the same +`AllocRange unable ... 134217728 bytes` in the log. + +## So the correct statement is broader + +**The game freezes on the first content load after the main menu, whichever menu +item is taken.** MISSION SELECT was simply the route every earlier run happened +to use. The previous section's "avoid MISSION SELECT" recommendation is +withdrawn — there is nothing to avoid. + +That puts the memory account back at the centre: if ~379 MB is already live and +a content load wants 128 MB, *any* content load fails, which is exactly what two +different routes now show. + +⚠️ Note also how badly `screen_id.py` misleads here: it called a frozen frame +`flight` on a run that never left the menus. **Liveness first, classification +second** — a classifier's answer is meaningless until the frame is known to be +moving. + +❔ Still open: whether the ~171 MB allocated at boot (112.88 MB at `B50C0000`, +58.50 MB at `BC220000`, both before any menu) is avoidable, since that plus a +128 MB content load is most of the console. That is the last untested lever, and +it is a question about what those two blocks are. diff --git a/tools/re-capture/route_liveness_probe.sh b/tools/re-capture/route_liveness_probe.sh new file mode 100755 index 00000000..0002079d --- /dev/null +++ b/tools/re-capture/route_liveness_probe.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Boot to the main menu, then walk menu steps, measuring LIVENESS after each by +# comparing two frames a few seconds apart. Never trusts a progress log. +set -u +export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 XENIA_PAD_FILE=/tmp/xenia_pad.txt +export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages +SD=/tmp/claude-1000/-home-fabi-RE-Project-Sylpheed/b113cc12-4769-4ed8-ad66-c2b48f800773/scratchpad/wt-root/Syplheed-Reborn/tools/re-capture +export PATH="$SD/bin:$PATH" +OUT=/tmp/route; rm -rf $OUT; mkdir -p $OUT +pad(){ python3 "$SD/pad.py" "$@"; } +shot(){ screenshot "$OUT/$1.png" >/dev/null 2>&1; } +live(){ # two frames 5s apart -> % pixels changed + screenshot "$OUT/_a.png" >/dev/null 2>&1; sleep 5; screenshot "$OUT/_b.png" >/dev/null 2>&1 + python3 - "$OUT/_a.png" "$OUT/_b.png" <<'PY' +from PIL import Image; import numpy as np, sys +a=np.asarray(Image.open(sys.argv[1]).convert('RGB'),dtype=float) +b=np.asarray(Image.open(sys.argv[2]).convert('RGB'),dtype=float) +d=np.abs(b-a); print(f"{(d.max(2)>4).mean()*100:.2f}") +PY +} +say(){ echo "[$(date +%T)] $*"; } + +say "waiting for the title, then one A" +for i in $(seq 1 240); do + shot probe; s=$(python3 "$SD/screen_id.py" "$OUT/probe.png" | awk '{print $1}') + [ "$s" = title ] && break + [ "$s" = menu ] && break + sleep 2 +done +say "screen=$s ; liveness here = $(live)%" +if [ "$s" = title ]; then pad tap A; sleep 6; fi +for i in $(seq 1 60); do + shot probe; s=$(python3 "$SD/screen_id.py" "$OUT/probe.png" | awk '{print $1}') + [ "$s" = menu ] && break; sleep 2 +done +say "MAIN MENU (screen=$s) liveness = $(live)%" +shot 00-mainmenu