# Plan 03-07 — SteamVR Full-Restart UAT Record

**Phase:** 03 (auto-start)
**Plan:** 07 (retry thread + ordered shutdown + UAT)
**Rig:** Bigscreen Beyond + Windows 11 Pro (dev machine)
**UAT run:** 2026-04-23 (live, post-Task-1 commit `a6d2372`)
**Verdict:** ALL PROCEDURES PASS — 2 documented plan deviations (non-blocking), 0 gate failures.

---

## Summary

| Procedure | Scope | AUTO-IDs | Verdict |
|-----------|-------|----------|---------|
| A — Headless register/unregister exit codes | CLI fork (Plan 06) + registrar (Plan 04) | AUTO-02, AUTO-03 | PASS |
| B — Silent auto-launch cycle | Silent boot + balloon | AUTO-01, AUTO-06 | PASS |
| C — VREvent_Quit ≤2s clean exit | Ack-first + ordered teardown | AUTO-05 | PASS |
| D — 5-cycle #1547 drift characterization | Retry-thread self-heal | AUTO-04 | PASS (no drift observed across 5 cycles) |
| E — Focus-steal probe | `--minimized` single-instance skip | AUTO-06 | PASS |

**Final machine state:** MicMap manifest is REGISTERED (left registered after A5 for production-like state). `bigscreen.micmap` auto-launch toggle is ON.

---

## Procedure A — Headless register/unregister exit codes (AUTO-02, AUTO-03)

| Step | Expected | Observed | Verdict |
|------|----------|----------|---------|
| A1. SteamVR OFF → `--register-vrmanifest` | rc=1, log shows `VRInitError_Init_HmdNotFound` (plan assumption) | **rc=0**, log: `manifest registered + auto-launch enabled: bigscreen.micmap`, `appconfig.json` updated with MicMap path. No console window flashed. | PASS (with deviation) |
| A2. SteamVR ON → re-register | rc=0, log `manifest registered + auto-launch enabled: bigscreen.micmap` | rc=0, same log line, `appconfig.json` unchanged (idempotent re-register) | PASS |
| A3. SteamVR UI → Settings → Startup/Shutdown → Manage Startup Overlay Apps | `MicMap` row present, auto-launch toggle ON | `MicMap` row present, auto-launch toggle ON | PASS |
| A4. `--unregister-vrmanifest` (SteamVR running) | rc=0, log `manifest removed: bigscreen.micmap`, MicMap no longer in Startup Overlay Apps | rc=0, log `manifest removed: bigscreen.micmap`, `appconfig.json` MicMap entry gone, sibling entries preserved | PASS |
| A5. Re-register for B–E continuation | rc=0 | rc=0 | PASS |

### Deviation from plan expectation — A1 rc=0 instead of rc=1

**Plan text:** "With SteamVR NOT running → expect `rc=1` and log shows `VRInitError_Init_HmdNotFound`."

**Reality:** `VR_Init(&err, vr::VRApplication_Utility)` with `VRApplication_Utility` init mode **works offline**. The utility mode does not require vrserver — utility APIs talk directly to `vrpathreg` and `%LOCALAPPDATA%\openvr\openvrpaths.vrpath` / `appconfig.json` on disk. A1's register call succeeded on disk with no running vrserver.

**Assessment:** The plan's rc=1 expectation was wrong. Actual behavior (`rc=0` offline) is more robust — register/unregister work without SteamVR needing to be running, which is exactly what the installer needs for its `[Run]` post-install step (Phase 4). No code change required; the plan's UAT expectation was too pessimistic.

**Follow-up:** Update `.planning/phases/03-auto-start/03-07-PLAN.md` Procedure A step 1 to reflect rc=0 offline behavior the next time the plan is touched (cosmetic, non-blocking).

---

## Procedure B — Silent auto-launch cycle (AUTO-01 success #1, AUTO-06)

