# Phase 13: Slider Persistence and Hardening - Context

**Gathered:** 2026-03-28
**Status:** Ready for planning

<domain>
## Phase Boundary

IPD changes persist to the headset's lighthouse config so they survive SteamVR restarts. The driver writes the updated IPD value on SteamVR shutdown. All persistence code is behind a compile-time flag so it can be completely disabled if the external Beyond Utility app handles persistence instead.

</domain>

<decisions>
## Implementation Decisions

### Persistence trigger
- **D-01:** Persist IPD on SteamVR shutdown only (VREvent_Quit / Cleanup), not on every slider change
- **D-02:** Persist from any IPD source (slider and pipe command), not just slider-originated changes
- **D-03:** Only persist if IPD actually changed during the session (compare against startup value)

### Write mechanism
- **D-04:** Use lighthouse_console.exe `uploadconfig` to write to headset flash — config.json on disk is just a backup never actively read by anything
- **D-05:** Research alternative persistence methods that avoid tracking disruption — the Valve Index persists IPD without tracking loss; investigate how SteamVR/lighthouse driver handles this (SetFloatProperty persistence? steamvr.vrsettings? direct flash protocol?) before committing to lighthouse_console
- **D-06:** lighthouse_console located via openvrpaths.vrpath → runtime key → `tools/lighthouse/bin/win64/lighthouse_console.exe` (same file already read for config dir in Phase 11)

### Process spawning
- **D-07:** Blocking CreateProcess with short timeout (~10s) — Cleanup() has a window before vrserver exits
- **D-08:** Log success/failure of persistence but never block SteamVR shutdown (per HARD-03)

### Serial identification
- **D-09:** Use tracking/lighthouse serial (m_sTrackingSerial from Prop_SerialNumber_String) for lighthouse_console device identification — same serial used for lhr-<serial> folder matching

### Build isolation
- **D-10:** All persistence code wrapped in `#ifdef ENABLE_IPD_PERSIST` preprocessor guard
- **D-11:** CMake option `ENABLE_IPD_PERSIST` defaulting to ON, easily toggled OFF
- **D-12:** If the Beyond Utility app takes over persistence, this entire feature can be compiled out cleanly

### Claude's Discretion
- Exact lighthouse_console command-line arguments (research needed)
- How to update default_mm in the config payload for uploadconfig
- Error message wording for persistence failures
- Whether to also update config.json on disk as a secondary backup
- Internal code organization for the persistence path

</decisions>

<specifics>
## Specific Ideas

- The Valve Index persists its IPD value without ever losing tracking — research how it achieves this. If there's a SteamVR-native persistence mechanism (property persistence, vrsettings), prefer that over lighthouse_console
- Persistence may ultimately be handled entirely by the external Beyond Utility app, so the driver implementation should be cleanly removable
- config.json on disk is never actively read — it's a backup from the last lighthouse_console downloadconfig

</specifics>

<canonical_refs>
## Canonical References

### Prior phase context
- `.planning/phases/11-core-ipd-pipe-command/11-CONTEXT.md` — Config dir discovery, lighthouse serial extraction, openvrpaths.vrpath parsing, load_lh_config fallback
- `.planning/phases/12-steamvr-ipd-slider-ui/12-CONTEXT.md` — ApplyIpd shared helper, VREvent_IpdChanged handling, HMD serial for Beyond 1 detection

### Requirements
- `.planning/REQUIREMENTS.md` — SLIDER-03 (persistence), SLIDER-04 (serial from HID), HARD-03 (persistence failures non-blocking)

### Existing implementation
- `src/driver/device_provider.cpp` — ApplyIpd(), LoadLighthouseConfig(), HandleIpdSet(), VREvent_IpdChanged handler, Cleanup(), openvrpaths.vrpath parsing
- `src/driver/device_provider.h` — m_sTrackingSerial, m_fCurrentIpd, m_bHmdSerialChecked

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `LoadLighthouseConfig()` / `ExtractVrPathValue()` — already parse openvrpaths.vrpath and discover config directory; reuse for locating lighthouse_console.exe in the same SteamVR runtime tree
- `m_sTrackingSerial` — already populated from Prop_SerialNumber_String during config loading
- `m_fCurrentIpd` — tracks current IPD in meters; compare against startup value to detect changes

### Established Patterns
- `ApplyIpd()` with loop guard — existing pattern for safe IPD mutation; persistence hooks into the same flow
- `ExtractVrPathValue()` — JSON key extraction from openvrpaths.vrpath; extend to extract `runtime` key for lighthouse_console path
- Phase 11 decision: no lighthouse_console for reading — but Phase 13 uses it for writing (different use case, consistent reasoning)

### Integration Points
- `Cleanup()` in device_provider.cpp — natural hook point for shutdown persistence
- `VREvent_Quit` handling in RunFrame() — alternative trigger point
- CMakeLists.txt — add `ENABLE_IPD_PERSIST` option and compile definition

</code_context>

<deferred>
## Deferred Ideas

- Beyond Utility app handling all IPD persistence externally — may supersede this entire phase; build flag isolation covers this scenario
- Direct HID flash write protocol (bypassing lighthouse_console entirely) — potential future optimization if tracking disruption is unacceptable

</deferred>

---

*Phase: 13-slider-persistence-hardening*
*Context gathered: 2026-03-28*
