From 2939fa21d8203dd7ac413d27af60a7e64776d5b4 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Sat, 12 Sep 2026 12:13:41 +0000 Subject: [PATCH] re: F1 measured -- held DOWN moves the cursor once and no more, via the file driver Fifth boot attempt this session, first clean one, using the harness debugged last iteration (env-safe input, wide capture window, signed-in profile, blanked X root). Reached the menu, held DOWN 2.5s wall-clock, armed the F10 draw capture, released, killed -- zero crashes. Achieved vs requested: 433 of 600 requested frames, spanning 14.43s of GUEST time against ~4.5s wall-clock for the hold+tail window -- this run went at ~3.2x real time (a cheap static menu, nothing pacing it to a display refresh). The 2.5s wall-clock hold covers roughly 8s of that guest time. Method: track one quad shape's position per frame via read_draws.py, not a screen diff -- the approach f1-menu-repeat-harness-built-not-answered.md already named as correct. Exactly one shape-group moves at all: one jump of ~0.21-0.225 NDC units (matching the known one-menu-item pitch), complete within 4 frames / 133ms guest time of arming, then flat (a ~0.0125 pulse wobble only) for the remaining 427 frames / 14.3s of guest time. Broadening the match threshold finds no other moving element. This is a STRONGER negative than the 2026-08-30 result it agrees with (that one sampled a screen diff at ~4-5fps for 2.0s; this reads every quad every frame for ~8s of guest-time held) -- it survives, rather than confirms, last iteration's "coarse sampling hid a fast repeat" alternative, which does not survive a per-frame instrument over a much longer window. Refutation attempt against my own prior lean, recorded: it does not survive. Reconciled, not left in conflict: the human's play-test and pad.py's "auto-repeats" warning almost certainly went through a REAL controller (Canary's SDL driver), which auto-repeats keystrokes at a documented upstream 400ms/100ms (guest time) via the REPEAT keystroke flag. The file driver deliberately never emits that flag, by design -- so it cannot show repeat regardless of how carefully or how long it's held, which is exactly the negative measured here. Still no F1 number; now a specific, well- evidenced reason the file driver alone will never produce one, and a named next step (add opt-in REPEAT-flag support to the file driver, matching the SDL driver's constants, then re-run this exact capture) -- not attempted this iteration, a second unit on an already-complete one. Reference data: docs/re/data/f1-cursor-quad-y-per-frame.tsv -- the derived per-frame Y position and guest tick, not the raw draw log itself (a capture artifact, kept uncommitted per the corpus's game-content rule). --- docs/port/HANDOFF.md | 25 ++++ docs/re/REFUTED.md | 14 ++ docs/re/data/f1-cursor-quad-y-per-frame.tsv | 131 +++++++++++++++++ ...down-measured-no-repeat-via-file-driver.md | 132 ++++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 docs/re/data/f1-cursor-quad-y-per-frame.tsv create mode 100644 docs/re/f1-held-down-measured-no-repeat-via-file-driver.md diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 2124136f..bf176fad 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -6607,3 +6607,28 @@ read). Ran out of iteration budget before a clean end-to-end run. **Still no number for you** β€” keep `-1.0`. The harness should work next attempt; flagging for whoever's container hits the same profile-crash first, since every fresh container starts with none. + +## πŸ”΄ F1 β€” clean run landed. Still keep `-1.0`, but now for a sharper reason + +[`docs/re/f1-held-down-measured-no-repeat-via-file-driver.md`](../re/f1-held-down-measured-no-repeat-via-file-driver.md). +Fifth boot attempt, all four bugs fixed: reached the menu, armed the draw +capture cleanly, held ⬇ for 2.5 s wall-clock (β‰ˆ8 s of *guest* time β€” this +run went at ~3.2Γ— real time, a cheap static menu with nothing pacing it to a +display refresh). **Measured, not inferred:** one cursor step, caught within +133 ms of the press; then zero further moves for the remaining ~14 s of +guest time the capture covers, tracked per-frame off the draw log, not a +screen diff. This is a *stronger* negative than the 2026-08-30 one, not a +different one β€” it survives the "coarse sampling hid a fast repeat" theory +that seemed to be winning after the previous iteration's static trace. + +**Read together with the earlier source finding**, the picture now holds +together instead of conflicting: the human's play-test and `pad.py`'s +"auto-repeats" warning were almost certainly observed through a **real +controller** (Canary's SDL driver), which auto-repeats keystrokes at a +documented 400 ms delay / 100 ms interval (guest time) β€” upstream Xenia, not +this game's own code. Our **scripted** input goes through the `file` driver, +which deliberately never emits that repeat flag. So: **still no number, and +now a clear reason no amount of re-testing through the file driver will ever +produce one.** The next thing that could is patching repeat support into the +file driver (proposed twice, not yet built) and re-running this exact +capture. `-1.0` stays authored, not guessed, either way. diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index 0d61e267..25ce915c 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -1094,6 +1094,20 @@ attached to a right direction; only the reason is refuted here. emulator needed β€” or re-run with a repeat-capable file driver and read cursor position off the draw log per frame, not a coarse screen-diff. [`f1-no-repeat-was-the-harness.md`](f1-no-repeat-was-the-harness.md) ⟨harness⟩ + πŸ”΄ **Half of "to settle" done 2026-09-12, and it points back the other + way.** Re-ran with the *existing* file driver, tracking one quad shape's + position per frame (not a screen diff): one real move, caught cleanly at + 133 ms guest resolution, then **nothing for the remaining ~8 s of guest + time the button was held** β€” a stronger negative than the original, not a + reversal of it. This also refutes my own leading alternative from the + `C_PAD_RINGBUF` trace (that a coarse screen-diff was hiding a fast + polled-state repeat): a per-frame instrument over a much longer held + window still finds nothing. Reading now favours Keystroke-`REPEAT`-driven + menu navigation after all β€” which the file driver cannot produce by + design, regardless of how carefully it's held β€” over the polled-state theory. + Still 🟑, not ❌: only the file-driver path is tested; the SDL-driver path + (what a real controller and the human's play-test went through) is not. + [`f1-held-down-measured-no-repeat-via-file-driver.md`](f1-held-down-measured-no-repeat-via-file-driver.md) ⟨capture⟩ * "F10-arming a UI draw capture is safe on any settled screen" β€” **implicit in every prior use of it, and false in a fresh container.** A `run-canary` launch with no signed-in profile (every container right after a restart β€” diff --git a/docs/re/data/f1-cursor-quad-y-per-frame.tsv b/docs/re/data/f1-cursor-quad-y-per-frame.tsv new file mode 100644 index 00000000..8bc75427 --- /dev/null +++ b/docs/re/data/f1-cursor-quad-y-per-frame.tsv @@ -0,0 +1,131 @@ +# frame gtick gfreq_hz cursor_quad_y_ndc +# source: xenia_re_ui_draws_01.log, 2026-09-12, f1_hold_capture.py run 5 (f1g) +# quad: page B5B1C73032BA3FA3, ~0.09x0.16 NDC (focus highlight) +# held DOWN written at wall t~=161.6s (script HOLD start), released ~164.1s +1 8077295605 50000000 0.0525 +2 8078950338 50000000 0.055 +5 8083967064 50000000 -0.16 +6 8085646790 50000000 -0.16 +7 8087365907 50000000 -0.16 +10 8092344928 50000000 -0.1625 +11 8094014388 50000000 -0.1625 +12 8095718136 50000000 -0.16 +13 8097430051 50000000 -0.1625 +21 8110812183 50000000 -0.1675 +22 8112478213 50000000 -0.17 +23 8114208650 50000000 -0.1675 +27 8120905050 50000000 -0.17 +28 8122530583 50000000 -0.17 +29 8124213619 50000000 -0.17 +36 8135946976 50000000 -0.17 +37 8137620430 50000000 -0.17 +57 8171043909 50000000 -0.16 +58 8172689798 50000000 -0.16 +65 8184394745 50000000 -0.16 +66 8186106441 50000000 -0.16 +67 8187771244 50000000 -0.16 +70 8192768181 50000000 -0.1625 +71 8194492771 50000000 -0.1625 +72 8196154016 50000000 -0.16 +73 8197801248 50000000 -0.1625 +81 8211207297 50000000 -0.1675 +82 8212855981 50000000 -0.17 +83 8214569003 50000000 -0.1675 +87 8221264934 50000000 -0.17 +88 8222911320 50000000 -0.17 +89 8224614159 50000000 -0.17 +96 8236285728 50000000 -0.17 +97 8237986083 50000000 -0.17 +117 8271508635 50000000 -0.16 +118 8273209128 50000000 -0.16 +125 8284925117 50000000 -0.16 +126 8286599612 50000000 -0.16 +127 8288306224 50000000 -0.16 +130 8293338269 50000000 -0.1625 +131 8295030562 50000000 -0.1625 +132 8296723652 50000000 -0.16 +133 8298377150 50000000 -0.1625 +141 8311734503 50000000 -0.1675 +142 8313414801 50000000 -0.17 +143 8315067618 50000000 -0.1675 +147 8321746008 50000000 -0.17 +148 8323415335 50000000 -0.17 +149 8325134926 50000000 -0.17 +156 8336880646 50000000 -0.17 +157 8338715074 50000000 -0.17 +177 8372226351 50000000 -0.16 +178 8373910878 50000000 -0.16 +185 8385596519 50000000 -0.16 +186 8387262693 50000000 -0.16 +187 8388929103 50000000 -0.16 +190 8393937806 50000000 -0.1625 +191 8395639462 50000000 -0.1625 +192 8397278358 50000000 -0.16 +193 8398933992 50000000 -0.1625 +201 8412260793 50000000 -0.1675 +202 8414004734 50000000 -0.17 +203 8415667167 50000000 -0.1675 +207 8422367364 50000000 -0.17 +208 8424030952 50000000 -0.17 +209 8425700045 50000000 -0.17 +216 8437382342 50000000 -0.17 +217 8439068602 50000000 -0.17 +237 8472406179 50000000 -0.16 +238 8474085108 50000000 -0.16 +245 8485743903 50000000 -0.16 +246 8487413304 50000000 -0.16 +247 8489067654 50000000 -0.16 +250 8494081252 50000000 -0.1625 +251 8495818763 50000000 -0.1625 +252 8497475603 50000000 -0.16 +253 8499145711 50000000 -0.1625 +261 8512509389 50000000 -0.1675 +262 8514145895 50000000 -0.17 +263 8515853531 50000000 -0.1675 +267 8522549986 50000000 -0.17 +268 8524228499 50000000 -0.17 +269 8525932669 50000000 -0.17 +276 8537663413 50000000 -0.17 +277 8539354084 50000000 -0.17 +297 8572827882 50000000 -0.16 +298 8574531771 50000000 -0.16 +305 8586256367 50000000 -0.16 +306 8587913956 50000000 -0.16 +307 8589566049 50000000 -0.16 +310 8594594709 50000000 -0.1625 +311 8596292779 50000000 -0.1625 +312 8597936103 50000000 -0.16 +313 8599628814 50000000 -0.1625 +321 8613009386 50000000 -0.1675 +322 8614648734 50000000 -0.17 +323 8616321818 50000000 -0.1675 +327 8622991187 50000000 -0.17 +328 8624688470 50000000 -0.1725 +329 8626338823 50000000 -0.17 +336 8638111132 50000000 -0.17 +337 8639823382 50000000 -0.17 +357 8673272643 50000000 -0.16 +358 8674946981 50000000 -0.16 +365 8686603708 50000000 -0.16 +366 8688279571 50000000 -0.16 +367 8689942913 50000000 -0.16 +370 8694996359 50000000 -0.1625 +371 8696658822 50000000 -0.1625 +372 8698321977 50000000 -0.16 +373 8699983034 50000000 -0.1625 +381 8713361239 50000000 -0.1675 +382 8715049029 50000000 -0.17 +383 8716689919 50000000 -0.1675 +387 8723381237 50000000 -0.17 +388 8725066870 50000000 -0.1725 +389 8726756040 50000000 -0.17 +396 8738454519 50000000 -0.17 +397 8740146250 50000000 -0.17 +417 8773614167 50000000 -0.16 +418 8775249863 50000000 -0.16 +425 8787042967 50000000 -0.16 +426 8788724033 50000000 -0.16 +427 8790309865 50000000 -0.16 +430 8795410469 50000000 -0.1625 +431 8797094266 50000000 -0.1625 +432 8798815247 50000000 -0.16 diff --git a/docs/re/f1-held-down-measured-no-repeat-via-file-driver.md b/docs/re/f1-held-down-measured-no-repeat-via-file-driver.md new file mode 100644 index 00000000..f17eb7e3 --- /dev/null +++ b/docs/re/f1-held-down-measured-no-repeat-via-file-driver.md @@ -0,0 +1,132 @@ +# F1 β€” measured: held ⬇ moves the cursor once and no more, over ~8 s of guest time, through the file driver + +**Status:** βœ… measured (⟨capture⟩), for the path this container can actually +drive. ❔ still not F1's requested initial-delay/interval numbers β€” this +result explains why they cannot come from this instrument, and names the one +that could. Instrument: +[`tools/re-capture/f1_hold_capture.py`](../../tools/re-capture/f1_hold_capture.py), +after the four fixes in +[`f1-hold-capture-harness-debugged.md`](f1-hold-capture-harness-debugged.md). +2026-09-12, fifth boot attempt, first clean one. + +## The run + +Reached the settled main menu at 159.3 s (title at 154.3 s, matching the last +clean run's timing), armed the F10 draw capture, held ⬇ for 2.5 s wall-clock, +released, waited 2 s, killed. Zero crashes. 433 of the requested 600 frames +captured before the kill (`ui_draw_capture_frames=600`, bumped from the +default 3 last iteration). + +⚠️ **Achieved vs. requested, per `TEMPORAL-VERIFICATION.md`:** the 433 frames +span **14.43 s of *guest* time** (`gtick`/`gfreq` on each frame header, `50 000 000` Hz) +against **~4.5 s of wall-clock** for the hold + tail β€” this window ran at +roughly **3.2Γ— real time**, not 1:1. A static 2D menu is cheap to render, and +nothing here was pacing the emulation to a display refresh. The 2.5 s +wall-clock hold itself therefore covers **roughly 8 s of guest time** (the +hold is 2.5 of the 4.5 wall-clock seconds captured), not 2.5. Achieved guest +frame rate: **29.87 fps** β€” close to a 30 Hz internal tick, for what it's +worth, but this is a rate, not a claim about which clock the menu itself +runs on. + +## Method β€” track one quad shape's position, not a screen diff + +Read every draw with `read_draws.py`, grouped quads by `(texture page, +width, height)` β€” the *"track one quad shape's position over time"* approach +`f1-menu-repeat-harness-built-not-answered.md` already named as the correct +one. Exactly one shape-group moves at all across the whole capture: page +`B5B1C73032BA3FA3`, a small (`0.09 Γ— ~0.16` NDC) element β€” the focus +highlight, by elimination and by the size of its move (below). + +## What it shows + +| frame | guest ms since frame 1 | Y (NDC) | +|---|---|---| +| 1 | 0 | **+0.0525** | +| 5 | 133 | **βˆ’0.16** | +| 6…432 | up to 14 297 ms later | **βˆ’0.1725 … βˆ’0.16** (wobble only) | + +**One jump, size 0.21–0.225 NDC units, complete within 4 frames (133 ms +guest) of arming** β€” close to the one-item pitch computed from +`menu_focus.py`'s known button rows (`(390βˆ’315)/720Γ—2 = 0.208`), i.e. this +reads as one menu step. **Then nothing.** The βˆ’0.16-ish value that follows +oscillates by ≀0.0125 on a ~60-frame cycle for the remaining **427 frames / +14.3 s of guest time** β€” a pulsing highlight glow, the same shape of +animation this corpus has measured elsewhere, not a second discrete move. +Broadening the shape-match threshold from β‰₯50 to β‰₯5 frames-present finds +only one other group, at the same page and a rounding-adjacent height +(`0.17` vs `0.16`, same element split by sub-pixel quantisation), showing +the identical pattern. **Nothing else in the capture moves at all.** + +This is a stronger negative than the 2026-08-30 result it agrees with: +`nav_repeat_and_b.py` sampled a screen diff at ~4–5 fps for 2.0 s wall-clock; +this reads *every submitted quad, every frame*, for a window covering **~8 s +of guest time** the button was actually held. A coarse-sampling explanation +for "no repeat" β€” the leading alternative hypothesis after last iteration's +`C_PAD_RINGBUF` trace β€” cannot survive a per-frame instrument that still +finds nothing. + +## Reconciling with the standing conflict, not hand-waving past it + +Three facts now sit together and are not actually in tension once separated +by **instrument**, not by hand: + +1. The human's play-test, and `pad.py`'s own docstring, both describe a real + repeat β€” almost certainly observed through a **real controller**, i.e. + Canary's **SDL** input driver. +2. That driver's `GetKeystroke()` (`f1-no-repeat-was-the-harness.md`) + auto-repeats keystrokes at a *documented, upstream* 400 ms initial delay + then 100 ms interval, guest time (`HID_SDL_REPEAT_DELAY`/`_RATE`). +3. This measurement went through the **file** driver, whose `GetKeystroke()` + is *deliberately* built to emit exactly one event per press and never a + `REPEAT`-flagged one β€” and now, measured rather than argued, holding the + button via that driver's continuously-reporting `GetState()` for ~8 + guest-seconds produces no second move either. + +**The straightforward reading:** menu repeat is driven by `REPEAT`-flagged +keystrokes, not by polling raw held state every frame. The file driver +cannot produce that flag by design, so it cannot show repeat, however long +or precisely you hold the button through it β€” which is exactly the negative +result above. Last iteration's `C_PAD_RINGBUF` trace (analog-axis-shaped +fields, favouring a polled-state reading) likely belongs to a different +consumer β€” plausibly raw stick deflection for something else entirely β€” not +to whatever specifically steps the menu cursor. That inference is not +re-verified here; flagging it as probably-superseded rather than silently +dropping it. + +## What would actually answer F1 + +Give the file driver the same `REPEAT`-flag capability the SDL driver has, +gated behind a new flag so every other scripted script keeps today's +one-event-per-press behaviour (this was proposed, not built, twice already β€” +`f1-no-repeat-was-the-harness.md`'s original "what would close it" and the +harness-debugging page's "what's left"). Then re-run this exact capture. +Two outcomes, both answers: + +* it now shows a genuine repeat, at some interval derived from the SDL + driver's constants or the game's own consumption of them β€” measure the + interval in frames at the achieved guest rate, not a millisecond guess; +* it still shows nothing, which would mean the menu's repeat is driven by + something other than the Keystroke `REPEAT` flag after all, and the + question reopens from a different angle. + +Not attempted this iteration β€” patching the driver and re-running is a +second, larger unit on top of an already-complete one. + +## Reference data + +[`data/f1-cursor-quad-y-per-frame.tsv`](data/f1-cursor-quad-y-per-frame.tsv) β€” +the focus-highlight quad's Y (NDC) per frame it appears in, with the guest +tick/frequency for each, derived from the run's draw log (not the log +itself, which is a capture artifact and stays uncommitted per the corpus's +rule on game content β€” this is measurement output, numbers only). + +## Reach + +⟨capture⟩ for the "no repeat via the file driver" result β€” direct, per-frame, +quantified in guest time, with the instrument's own capability shown by the +one real move it caught. Everything about *why* (Keystroke vs. polled state) +is ⟨canary-source⟩ reasoning connecting this result to the prior static +trace, not itself re-measured. One run only β€” the corpus's own two-run +minimum for a reproducibility claim is not met, though this run's own +positive control (catching the initial move at 133 ms resolution) is +evidence the null isn't an instrument artifact of *this specific* run.