Goal
Create hydraheadquest: a head app for Meta Quest, equal in role to hydraheadipad. One APK. Deploy it once, then manage the fleet remotely. The headset enrolls in hydracluster, shows the experience catalog, and receives streams from bodies. Later it must also receive true immersive XR streams.
Findings
1. The APK is a head app, not hydranode
Heads are outbound clients. The iPads do not run hydranode, and hydraheadipad has no local listener. hydranode is the substrate for bodies and infra nodes (systemd, exec channel, provisioning roles). Android has no systemd and no arbitrary exec, and hydranode has no Android build target. All remote ops a head needs (screenshot, diagnostics, command poll) already exist in the head API that hydraheadipad uses. Conclusion: hydraheadquest is one Kotlin APK with the same shape as hydraheadipad.
2. What to port from hydraheadipad
The Hydra layer is small and well isolated:
- Enrollment: fleet QR with
{server_url, enrollment_token}, then POST /api/v1/heads. Persist {server_url, head_id, token}.
- Heartbeat:
PUT /api/v1/heads/{id} every 30 s (5 s while streaming) with status, diagnostics, version, latency.
- Catalog:
GET /api/v1/heads/{id}/experiences.
- Body selection:
GET /api/v1/bodies/eligible, pick first body with stream_count==0, probe LAN address first, then WireGuard address.
- Pairing: Sunshine GameStream on :47989, PIN via
POST https://host:47990/api/pin with Basic auth, unpair then repair on alreadyPaired.
- Command poll:
GET /api/v1/heads/{id}/commands every 3 s, screenshot upload via POST /api/v1/heads/{id}/screenshot.
- Stop:
DELETE /api/v1/heads/{id}/stream.
- Mic relay: mic to Opus to RTP on UDP :47995 (per experience flag).
The state machine to port literally is Sources/HydraHeadiPad/AppState.swift; the API client is Services/HydraClusterClient.swift.
Head naming: use quest-head-<6 chars of device id> from the first build. Never repeat the ipad-head HydraGuard collision (hydracluster #449).
3. Base client: fork moonlight-android-xr
github.com/Gilleece/moonlight-android-xr (v0.2) is a moonlight-android fork for Quest and Pico. It already provides: immersive resizable virtual screen, passthrough toggle, controller acts as mouse, hand tracking, 1440p default. Fork it into cederikdotcom and add the Hydra layer above. This streams the existing Windows Sunshine bodies with zero body-side changes. Our moonlight fork convention applies: commit directly to master on the fork.
WireGuard: on Android the wireguard-android library runs in-process via VpnService. No separate tunnel extension, no split signing (simpler than iOS). Risk to verify early: whether Horizon OS allows VpnService for MDM-deployed apps. Fallback: venue LAN plus hydraneck mesh routing, like the iPads.
4. MDM: no vendor certificate needed, unlike Apple
On Apple we needed a commercial MDM (SimpleMDM) because self-hosting requires an APNs MDM vendor certificate. Quest is different:
- Meta Horizon Managed Services (HMS) is free since 2026-02-20 and is required for managed Quests. Enroll in Shared Mode: no Meta accounts to manage.
- HMS has a built-in Device Manager. It deploys private apps to managed devices from a URL to a self-hosted APK. That is our own releases server: CI builds the APK, publishes to releases.experiencenet.com, HMS points at that URL. It can also block the public store and restrict which apps users see.
- Third-party MDMs (ManageXR, ArborXR, Intune, Omnissa, Ivanti) plug into HMS through Meta-approved APIs. Devices can only enroll into Meta-APPROVED third-party MDMs. We cannot register hydracluster as an MDM.
- A DIY device-owner agent (Android Enterprise style, like Headwind MDM) is not viable on Horizon OS:
dpm set-device-owner fails when accounts exist on the device, and Meta does not support it. Without device owner there are no silent APK installs.
Conclusion: build nothing MDM-side. Use free HMS built-in Device Manager for enrollment, APK deployment from our own URL, and store lockdown. Add ManageXR or ArborXR only if we hit a gap (deeper kiosk pinning, analytics). Document the Quest lane as a runbook in hydramdm, next to the SimpleMDM lane.
5. Immersive XR streams (later phase)
Two stream types must not be confused:
- Flat in VR: the current Sunshine output on a big virtual screen in passthrough. Phase 1 delivers this.
- True 6DoF immersive: the Unreal VR app runs on the body PC, headset sends poses up, receives stereo video down. This needs ALVR (Windows bodies, SteamVR) or WiVRn (Linux bodies, fits hydralinuxpipeline #508). WiVRn is Linux only; ALVR is the Windows option.
Do not merge an ALVR or WiVRn client into the hydraheadquest APK. Deploy both APKs via HMS; hydraheadquest launches the XR client by intent when an experience has stream_mode: xr. hydracluster needs the new stream_mode plus slot handling, which lines up with multi-stream #504.
Note: running Android APKs in an emulator on a body and streaming them is a dead end for XR. Emulators have no GPU XR runtime. The correct form of "run on PC and stream" is the ALVR/WiVRn model.
Plan
- Fork moonlight-android-xr to cederikdotcom/hydraheadquest. Add QR enrollment, heartbeat, catalog grid, eligible-body selection, Sunshine pairing, command poll. Smoke test against cosmic-pretzel-98.
- Set up Meta HMS org (Shared Mode). Enroll first Quest. Configure private app deploy from releases.experiencenet.com. CI lane: tag, build APK, publish, HMS picks it up.
- WireGuard: test wireguard-android VpnService on Horizon OS. If blocked, hydraneck routing fallback.
- hydramdm runbook: Quest lane (HMS enrollment, app deploy, store lockdown, recovery).
- Phase 3 (separate issue when reached): immersive via ALVR/WiVRn,
stream_mode: xr in hydracluster.
Open questions
- Which Quest hardware and how many units (Quest 3 or 3S)?
- HMS Shared Mode account and org setup: who owns the Meta business account?
- Kiosk depth: is HMS store lockdown enough, or do we need single-app pinning (third-party MDM feature)?
- Screenshot command on Quest: video surfaces may capture black, same as the Metal issue on iPad. May need MediaProjection.
Progress 2026-08-21
Phase 1 scaffold DONE and released as v0.1.0:
- Forked Gilleece/moonlight-android-xr to cederikdotcom/hydraheadquest (master, submodules intact).
- Hydra layer scaffolded in app/src/main/java/com/limelight/hydra/ (Kotlin): models, hydracluster client, head state machine ported from hydraheadipad AppState, enrollment activity (QR still TODO, adb intent extras work), config store. applicationId com.experiencenet.hydraheadquest.
- Authoritative API contract extracted from the iPad app into docs/hydra-api-contract.md. Runbook and testbook in docs/.
- CI: build.yml compiles on every master push (GREEN, full NDK build). release.yml on v* tags builds, signs when ANDROID_KEYSTORE_* secrets exist (not yet created), attaches the APK to the GitHub release, and publishes to releases.experiencenet.com when HYDRARELEASE_PUBLISH_TOKEN is set (not yet; job skips gracefully). v0.1.0 release carries hydraheadquest-v0.1.0.apk (unsigned).
Next: wire StreamHooks into the Moonlight core (NvHTTP pairing, NvConnection start), QR scanning, generate a release keystore and set the signing plus publish secrets, buy a Quest and run the testbook against cosmic-pretzel-98.
Progress 2026-08-21 (later)
v0.2.0 RELEASED, signed, feature parity except Phase 2 items:
- Signing: PKCS12 keystore generated (RSA 4096, valid to 2056), 4 GitHub secrets set, key backed up to the Storage Box next to the supervision identity (hydramdm-backups/hydraheadquest-signing/). v0.2.0 APK carries a verified v2 signature; adb install now works directly.
- QR enrollment: Camera2 + ZXing fleet QR scan with live preview; Quest 3/3S passthrough camera via horizonos.permission.HEADSET_CAMERA (Horizon OS v74+); older Quests fall back to manual entry; adb intent extras still work.
- Streaming wired to the real Moonlight core: NvHTTP pairing on :47989 with the PIN posted to Sunshine :47990, unpair-then-repair on already-paired, app lookup by experience name, resolution/bitrate injected via Moonlight prefs, Game/GameXR launch, stop, stream-end detection.
- Kiosk: HydraLaunchActivity (launcher "Hydra Kiosk") routes enrollment vs catalog; 3-column self-service grid; operator PIN 1337 gates diagnostics, Moonlight debug UI, and enrollment reset. PixelCopy screenshot command (streaming surface may capture black; MediaProjection TODO). Issue reporting posts here (project hydraheadquest).
- Still Phase 2: WireGuard (VpnService on Horizon OS unverified), mic relay, first-frame stream-established signal, MediaProjection screenshots.
Next: hardware. Buy a Quest 3/3S, enable developer mode, adb install hydraheadquest-v0.2.0.apk, run docs/testbooks/testbook.md against cosmic-pretzel-98. Then HMS org setup for the fleet lane.
Progress 2026-08-25: first real headset enrolled
Quest 2 (v76) enrolled as quest-head-36afcd (node-695ddd25), heartbeating with full diagnostics, catalog renders 4 experiences. Driven entirely over cluster exec on cranky-toaster-86 (the omarchy MacBook Air) with adb over USB.
Found and fixed on the way:
- v0.2.1: upstream pinned horizonos minSdkVersion 77; Quest 2 on v76 got INSTALL_FAILED_OLDER_SDK. Lowered floor to 74, target stays 77.
- App bug (open, fix in v0.2.2): after a fresh Enroll the tick/heartbeat loop does not start until an app restart; enrollment screen also does not navigate onward. Restarting the app worked.
- Horizon OS refuses activity starts while the headset is asleep; adb am start silently spawns nothing. Launch only with the headset worn.
- hydranode #548: exec timeout does not kill hung children (adb wedged all 4 exec workers; recovered by local pkill).
Streaming from a non-venue network is confirmed blocked as designed: head on home Wi-Fi, no WireGuard on Quest yet (Phase 2), so no body reachable. Options: test at a venue LAN, or stand up a temporary Sunshine body on the same LAN.
Progress 2026-08-25 (v0.4.0): first mesh stream, tick-kill bug found and fixed
WireGuard Phase 2 SHIPPED and WORKS on Horizon OS: VpnService + GoBackend run fine on Quest 2 (consent pre-grantable via adb appops ACTIVATE_VPN). quest-head-36afcd streams from chunky-turnip-23 over 10.10.0.0/16 from a home network.
First streams died after ~23 s. Root cause was OUR tick: the head's heartbeats materialize a stream block server-side; its stream_url_lan (venue LAN IP) never matches the mesh host the head streams from, so the Streaming tick read every mesh session as a changed assignment and killed plus relaunched GameXR (same stale-stream-block family as hydracluster #137). Fixed in v0.4.0: tick matches the body's full candidate host set plus app name; ended self-service sessions mark their block stale (delete, never relaunch). Contract doc corrected.
Also in v0.4.0: mesh hosts stream remote-tuned (packet size 1024, STREAM_CFG_REMOTE; path shows 42-146 ms jitter), tunnel MTU pinned 1420, connection terminations now log to logcat tag HydraStream, and a full VR UI overhaul (HydraUi layer: 2-column card catalog, keypad PIN, full-panel WireGuard/diagnostics screens).
Gotchas collected: Horizon OS launches nothing while the headset sleeps (adb keyevent KEYCODE_WAKEUP works); GameXR launched from a background thread gets REJECTED by SWMS and retried via vrshell pending intent ~23 s later.
Progress 2026-08-25 (v0.4.1): the real stream killer was hydrabody, plus VR-scale UI
Second round of ~20 s stream deaths (after our tick fix) traced BODY-side with the new HydraStream logcat instrumentation: errorCode=0 graceful termination, and chunky's hydrabody log showed the sunshine-api watchdog killing the experience ('[gpu-mismatch] GPU 98% with stream status idle') because every Sunshine API call returned 401. Three config gaps fixed: Sunshine on chunky had no admin account (--creds set), districts bxl1-test-2/-3 were missing from the hydracluster providers config (added, service restarted), and hydrabody needed a config_cache patch plus restart because hydranode only writes the cache at its own startup. Watchdog robustness bug filed as hydrabody #551. MTU theory tested and disproven (1420-byte packets traverse the tunnel); mesh path RTT 42-146 ms so remote tuning stays.
v0.4.1: HydraUi panel scale (~2.3x on the 3664 px Quest panel, 1x on phones) applied everywhere; default VR environment seeded to black void (upstream default decodes a 4096x2048 equirect photo layer, too heavy for Quest 2 while decoding 1080p60; in-stream picker still overrides). Flagged for later: upstream MiDaS depth model pref doubles the swapchain, worth defaulting off on Quest 2.
Progress 2026-08-25 (v0.5.0): first fully working streaming session, then VR polish
User confirmed: stream smooth and stable on the mesh after the v0.4.1 + hydrabody fixes. v0.5.0 polish, all standard OpenXR:
- Ambient Dusk: generated 1024x512 equirect gradient (HydraAmbient.kt) as picker entry and seeded default; migrates v0.4.1's void seed once.
- Curved screen: upstream already implements XR_KHR_composition_layer_cylinder; seeding seekbar_vr_curvature=100 gives radius = screen distance, flat-quad fallback intact.
- Look-around reprojection artifacts: depth source seeded off (upstream default runs a MiDaS TFLite pass and doubles the swapchain; heavy on Quest 2 and the artifact suspect).
- Hand menu: menu action bound on touch/simple/pico4 profiles plus the Quest system hand gesture via XR_FB_hand_tracking_aim MENU_PRESSED bit; opens the in-stream environment picker.
Installed on quest-head-36afcd, awaiting user verification.
Progress 2026-08-25 (v0.6.0): hand menu root cause, visible hands, exit button, FFR, landscape panels
- Dead hand menu ROOT CAUSE: the XR_FB_hand_tracking_aim state (carrying MENU_PRESSED) was read only as the right operand of a short-circuit OR with the trigger action; the ext/hand_interaction profile drives the trigger during a pinch, so the aim read was skipped on exactly the menu-pinch frames. Fixed: aim polled every frame; binding XrResults and per-second aim status now log under moonlight-xr.
- Hands-only exit guaranteed: floating X button on the move bar ends the stream via Game.finish().
- Visible hands: 26 joints per hand as translucent dots in a new half-res projection layer (the renderer previously had NO projection layer; everything is compositor layers).
- Fixed foveated rendering: XR_FB_foveation level HIGH on that projection layer (honest note: video/env are compositor layers, unaffected).
- Horizon 2D panels: default was phone-size 360x640 portrait; now landscape 1024x640dp with free resizing on the three Hydra activities.
- User confirmed earlier: depth-off removed the look-around reprojection artifacts.
v0.6.0 installed on quest-head-36afcd, awaiting verification. Repo note: a stray 47 MB gcc deb was amended out of history before the final tag.
Progress 2026-08-25 (v0.7.0): diagnostics parity
- In-stream stats panel (iPad parity): info button on the move bar toggles a side card with RTT+variance, FPS in/rendered, net drop %, host latency avg/min-max, decode time, codec, resolution, bitrate, route (mesh/lan), body host. Fed by Moonlight's existing 1 s stats window (was gated behind the flat perf-overlay pref, never visible in VR).
- Operator 5-step diagnostics run ported faithfully from DiagnosticsView.swift: Cluster connection, Experience catalog, Body available, Body reachable (using HydraState's real probe order), WireGuard routing (iPad probe semantics incl. hub 10.10.0.1 fallback, plus local tunnel state). Results appended to operator issue reports.
- User earlier confirmed: v0.6.0 hands visible and good; backgrounds fine once the depth reprojection was gone.
v0.7.0 installed on quest-head-36afcd. Separate background workstream running: Gallo-Romeins OpenXR experience into bxl1-test-2 + immersive streaming path to Quest 2 (Phase 3).
Progress 2026-08-25 (Phase 3 prep): gallo-romeins-museum in test district, ALVR server validated headless
Part A, experience available in bxl1-test-2:
- gallo-romeins-museum experience updated in the library: district bxl1-test-2, venue cloud-seven added (bxl1 production untouched). Build 2 (Perforce CL 5) build_url on nbg1 hydramirror verified downloadable (5.3 GB, sha256 84ad4acc...).
- quest-head-36afcd catalog (GET /api/v1/heads/node-695ddd25/experiences) now lists gallo-romeins-museum.
- chunky-turnip-23 provisioned build 2 in ~2 min and registered the Sunshine app.
- Layout mismatch found: the Perforce archive zip has a top-level Builds/ folder, but exe_path expects RomeinsMuseum.exe at the experience root. Fixed locally on chunky by moving Builds/* up one level. Do NOT change exe_path in the library: the production museum body still runs build 1 with the root layout and the exe-already-present skip protects it from a forced 5.3 GB re-download. Real fix belongs in the archive pipeline (strip the Builds/ prefix) or per-district exe_path.
- The title is a UE5 OpenXR VR build (openxr_loader.dll shipped). Launched flat it initializes OpenXR against whatever runtime is active.
Part B, immersive path (ALVR) prepared and tested as far as possible without a human in the headset:
- chunky inventory: RTX A5000, Win 11 26200, 51 GB free on C:. WDAC kernel CI enforced but user-mode CI OFF, no AppLocker rules, so new user-mode binaries run. Steam already installed at D:\Cederik\Programs\Steam with SteamVR, account manjok has RememberPassword + AllowAutoLogin. OpenXR ActiveRuntime already SteamVR. Oculus PC runtime services also present.
- Steam auto-login works with ZERO interaction: scheduled task (Interactive logon type, user Z2 CMT G9) runs steam.exe -silent, connection_log shows Logged On OK.
- ALVR streamer v20.14.1 installed portable at C:\hydra\alvr (session.json lives next to the exe). Firewall rules ALVR-hydra-udp/tcp for 9943,9944 added. Driver registration = external_drivers entry in the user openvrpaths.vrpath (backup at openvrpaths.vrpath.bak-pre-alvr).
- Validated end to end server-side: SteamVR started headless in session 1 and loaded driver_alvr_server.dll, virtual HMD active (serial 1WMHH000X00000), vrcompositor running, waiting for a client. No WDAC block, no login prompt, no human step.
- Quest 2: alvr_client_android.apk v20.14.1 (must match server version) sideloaded on quest-head-36afcd via the Air. Package alvr.client.stable, activity android.app.NativeActivity. Horizon OS refuses to bring it forward while the headset is not worn (prox_close broadcast is not enough on v76), so the client has not run yet.
- Interaction found: with the ALVR driver registered, RomeinsMuseum launched flat initializes OpenXR on SteamVR and renders to the virtual HMD (log: Initialized OpenXR on SteamVR/OpenXR runtime 2.14.5). So ALVR registration changes flat-launch behavior. Left DISABLED (vrpath restored); re-enable is one JSON edit. Everything else (files, firewall, scheduled tasks hydra-alvr-steam / hydra-alvr-dashboard / hydra-alvr-steamvr) left in place, inert.
- WiVRn stays Linux-only server-side (fits hydralinuxpipeline #508 later); ALVR is the Windows option, confirmed.
Remaining human steps for the first immersive stream:
- Wear the headset, open ALVR (or adb am start alvr.client.stable/android.app.NativeActivity while worn), read the client hostname shown on screen.
- On chunky: add that hostname with trusted=true and manual_ips [10.10.200.7] to client_connections in C:\hydra\alvr\session.json, re-enable the driver (external_drivers = C:\hydra\alvr), start tasks hydra-alvr-steam then hydra-alvr-dashboard then hydra-alvr-steamvr.
- Expect mesh RTT 42-146 ms jitter to be marginal for 6DoF; venue LAN is the real target. Note the device-wide hydraheadquest WireGuard VpnService should carry ALVR client traffic too (unverified).
Unrelated but noisy: hydrabody on chunky retries a sunshine provider update every 30 s and gets HTTP 404 on releases.experiencenet.com/providers/sunshine/latest/sunshine-windows.zip.
MILESTONE 2026-08-26: first immersive XR stream (Phase 3 proven)
gallo-romeins-museum streamed in full 6DoF from chunky-turnip-23 (SteamVR + ALVR v20.14.1) to quest-head-36afcd over the WireGuard mesh, off-venue. User confirmed in-headset. Chain: hydraheadquest v0.7.2 holds the tunnel (new HydraTunnelService foreground service fixed the LMK-kills-tunnel blocker), ALVR client launched with the VR intent category, client trusted via the dashboard API (POST /api/dashboard-request, X-ALVR header; hand-editing session.json gets reverted), museum launched via an Interactive-principal scheduled task.
Learned this session: schtasks /create /it from SYSTEM fails (no account mapping); use Register-ScheduledTask with New-ScheduledTaskPrincipal -LogonType Interactive. ALVR client must be started with -c com.oculus.intent.category.VR or vrshell rejects the placement. ALVR client cannot be exited with hand tracking alone (upstream gap; future hydra fork item). SteamVR/vrserver did not survive overnight; the future hydrabody alvr provider role must supervise it.
Next steps for productizing: stream_mode xr in hydracluster + catalog; hydraheadquest intent handoff (per the Phase 3 proposal in this issue); hydra ALVR client fork (auto-trust, no wizard, hands exit); hydrabody alvr/steamvr provider role; venue LAN validation for real latency.
Phase 3 policy decisions 2026-08-26 (with Cederik, post-milestone)
Architecture facts settled: ALVR is a SteamVR driver, not a runtime (game-facing side is OpenXR-agnostic only because SteamVR implements OpenXR+OpenVR; client side is a plain OpenXR app, Quest and Pico). WiVRn IS a runtime (Monado+streaming), Linux only. Monado-Windows builds but has no drivers, not production. CloudXR 6 evaluated and REJECTED: proprietary at the runtime layer and NVIDIA-only; we want AMD-capable bodies.
Per-headset policy:
- Pico (enterprise) = golden path: Pico Business Streaming 2.x, which bypasses SteamVR by default (native OpenXR streaming; desktop mode exists too). No Meta, no Steam, no accounts anywhere. Fleet management via Pico business tooling, not HMS.
- Quest = ALVR + SteamVR remains our driver. Requires a Meta account by platform nature plus the Steam chain.
- Strategic: WiVRn on Linux bodies (rides #508). Open, account-free, AMD-friendly, serves both headset families.
Steam login flows (no credential custody, QR only):
- Venue body account, one time: hydrabody steamvr role flags needs-steam-login; operator opens the body desktop stream; scans Steam's QR with the venue Steam mobile app; credential cache makes it silent forever (validated on chunky).
- Visitor bring-your-own-library session: stream_mode xr + visitor_login flag; body logs out to the QR screen, head streams it, visitor scans with their own phone; session end = guaranteed logout + credential cache wipe by the role.
Licensing posture: our own builds (non-Steam content) through SteamVR with one dedicated account per body = defensible (no account sharing, no store content exhibited). Steam-store titles for visitors would need Cafe Program / publisher LBE licensing. Visitor-QR sessions are personal use of their own account. Legal review before fleet scale.
Session-lifecycle note: doffing the headset pauses the ALVR client, the server disconnects in seconds and the experience exits (no bandwidth when unworn; ~30 Mbps constant down + 2-3 Mbps up while worn at defaults, ~90-100 GB per continuous 6 h). Productized role should add a doff grace period before tearing the experience down.
Issue set to file next: stream_mode xr in hydracluster+catalog+head handoff; hydrabody XR provider roles (pico-business-streaming first-class, alvr/steamvr with login flows and doff grace); hydraheadquest Pico port validation; hydra ALVR client fork (auto-trust, no wizard, hands-only exit); WiVRn Linux body spike.
Issue set filed 2026-08-26
- #556 hydracluster: stream_mode xr model, server-side driver selection (quest->alvr, pico->pbs), exclusive slots
- #557 hydrabody: XR provider roles, tri-mode graceful switching (Sunshine flat / ALVR+SteamVR / PBS), Steam QR login flows, doff grace, watchdog exemption; formalizes fluffy's co-located PBS setup; chunky is the reference tri-mode body
- #558 hydraheadquest: stream_mode xr handoff to the ALVR client
- #559 hydraheadpico: NEW head lane, Pico port with PBS handoff (account-free golden path)
- #560 hydraheadquest: Hydra fork of the ALVR client APK
- #561 hydralinuxpipeline: WiVRn Linux body spike (strategic)
- #562 hydraperforce: strip Builds/ prefix in archive step (bug found via gallo build 2)
Fleet note from Cederik: fluffy already runs the Pico Business runtime co-located with hydrabody (exe deployed by hydrabody, runtime on the same machine); there will be hydraheadpico AND hydraheadquest, different drivers, and bodies must serve both plus Sunshine flat with graceful switching.
Venue test session 2026-08-27 (cloud-seven): flat re-proven, XR one wall from working
Confirmed working at the venue: enrollment survives network moves, tunnel reconnects (handshakes from venue Wi-Fi 192.168.3.x), landscape panel fix works, flat Rupelmonde streams (after v0.8.1 fixed the REAL VerifyError: the v0.7.1 rewrite was insufficient; row builders now live in StreamDiagnostics with integer-math formatting so a rejected method can never poison the decoder class again; LESSON: verify flat by stream start, not app launch). New bug filed: kiosk overlay backdrop covered half the flat stream (hydrabody tracker).
XR path exercised end to end repeatedly: session create, directive, arming, driver register, dashboard start, clean teardown with reason propagation all work. Blocked on one comedy of errors: the alvr module had TWO hardcoded Steam paths (chunky's Steam is on D:); rc.3 fixed only the first; binary swaps lost races to the one-minute keepalive relauncher TWICE because the venue-uplink download was slow (deploy lesson: download and verify fully, disable the keepalive task, then swap). rc.4 (both sites use registry-based resolveSteamPath) is verified running on chunky by startup banner.
App v0.8.2 shipped: XR start wrapped in Throwable catch with on-screen error (the scheduled executor silently swallows task exceptions, cause of three no-trace hangs), step logging, Cancel buttons on all start overlays. Parked ready: stale sessions cleaned, driver disarmed, Sunshine serving flat, chunky on staging rc.4. Next tap of gallo should arm for real.