# Phase 3: Proximity Component Spike - Context

**Gathered:** 2026-03-21
**Status:** Ready for planning

<domain>
## Phase Boundary

Validate that a `/proximity` boolean component on the sidecar GenericTracker triggers SteamVR standby/wake behavior for the lighthouse-owned Beyond 2 HMD. This is the highest-risk feasibility gate — no documentation confirms this works. Uses hardcoded proximity toggle via debug control channel (no real HID data). If the sidecar approach fails, a structured checklist of alternative API approaches is explored before declaring failure.

</domain>

<decisions>
## Implementation Decisions

### Toggle mechanism
- Named pipe debug control channel: driver listens on a named pipe for commands from an external CLI tool
- CLI tool (`beyond_prox_ctl`) built from the same CMake project, sends commands like `proximity on`, `proximity off`, `status`
- Supports both write commands (set proximity state) and read queries (get current state back)
- This debug control channel persists beyond the spike — kept as a developer tool through later phases for testing HID pipeline, proximity algorithm, etc. without needing the headset on/off head

### Fallback strategy
- Primary attempt: `/proximity` boolean component on sidecar GenericTracker
- If primary fails: explore alternative SteamVR APIs before committing to a heavier fallback (e.g., Prop_ContainsProximitySensor cross-device, IVRServerDriverHost activity level APIs, cross-device property writes)
- Structured checklist approach: define 3-4 specific approaches to try. If all fail, spike is conclusive with documented results
- Ultimate fallback not locked in — both override driver (HMD registration) and firmware change (Plan C) remain viable; decision deferred to after spike results inform which makes more sense

### Verification approach
- Layered: scripted checks first, then manual UAT confirmation
- **Scripted layer:** PowerShell script queries `GetTrackedDeviceActivityLevel()` for the HMD after pipe toggle commands, and parses vrserver log for standby/wake transitions
- **Manual UAT — key success signal:** SteamVR dashboard does NOT reset its location while proximity=true and headset is motionless. Today, the dashboard always resets because SteamVR assumes proximity=false at all times (no proximity sensor communicated). Success means we've broken that cycle.
- Note: Beyond 2 display going dark/light is firmware-controlled and is NOT affected by this project. The effects to verify are OpenVR API proximity state readback and SteamVR dashboard standby behavior.

### Claude's Discretion
- Whether to set `Prop_ContainsProximitySensor_Bool` on the sidecar device, the HMD, or both
- Named pipe path and protocol format (text-based commands are fine)
- Exact sequence of alternative API approaches in the structured checklist
- Component path naming (e.g., `/proximity` vs `/input/proximity/click`)

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### OpenVR driver API
- `extern/openvr/headers/openvr_driver.h` — `CreateBooleanComponent` (line 3710), `Prop_ContainsProximitySensor_Bool` (line 408, prop 1025), `IVRDriverInput` interface
- `extern/openvr/samples/drivers/drivers/simplehmd/src/hmd_device_driver.cpp` — Reference for `CreateBooleanComponent` usage pattern with `/input/*` paths
- `extern/openvr/samples/drivers/drivers/tutorial/src/controller_device.cpp` — Additional `CreateBooleanComponent` + `UpdateBooleanComponent` usage

### Existing driver code (Phase 1 & 2 output)
- `src/driver/proximity_device.h` — ProximityDevice class, needs /proximity component added
- `src/driver/proximity_device.cpp` — Activate() method where component creation goes (line 14-39)
- `src/driver/device_provider.h` — DeviceProvider class
- `src/driver/device_provider.cpp` — Init/RunFrame lifecycle, HID initialization

### Firmware reference (proximity algorithm context)
- `code_samples/beyond_firmware/src/Devices/prox_control.c` — Firmware proximity detection algorithm
- `code_samples/beyond_firmware/src/Devices/prox_control.h` — Proximity constants

### Project requirements
- `.planning/REQUIREMENTS.md` — FEAS-03, INTG-01 define Phase 3 requirements

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `ProximityDevice::Activate()` — Already sets properties; `/proximity` component creation goes here
- `DeviceProvider::RunFrame()` — Currently empty; will poll named pipe and call `UpdateBooleanComponent`
- `scripts/verify_driver.ps1` — Existing verification pattern to extend with standby/wake checks
- `src/hid/hid_device.h` — RAII HID wrapper (not needed for Phase 3 but establishes code style)

### Established Patterns
- CMake build with vendored dependencies (OpenVR SDK, HIDAPI in extern/)
- Atomic PowerShell verification scripts with numbered checks
- DriverLog for all driver-side logging
- RAII wrappers for external resources (HidDevice pattern)

### Integration Points
- `ProximityDevice::Activate()` — Add `CreateBooleanComponent` call for `/proximity`
- `DeviceProvider::RunFrame()` — Poll named pipe, call `UpdateBooleanComponent` on state change
- `CMakeLists.txt` — Add `beyond_prox_ctl` as second build target
- Named pipe created in driver Init, listened in RunFrame or background thread

</code_context>

<specifics>
## Specific Ideas

- The debug control CLI (`beyond_prox_ctl`) should be a first-class development tool, not throwaway scaffolding — it will be valuable through HID pipeline and proximity algorithm phases for testing without wearing the headset
- Current SteamVR behavior with Beyond 2: dashboard location resets when headset is motionless because SteamVR assumes proximity=false (no sensor communicated). The spike succeeds when this reset STOPS happening with proximity=true
- OpenVR SDK samples only use `/input/*` component paths — `/proximity` as a component path on a non-HMD device is uncharted territory, which is exactly why this is a spike

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 03-proximity-component-spike*
*Context gathered: 2026-03-21*
