From 85d2e93d05cefa4801c61df69376864c42ee28a3 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Wed, 2 Sep 2026 16:41:55 +0000 Subject: [PATCH] re: F1 harness built and committed; the run did not reach the menu The port needs an initial delay and a repeat interval and has shipped the mechanism with -1.0 rather than invent them. This builds the instrument and does not get the numbers. The vertex stream is the right instrument: C_PAD_DECODER has no timer on any direction path, so the repeat is in the layer above and cannot be read out of that function; a cursor move is a quad changing position, and the guest's vertex buffer carries that with no Canary processing in the path. tools/re-capture/menu_repeat_probe.sh arms the logger, drives the boot with pad-file presses and holds a direction. It ran its whole sequence. But the boot did not reach the menu: in the settled era every quad shape holds a constant y, and the shapes are near-full-screen rects rather than a button list. No cursor, so nothing to repeat. Two instrument errors worth more than the failed run. The first attempt ARMED NOTHING and said nothing -- xdotool search --class ... --window %1 F10 behind a || true. The run completed normally and only the ABSENT log revealed it. The working form looks the window up by name, activates it, and sends F10 to the window and globally; a missing window is now fatal rather than tolerated, because a silent arming failure is indistinguishable from a screen that draws nothing. My first cursor detector measured the wrong change: presents where the SET of quads changed, which fired on 157 of 161 adjacent pairs because during a build-in everything animates. Tracking one quad shape's POSITION instead correctly reports no motion. That is the play-test's own lesson landing on me -- an instrument that measures change can still measure the change of the wrong thing. Next run must not drive the boot blind: gate the presses on a classifier or join with boot_menu.sh, which reaches the menu but takes screenshots rather than draw logs. Blind was chosen because screenshots cost ~10 s each while xenia runs; wrong trade, since a mistimed press costs the whole run. Reach: the negative is about this run, not the game. F1 stays open and the port should keep its -1.0 constants. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t --- ...-menu-repeat-harness-built-not-answered.md | 82 +++++++++++++++++++ tools/re-capture/menu_repeat_probe.sh | 57 +++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 docs/re/f1-menu-repeat-harness-built-not-answered.md create mode 100755 tools/re-capture/menu_repeat_probe.sh diff --git a/docs/re/f1-menu-repeat-harness-built-not-answered.md b/docs/re/f1-menu-repeat-harness-built-not-answered.md new file mode 100644 index 00000000..1a6463b0 --- /dev/null +++ b/docs/re/f1-menu-repeat-harness-built-not-answered.md @@ -0,0 +1,82 @@ +# ❔ F1, the menu repeat rate — the harness works, the boot did not reach the menu + +**Status: ❔ not answered. Instrument built and committed; the run did not land.** +2026-09-02. Instrument: ⟨capture⟩ attempted via +[`tools/re-capture/menu_repeat_probe.sh`](../../tools/re-capture/menu_repeat_probe.sh). + +The port needs two numbers — initial delay and repeat interval — and has shipped +the mechanism with `-1.0` constants rather than invent them. This iteration built +the instrument to measure them and **did not get them.** + +--- + +## Why the vertex stream is the right instrument for this + +`C_PAD_DECODER` has **no timer on any direction path** +([`pad-decoder-double-tap-not-key-repeat.md`](pad-decoder-double-tap-not-key-repeat.md)), +so the repeat is in the layer above and cannot be read out of that function. A +cursor move is observable as **a quad changing position**, and the guest's own +vertex buffer carries that with no Canary processing in the path — the same +instrument that settled the splash. + +## What the harness does, and it now works + +Arms the draw logger, drives the boot with pad-file presses (`press=A`, then a +held `press=DOWN`), and records per-frame vertex geometry throughout. The run +completed its whole sequence: + +``` +armed at 8s (win=4194307) A #1 at 21s A #2 at 28s +A #3 at 31s HOLDING DOWN at 36s released at 48s done at 51s +``` + +## 🔴 Why it still did not answer F1 + +**The boot did not reach the menu.** In the settled era (frames 520–596) every +quad shape holds a **constant** y: + +``` +quad shapes present: (1.77,0.78) (1.75,0.73) (1.43,0.32) (0.06,0.05) (1.55,0.29) (1.08,0.05) +moving shapes: none +``` + +Those are near-full-screen rects, not a list of menu buttons, and **no quad moves +at all** — so there was no cursor to repeat. The blind timings did not land; the +presses went somewhere other than where they were aimed. + +## ⚠️ Two instrument errors on the way, both worth more than the failed run + +**1 — the first attempt armed nothing, and said nothing.** It used +`xdotool search --class xenia_canary key --window %1 F10` behind a `|| true`. The +run completed normally and **only the absent log revealed it.** The working form, +copied from `ui_draw_capture.sh`, looks the window up by **name**, activates it, +and sends F10 to the window **and** globally. A failure to find the window is now +**fatal** rather than tolerated — a silent arming failure is indistinguishable +from a screen that draws nothing. + +**2 — my first cursor detector measured the wrong change.** I counted presents +where the **set** of quads changed, and got a change on 157 of 161 adjacent pairs. +That is not the cursor: during a build-in everything animates, so set-membership +tracks animation. Replacing it with *"track one quad shape's position over time"* +correctly reports **no motion** — which is the right answer for a screen with no +cursor, and is why the second detector is trustworthy where the first was not. + +📌 This is the play-test's own lesson landing on me: **an instrument that measures +change can still measure the change of the wrong thing.** `motion-census` exists +because three port instruments measured a pose or a throughput; mine measured +change, and still needed the change to be *of the cursor*. + +## What the next run should do differently + +**Do not drive the boot blind.** The presses need gating on a screen classifier, +or the documented `boot_menu.sh` path — which reaches the menu reliably but takes +screenshots rather than draw logs, so the two harnesses need joining. The blind +route was chosen because `screenshot` costs ~10 s per grab while xenia runs; that +trade was wrong here, because a mistimed press costs the whole run. + +## Reach + +The negative is about **this run**, not about the game: it says the presses did not +land, not that a held direction fails to repeat. The human watched the real game +and reports it does. **F1 remains open, and the port should keep its `-1.0` +constants rather than take a number from anything here.** diff --git a/tools/re-capture/menu_repeat_probe.sh b/tools/re-capture/menu_repeat_probe.sh new file mode 100755 index 00000000..9829f89c --- /dev/null +++ b/tools/re-capture/menu_repeat_probe.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# F1 -- MEASURE THE MENU REPEAT RATE, as a count of presents between cursor moves. +# +# The human watched the real game: a held direction repeats. `C_PAD_DECODER` has +# no timer on any direction path (pad-decoder-double-tap-not-key-repeat.md), so +# the repeat is in the layer above and can only be MEASURED here. +# +# Instrument: the guest's own vertex stream via the UI draw logger -- the same +# instrument that settled the splash, and the one with no Canary processing in +# it. A cursor move shows up as the focused quad's NDC rect changing. +# +# ⚠️ Drives the boot BLIND on timings rather than classifying frames. That is +# deliberate: the classifier route costs a screenshot per poll, and screenshots +# cost ~10 s each while xenia runs. The draw log records what actually happened, +# so a mistimed press is visible in the result rather than silently assumed. +# +# menu_repeat_probe.sh [out_dir] [hold_button] +set -u +export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 +export XENIA_PAD_FILE=/tmp/xenia_pad.txt +OUT="${1:-/sylph-home/re/f1}" +HOLD="${2:-DOWN}" +mkdir -p "$OUT"; rm -f "$OUT"/xenia_re_ui_draws_*.log + +pad() { printf '%s' "$1" > "$XENIA_PAD_FILE.tmp"; mv "$XENIA_PAD_FILE.tmp" "$XENIA_PAD_FILE"; } +tap() { pad "press=$1"; sleep 0.40; pad ""; sleep 0.30; } + +pad "" +( cd "$OUT" && nohup run-canary --log_ui_draws=true \ + --ui_draw_capture_frames=4000 --ui_draw_capture_max=900000 \ + >"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & ) +sleep 8 +# Arm the capture. ⚠️ Copied verbatim from ui_draw_capture.sh, which works. +# A first attempt used `xdotool search --class xenia_canary key --window %1 F10` +# and armed NOTHING -- it failed silently behind a `|| true`, the run completed, +# and only the ABSENT log revealed it. Window lookup is by NAME, the key goes to +# the window AND globally, and a failure is now fatal rather than tolerated. +win="$(xdotool search --name "Xenia-canary" | tail -1)" +[ -n "$win" ] || { echo "FATAL: no Xenia window to arm"; pkill -x xenia_canary; exit 1; } +xdotool windowactivate "$win" 2>/dev/null +xdotool key --window "$win" F10 +xdotool key F10 +echo "armed at ${SECONDS}s (win=$win)" + +# The boot, driven blind. Splashes ~8 s, then the attract movie; A skips it. +sleep 12; tap A ; echo "A #1 (skip video) at ${SECONDS}s" +sleep 6; tap A ; echo "A #2 (reveal plate) at ${SECONDS}s" +sleep 3; tap A ; echo "A #3 (accept plate) at ${SECONDS}s" +sleep 5 +echo "HOLDING $HOLD at ${SECONDS}s" +pad "press=$HOLD" +sleep 12 +pad "" +echo "released at ${SECONDS}s" +sleep 3 +pkill -x xenia_canary +echo "done at ${SECONDS}s; log:"; ls -la "$OUT"/*.log