Skip to content

Commit 05bc481

Browse files
Scottclaude
andcommitted
docs(#70): reorganize RUNBOOK.md around sittings, roles, and physical requirements
The runbook had accumulated a lot of narrative/reasoning inline with the actionable steps, scattered across sections in the order bugs were found rather than the order work happens in. Reorganized around how the work actually gets done across multiple Deck sittings: - Roles section: driver (Claude/SSH) vs human (Scott, physical) responsibilities, including the no-keyboard Escape-injection pattern that caused real friction last session (driver must inject proactively, not wait for "I'm stuck"). - Physical-requirements table: which of the 4 sittings need Scott at the Deck vs are driver-only (setup and Phase D comparison need no physical presence; MangoHud probe and both scored-cycle sittings do). - Known-gaps section carried forward from last session's findings (PolyMC version pinning, REPO_REF requirement, explicit MC version selection) so future benchmark work inherits them without re-deriving. - Progress checklist so a fresh session knows sitting 1 (setup + Phase A install + MangoHud probe + settings) is done and doesn't need redoing. - Sitting-by-sitting procedure (1: done/reference, 2: Phase A cycles, 3: Phase B install + Phase C cycles, 4: Phase D comparison/decision/docs) instead of phase-by-phase, since sittings are the actual unit of work. Archived the previous version as RUNBOOK-20260805.md (same convention as docs/PLAN-YYYYMMDD.md) before rewriting, per the outgoing version's last- updated date. Also updated docs/PLAN.md's #70 status line to match current reality instead of the stale "not yet run" framing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DrqhXXJ6ZxZw3Ry7oCTRV
1 parent a5f044f commit 05bc481

3 files changed

Lines changed: 826 additions & 441 deletions

File tree

docs/PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ pads and no production code change is required.
214214

215215
**Operator-observed render lag** (windows the automated geometry check had already passed not yet visually apparent) also came up mid-session — addressed by extending the post-convergence settle window from 5s to 20s, after which the operator confirmed all 4 windows visible on every one of the final 5 iterations.
216216
| **PR-5** #151 repro + quiesce-then-repoint fix | **CLOSED 2026-08-02 — both bugs Deck-validated fixed. Issue #151 closed on GitHub.** `tests/probe-node-swap.sh` (Tier 1) confirmed the kernel-level node-swap precondition is real and forceable (plain 2-pad: 3/3 clean). `tests/probe-reconnect-swap.sh` (Tier 2) then reproduced #151 live in a real 4-up `MCSS_CONTROLLER_PROXY=1` session — real EBUSY / "capabilities... different than expected" evsieve errors, matching the issue's own trace exactly. **Fix**: `controller_proxy.sh` gained `proxy_quiesce_slot()` (removes the disconnecting slot's proxy-pads symlink instead of leaving it stale), wired into `orchestrator.sh`'s `CONTROLLER_REMOVE` handler *before* `slot_release` — closes the window where a slot's `evsieve` (persist=reopen) could grab whatever pad the kernel handed the freed `eventN` to next. Unit test T16 (`test_controller_proxy.sh`) and two new assertions (`test_reconnect_dispatch.sh`) added, both mutation-tested (broke the fix, confirmed red, restored, confirmed green). CI baselines bumped. **Deck-validated 2026-08-02, 3/3 iterations: zero EBUSY/grab-failure signatures** — the original race is closed. Two rounds of probe-timing fixes were needed first (operator directly observed the forced disconnect firing before the 4th window had settled, and evsieve's own logs showed a udev-uaccess-tagging retry loop running tens of seconds — fixed by waiting on `hw_slot_window_visible` + a 15s settle + a 60s symlink-resolution poll instead of guessed sleeps). **New finding surfaced by that same run, same night**: the symlink-resolution check still failed 3/3 — not from residual EBUSY, but because slot 4 (whichever pad reconnects *first* after a simultaneous 2-pad drop) never got its `CONTROLLER_ADD` processed as a RESUME at all. Traced via a full `set -x` debug-log capture (`/tmp/splitscreen-debug-latest.log`, courtesy `SPLITSCREEN_DEBUG_LOG`): `controller_monitor.sh` detects and logs *both* simultaneous removals (`Controller removed: /dev/input/event20` and `.../event31`, back to back), but only **one** `CONTROLLER_REMOVE` message actually reaches the orchestrator's FIFO reader — confirmed via `_handle_msg`'s own trace, which never sees the second one. That slot's `disconnected` flag stays `false`, so its reconnect's `slot_claim` call sees "already active" and REJECTs it outright (not a grab race — an explicit, logged reject). Root-caused via the same debug log, cross-referenced against `dock_detection.sh`'s pre-existing (and previously unexplained) `# H6: tolerate broken pipe` workaround on its own FIFO writes — same underlying gap, just papered over there. **Fix (commit `03e728b`, same night)**: `orchestrator.sh` gained `_open_fifo_reader()`, which opens `SPLITSCREEN_FIFO` ONCE (`exec {fd}<> "$fifo"`) and holds it for the whole life of `docked_flow`/`handheld_flow`, called before any writer (controller monitor, dock monitor, watchdog) starts; `_read_fifo_msg` reads from that persistent fd (falling back to the old per-call open if it was never set up). Closed in `cleanup()` after all writers are killed. New test T6.9 (`test_orchestrator.sh`) mutation-tested: disabling `_open_fifo_reader` reproduces the loss (8/9, correctly red); restoring it fixes it (9/9, green) — also revealed the load-bearing part is holding *any* persistent fd open, not which fd `_read_fifo_msg` itself reads from. All existing suites (`test_orchestrator.sh` 9/9, `test_reconnect_dispatch.sh` 15/15, `test_controller_proxy.sh` 16/16, `test_watchdog.sh` 15/15) still pass; CI baseline bumped. Mid-investigation the Deck's gamescope session went unresponsive (joysticks/Steam button/shortcut button/touchscreen all dead, SSH still reachable); traced to 4 orphaned `evsieve` proxy processes from an earlier probe run still holding live `MCSS-slot1..4` ghost USB devices (killed by exact PID, per [[no-remote-steam-restart]] no Steam/gamescope restart attempted remotely) — didn't resolve the freeze, operator power-cycled. **Retry #1 after reboot also hit trouble**: Steam itself restarted mid-run (gamescope stayed up throughout — confirmed via process start times, not a gamescope crash). Operator correctly rejected the first read ("Steam doesn't take 30 minutes to settle") and named the real cause: `tests/probe-reconnect-swap.sh` was cycling 4 real JVMs up and down 3x with near-zero pacing between pad connects/disconnects and between iterations — plausibly hard enough on the machine to trip a Steam-side watchdog restart. **Fix (commit `59d0ed7`)**: 2s gaps between each pad connect/disconnect (initial 4-pad creation, and the swap's own drop/reconnect pairs — confirmed this doesn't defeat the swap mechanism, since the kernel hands out the lowest-free node at CREATE time, not destroy time) plus a 20s rest between iterations. **Retry #2, after a second reboot, with the pacing fix: full success.** `tests/probe-reconnect-swap.sh` — **3/3 clean, 0/3 reproduced**, no disruption during the run. All three checks (state-file identity, proxy symlink resolution, evsieve log signature) passed on every iteration, including the exact symlink-resolution failure that had failed 3/3 times on every run before the FIFO fix. A cosmetic `grep -c` double-count bug in the probe's own Check 3 (harmless "0\n0" arithmetic warning, didn't affect the PASS/FAIL result) found and fixed same session (commit `eeddd19`). Orphaned evsieve processes from the run's own teardown lag (a separate, pre-existing "Steam reaper still present" issue, not #151) cleaned up by exact PID after each attempt. | agent ~6h (spent) · **Deck ~1h10min (spent, Tier 1 + Tier 2 + 2 retries) — closed** |
217-
| **#70** benchmark pilot | **Planning done, not yet run.** Methodology decided per §5 below (options 2+3: distance-as-covariate + log-line-gated phase transitions for the first campaign; option 1, work-based scoring, deferred to a later iteration if the covariate turns out to matter). Logistics worked out 2026-08-02: a physical mouse (not a controller) handles all menu navigation — invisible to `controller_monitor.sh`'s gamepad-capability gate, so it never touches `slot_claim`'s identity system, which is why a real controller can't be used for this (it would claim a slot a virtual pad can't later resume into, since `slot_claim` matches strictly by `phys_uniq`). Command injection (`/tp`, gamerules) reuses the driver's existing `hw_xdo`-into-P1 technique from the 2026-07-17 A/B session (space every `/tp` a couple blocks apart per player; inject only in hands-off windows — racing live controller input can eat a command or misfire it). **`tests/benchmark/RUNBOOK.md` gained a new "Pre-flight — flight path scouting" section**: since `BenchWorld` is a fixed seed (`4815162342`), terrain along any start-point + bearing is deterministic and only needs checking once. Plan sequences players in one at a time on a single running session (1P → 2P → 3P → 4P — virtual pads claim slots first and stay connected the whole pass), scouting every bearing for mountains and recording confirmed start coordinates into a fill-in table (20 combinations total — every player's bearing changes at every N, so each stage re-checks all currently-loaded players, not just the newly-added one). **Next Deck sitting: run the pre-flight scouting pass**, then the campaign itself. | agent 4h+ · **Deck 3h+** |
217+
| **#70** benchmark pilot | **In progress — round 2 of the A/B campaign, sitting 1 of 4 done.** Round 1 (`docs/BENCH-AB-2026-07-18.md`) already has a MERGE verdict — PR #94 was merged on it — but used blind teleports as a chunk-load proxy since one human can't pilot 4 players at once. Round 2 uses the virtual-pad rig for genuine simultaneous piloted flight. Pre-flight scouting (all 20 bearing/start combos) completed 2026-08-03. `tests/benchmark/RUNBOOK.md` was reorganized 2026-08-05 into 4 sittings with an explicit physical-presence table and progress checklist (old version archived as `RUNBOOK-20260805.md`). **Sitting 1 done** (2026-08-04/05): since PR #94 already merged, "the existing install" was no longer a valid baseline — Phase A now installs fresh from commit `2d5d321` (pre-#94) with `REPO_REF` pinned (a same-day-unrelated commit changed the account-profile-name schema, which broke the install until pinned); MC version explicitly pinned to 26.2; MangoHud probe PASSED; settings standardized across all 4 instances. Also surfaced a real unpinned-dependency gap: PolyMC's own binary (`download_prism_launcher`) always fetches GitHub's "latest", built from a moving `develop` branch with no commit pinning — round 1 and round 2 are running different, unverifiable PolyMC builds; SHA256 of round 2's build recorded in RUNBOOK.md, not yet fixed in code. **Remaining: sitting 2** (Phase A scored cycles, 1P→4P), **sitting 3** (Phase B reinstall from current `main` + Phase C scored cycles), **sitting 4** (Phase D comparison + gate evaluation + merge/no-merge decision — driver-only, no physical presence needed). | agent 4h+ · **Deck 3h+ remaining across sittings 2-3** |
218218

219219
**Remaining after PR-5: agent 4h+ · Deck 3h+** (#70 only — PR-1 through PR-5 actuals all folded in above; PR-5 ran well over its original estimate after the FIFO message-loss bug surfaced underneath the original fix, plus two Deck-instability retries).
220220

0 commit comments

Comments
 (0)