| Step | Expected | Observed | Verdict |
|------|----------|----------|---------|
| B1. Quit SteamVR, close MicMap | clean state | clean state | PASS |
| B2. Relaunch SteamVR → observe desktop during 10s boot | No console flashed, no focus steal | No console flashed, no focus steal | PASS |
| B3. `micmap.exe` in Task Manager within 5s of SteamVR ready | present | present | PASS |
| B4. Tray icon visible | yes | yes | PASS |
| B5. First silent launch balloon (title "MicMap", body "Running in the system tray…") | appears briefly unless Focus Assist ON | balloon fired once, single fire | PASS |
| B6. `%APPDATA%\MicMap\config.json` has `"shownTrayNotification": true` | yes; mtime matches first-run | `shownTrayNotification: true` present; mtime matches first-run timestamp | PASS |
| B7. Second SteamVR cold-cycle → tray icon re-appears, balloon does NOT fire again | yes | yes (AUTO-06 first-launch-once confirmed) | PASS |

**AUTO-01, AUTO-06 confirmed at integration-plus-live level.**

---

## Procedure C — VREvent_Quit ≤2s clean exit (AUTO-05)

| Step | Expected | Observed | Verdict |
|------|----------|----------|---------|
| C1. Quit SteamVR via its tray menu while MicMap running | `micmap.exe` disappears within ~2s | `micmap.exe` disappears from Task Manager within ~2s; Valve watchdog ceiling honored | PASS |
| C2. No "killed" / "unresponsive" dialog from SteamVR | absent | absent | PASS |
| C3. `%APPDATA%\MicMap\micmap.log` shows ordered teardown lines | present | **Deviation:** no file log exists — logging is stdout-only | PASS (alt evidence) |
| C4. No zombie `micmap.exe` left behind | absent | absent | PASS |

### Deviation from plan — log-file evidence path unavailable (plan gap)

**Plan text (Procedure C step 3):** "Open `%APPDATA%\MicMap\micmap.log` and scroll to the tail. Confirm teardown order lines appear in sequence."

**Reality:** MicMap's logger writes to stdout via `ConsoleLogger` (Plan 06 noted: "the default ConsoleLogger's stdout writes are dropped on the floor" under the WINDOWS GUI subsystem, which has no attached console). No file log sink has been wired yet. `%APPDATA%\MicMap\micmap.log` does not exist and never did.

**How teardown ordering was verified instead:**
1. Task Manager observation — `micmap.exe` exits within the Valve 2s watchdog ceiling, implying ack-first fired before any slow teardown step (otherwise SteamVR would have posted the "killed/unresponsive" dialog; it did not).
2. No zombie process — confirms shutdown() completed cleanly (audio + detector + driverClient + vrInput + tray all torn down).
3. Unit-level ordering evidence already exists: Plan 03-05 extracted `processVREventImpl` with the ack-first invariant locked into `test_vr_input_events_quit_ordering` (commits `62fe4a5`, `6b32b42`). That GREEN test proves `AcknowledgeQuit_Exiting()` is called **before** `notifyEvent(Quit)`.
4. Post-commit self-test: `ctest --test-dir build -C Release --output-on-failure` still 8/8 GREEN after Task 1 commit `a6d2372`.

**Assessment:** Non-blocking. Live symptom (no-zombie + under-watchdog exit) + unit-level ordering test cover AUTO-05. The missing file log sink is a documentation/observability gap, not a correctness gap.

**Follow-up suggestion (recommend for Phase 5 or a dedicated observability micro-plan):**
- Wire a file log sink at `%APPDATA%\MicMap\micmap.log` alongside the existing stdout logger (tee via a `FileLogger` implementing `ILogger`).
- One-time rotation on startup or size cap to avoid unbounded growth.
- This makes future UAT procedures auditable post-hoc without requiring live Task Manager observation.

---

## Procedure D — 5-cycle #1547 drift characterization (AUTO-04)

| Cycle | MicMap auto-launched | Toggle still ON in Startup Overlay Apps | Notes |
|-------|----------------------|-----------------------------------------|-------|
| 1 | yes | yes | baseline |
| 2 | yes | yes | no drift |
| 3 | yes | yes | no drift |
| 4 | yes | yes | no drift |
| 5 | yes | yes | no drift |

