--- phase: 39-voice-polish plan: "02" subsystem: onboarding-voice tags: [hardware-detection, voice, onboarding, tdd] dependency_graph: requires: [] provides: [voice-capability-probe, hardware-aware-voice-step] affects: [server/src/services/hardware.ts, ui/src/components/onboarding/VoiceStep.tsx] tech_stack: added: [] patterns: [execFile-async-wrapper, tdd-red-green, conditional-ui-rendering] key_files: created: - server/src/__tests__/39-voice-hardware-probe.test.ts modified: - server/src/services/hardware.ts - ui/src/api/hardware.ts - ui/src/hooks/useHardwareInfo.ts - ui/src/components/onboarding/VoiceStep.tsx - ui/src/components/NexusOnboardingWizard.tsx decisions: - "Used execFile callback wrapper (not promisify) for consistent mocking in vitest — same pattern as voice-pipeline.ts" - "withVoiceTimeout wraps the entire detectVoiceCapability() call with 3s fallback to avoid slowing hardware detection" - "voiceCapability is optional (?) in the UI HardwareInfo type for backward compatibility if server not updated" - "VoiceStep does NOT auto-skip on insufficient hardware — user reads the note and skips manually" metrics: duration: "~4 minutes" completed_date: "2026-04-04" tasks_completed: 2 files_changed: 5 --- # Phase 39 Plan 02: Onboarding Voice Hardware Detection Summary Voice capability probe added to hardware service (whisper-cpp/whisper + piper binary detection with 2s timeout each), and VoiceStep updated with hardware-aware conditional UI based on detected capabilities. ## Tasks Completed | Task | Name | Commit | Files | |------|------|--------|-------| | 1 | Voice capability probe in hardware service | 175fde59 | server/src/services/hardware.ts, server/src/__tests__/39-voice-hardware-probe.test.ts | | 2 | VoiceStep hardware-aware UI with conditional enable/skip | 003d6416 | ui/src/api/hardware.ts, ui/src/hooks/useHardwareInfo.ts, ui/src/components/onboarding/VoiceStep.tsx, ui/src/components/NexusOnboardingWizard.tsx | ## What Was Built **Task 1 — Voice capability probe:** - Added `VoiceCapability` interface: `{ whisperAvailable, piperAvailable, voiceTierSufficient }` - Extended `HardwareInfo` with `voiceCapability: VoiceCapability` - `detectVoiceCapability()` probes whisper-cpp (falls back to whisper) and piper with 2s timeout each - `voiceTierSufficient` = true for apple_silicon/gpu, or cpu_only with >= 4GB free RAM - Wrapped in 3s `withVoiceTimeout()` to prevent voice probes from slowing hardware detection - GET /system/providers automatically includes voiceCapability (no route changes needed) - 6 TDD tests covering all binary combinations and tier sufficiency logic **Task 2 — Hardware-aware VoiceStep:** - VoiceStep accepts optional `voiceCapability` prop (backward-compatible) - Insufficient hardware: shows amber warning note + Skip button only (no Enable) - Binaries detected: shows green CheckCircle2 icons next to STT/TTS labels - Missing binaries but sufficient hardware: blue install note, outline Enable + Skip - No hardware info (loading/undefined): falls back to original mic-only behavior - NexusOnboardingWizard passes `hardwareInfo?.voiceCapability` to VoiceStep at step 4 ## Deviations from Plan None - plan executed exactly as written. ## Known Stubs None. ## Self-Check: PASSED Files created/modified: - FOUND: server/src/services/hardware.ts - FOUND: server/src/__tests__/39-voice-hardware-probe.test.ts - FOUND: ui/src/api/hardware.ts - FOUND: ui/src/hooks/useHardwareInfo.ts - FOUND: ui/src/components/onboarding/VoiceStep.tsx - FOUND: ui/src/components/NexusOnboardingWizard.tsx Commits: - FOUND: 1851c513 (TDD RED — failing tests) - FOUND: 175fde59 (TDD GREEN — hardware service implementation) - FOUND: 003d6416 (VoiceStep UI + wizard wiring) Tests: 6/6 passing