# Phase 10: Feasibility Spike - Context

**Gathered:** 2026-03-24
**Status:** Ready for planning

<domain>
## Phase Boundary

Confirm property-based IPD works from the sidecar driver, resolving all go/no-go unknowns before implementation begins. Three specific feasibility tests (FEAS-01, FEAS-02, FEAS-03) plus a documented go/no-go decision. No production features — spike code that evolves into production.

</domain>

<decisions>
## Implementation Decisions

### Spike methodology
- All three FEAS tests implemented as pipe commands via the existing named pipe infrastructure
- `ipd_test <mm>` — sets Prop_UserIpdMeters_Float on HMD container (FEAS-01)
- `eyetohead_check` — reads current EyeToHead transforms to verify IPD change took effect (FEAS-02)
- `slider_test` — sets IpdUIRange properties on HMD container (FEAS-03)
- Verification is programmatic: set IPD via pipe command, read back EyeToHead transforms to confirm change
- Test values use the Beyond 2 official range: 48mm-75mm (matching Beyond Utility)
- Spike code evolves into production — `ipd_test` becomes basis for Phase 11's `ipd` command, `eyetohead_check` stays as debug diagnostic

### Lighthouse driver interaction
- Approach: empirical test (set IPD, read transforms back) + SteamVR log analysis for SetDisplayEyeToHead calls
- If lighthouse driver blocks property-based IPD: document the finding and defer workaround to Phase 11 (spike identifies blocker, doesn't solve it)

### Slider UI trigger test
- Set IpdUIRangeMin=0.048, IpdUIRangeMax=0.075 (full Beyond 2 range)
- Test Prop_DriverDisplaysIPDChanges_Bool with both true and false values, document effect on slider visibility
- Add VREvent_IpdChanged detection to RunFrame during spike — log event to confirm mechanism for Phase 12
- Spike is fully automatable: agents can launch SteamVR, run pipe commands via beyond_prox_ctl, use screenshot tool to check for slider, and read logs — no manual user testing required

### Go/no-go criteria
- **FEAS-01 passes + slider works** = Full go for Phases 11-13
- **FEAS-01 passes + slider fails** = Go for Phase 11 (pipe-only IPD). Phase 12 (slider) gets descoped or redesigned
- **FEAS-01 fails** = No-go. Document failure and alternative strategies (SetDisplayEyeToHead, VRSettings). Defer to new investigation phase
- Findings documented in FINDINGS.md in the phase 10 directory with structured results per FEAS requirement

### Claude's Discretion
- Whether to read EyeToHead transforms from driver side (VRServerDriverHost) or client side (beyond_prox_ctl via IVRSystem) — pick whichever is more practical given API access
- Exact spike command syntax and response format
- Order of tests during execution
- SteamVR log parsing approach for SetDisplayEyeToHead detection

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### OpenVR API
- `extern/openvr/headers/openvr_driver.h` — Property IDs (Prop_UserIpdMeters_Float=2003, Prop_IpdUIRangeMinMeters_Float=2100, Prop_IpdUIRangeMaxMeters_Float=2101, Prop_DriverDisplaysIPDChanges_Bool=2108), SetFloatProperty API, SetDisplayEyeToHead on IVRDriverDirectModeComponent, VREvent_IpdChanged=105, VREvent_Ipd_t struct
- `extern/openvr/samples/drivers/drivers/simplehmd/src/hmd_device_driver.cpp` — Reference implementation of SetFloatProperty(Prop_UserIpdMeters_Float) on HMD container

### Existing driver code
- `src/driver/device_provider.cpp` — Current pipe command handler (HandlePipeCommand), HMD container access pattern (TrackedDeviceToPropertyContainer), SetBoolProperty pattern to extend
- `src/driver/device_provider.h` — Class structure, pipe server members
- `src/ctl/main.cpp` — CLI tool for pipe commands

### Requirements
- `.planning/REQUIREMENTS.md` — FEAS-01, FEAS-02, FEAS-03 definitions

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `DeviceProvider::SetHmdProximity()` (device_provider.cpp:291-308) — Pattern for getting HMD container handle and setting properties. Directly extensible to SetFloatProperty
- `DeviceProvider::HandlePipeCommand()` (device_provider.cpp:180-273) — Pipe command dispatcher. New spike commands follow same pattern (strcmp + snprintf response)
- `beyond_prox_ctl.exe` (src/ctl/main.cpp) — CLI tool for sending pipe commands. Already supports command-response exchange
- Named pipe `\\.\pipe\beyond_proximity_ctl` — Existing infrastructure, PIPE_TYPE_MESSAGE with disconnect-per-command pattern

### Established Patterns
- HMD container access: `VRProperties()->TrackedDeviceToPropertyContainer(k_unTrackedDeviceIndex_Hmd)` — proven pattern from v1.0
- Property write: `VRProperties()->SetBoolProperty(hmdProps, prop, value)` — extend to SetFloatProperty
- Pipe command format: `"command arg"` in, `"OK key=value"` or `"ERR message"` out
- State-change guard pattern in SetHmdProximity (only write on change) — reuse for IPD

### Integration Points
- `DeviceProvider::RunFrame()` — Where VREvent_IpdChanged polling would go (already polls pipe here)
- `DeviceProvider::HandlePipeCommand()` — Where new spike commands are added
- `DeviceProvider::Init()` — Where IpdUIRange properties would be set (after VR_INIT_SERVER_DRIVER_CONTEXT)

</code_context>

<specifics>
## Specific Ideas

- The spike should be fully executable by agents without human intervention — build, launch SteamVR, run commands, screenshot for slider, read logs
- Beyond 2 IPD range is 48-75mm per official Beyond Utility — this is the authoritative range for all IPD validation
- Spike code is not throwaway: `ipd_test` evolves into Phase 11's production `ipd` command, EyeToHead check stays as diagnostic

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 10-feasibility-spike*
*Context gathered: 2026-03-24*