**Drift count across 5 cycles: 0.** OpenVR issue #1547 did not manifest on this run. The retry thread self-heal path was NOT exercised (because nothing drifted), but the thread is present and will fire on every GUI boot per Task 1 `initialize()` code path. Retry-thread correctness is covered by Plan 03-04's `test_manifest_registrar` unit tests (5/5 GREEN, 2.43s including Case 2 poll-timeout).

**Informational note for ROADMAP:** A zero-drift observation across 5 cycles is not proof that #1547 is extinct — just that it did not manifest in this window. The self-heal path remains the defense-in-depth mitigation. If users file drift reports post-ship, escalate to a v1.x "Re-register" UI button per the original Phase 3 research spike.

---

## Procedure E — Focus-steal probe (AUTO-06)

| Step | Expected | Observed | Verdict |
|------|----------|----------|---------|
| E1. Close MicMap + SteamVR | clean state | clean state | PASS |
| E2. Foreground VS Code (non-game app), type to confirm focus | focus on VS Code | focus on VS Code | PASS |
| E3. Relaunch SteamVR; keep focus on VS Code while it boots | VS Code retains focus | VS Code retained focus throughout SteamVR boot | PASS |
| E4. When MicMap auto-launches silently: no window snap to MicMap | no snap | no snap; foregrounded app retained focus when SteamVR auto-launched MicMap | PASS |

**AUTO-06 focus-steal probe: PASS.** `--minimized` single-instance skip (Plan 06 D-08) + silent-boot window policy (D-06) + NIIF_RESPECT_QUIET_TIME balloon (D-10) cooperate correctly.

---

## Deviations Summary

| # | Procedure | Type | Severity | Disposition |
|---|-----------|------|----------|-------------|
| 1 | A1 | Plan expectation wrong (rc=1 offline) | cosmetic | Documented; no code change; update PLAN text on next touch |
| 2 | C3 | Plan assumed file log at `%APPDATA%\MicMap\micmap.log` (doesn't exist) | observability gap | Alt evidence used (Task Manager + unit tests); follow-up: wire file log sink in Phase 5 or a dedicated micro-plan |

Both deviations are **non-blocking** for Phase 3 closure. No procedure failed a correctness gate.

---

## Follow-up Items (informational; not Phase-3 blockers)

1. **File log sink** — `%APPDATA%\MicMap\micmap.log` via a `FileLogger` implementation of `ILogger`, teed alongside the existing `ConsoleLogger`. Unblocks post-hoc UAT auditing and field-report diagnostics. Candidate for a dedicated micro-plan OR folded into Phase 5 DOC-02 (architecture docs) if a logging section is added.
2. **PLAN text cleanup** — Update `.planning/phases/03-auto-start/03-07-PLAN.md` Procedure A step 1 to say "expect rc=0 even when SteamVR is not running (VRApplication_Utility is offline-capable)" the next time this plan file is edited. Cosmetic.

---

## Exit Criterion Statement

**Phase 3 exit criterion (`03-07-PLAN.md` success_criteria):**
- [x] Re-registration on every GUI boot via `std::thread` + `std::atomic` (NOT `std::async`) — commit `a6d2372`.
- [x] `MicMapApp::shutdown()` implements D-12 ordered teardown — commit `a6d2372`.
- [x] All exit paths converge on `running=false` → `shutdown()` (D-14) — commit `a6d2372`.
- [x] SteamVR full-restart UAT passes: silent auto-launch, no focus steal, balloon once, clean exit within 2s — this document (Procedures A–E all PASS).
- [x] AUTO-01 (manifest shipped + in SteamVR Startup list), AUTO-04 (self-heal), AUTO-05 (ack + ordered teardown) all closed.
- [x] Phase 3 ready for `/gsd-verify-work`.

**Verdict: Phase 3 complete. Ready for code review + verifier.**

---

*UAT run: 2026-04-23, Bigscreen Beyond + Win11 rig*
*Recorded by continuation executor after live user verification*
