Plan: Fail Fast When Glasses Not Connected in Glasses Mode
- Plan type:
plan - Parent plan: N/A
- Depends on: N/A
- Status:
draft
System Intent
- What is being built: A fail-fast guard in the record button flow that detects when the app is in glasses mode but no glasses are connected, and opens the audio selection bottom sheet instead of attempting a doomed recording.
- Primary consumer(s): Mobile app user pressing the record button
- Boundary:
CaptureButton.handlePress → fail-fast check → openDeviceSheet (Footer callback). No changes to recording orchestration or device-preference library.
Stage Gate Tracker
- [ ] Stage 1 Mermaid approved
- [ ] Stage 2 Flows approved
- [ ] Stage 3 Logs + Deployment approved or skipped
Mermaid Diagram
graph TD
User([User presses Record button]):::unchanged --> CB[CaptureButton.handlePress]:::changed
CB --> AlreadyRecording{Already recording?}:::unchanged
AlreadyRecording -->|Yes| StopRec[Stop recording]:::unchanged
AlreadyRecording -->|No| GlassesCheck{Glasses mode\nselected?}:::created
GlassesCheck -->|No — phone mode| StartRec[startRecordingFromControl]:::unchanged
GlassesCheck -->|Yes| AvailCheck{Glasses\nconnected?}:::created
AvailCheck -->|Yes| StartRec
AvailCheck -->|No| Log[Log: capture_glasses_unavailable]:::created
Log --> OpenSheet[openDeviceSheet — open audio picker bottom sheet]:::created
Footer[Footer.tsx]:::changed -->|passes onGlassesUnavailable=openDeviceSheet| CB
classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
classDef changed fill:#ffd966,stroke:#666,stroke-width:1px;
classDef created fill:#a8e6a3,stroke:#666,stroke-width:1px;
Flows
Global Types
StandardError {
message: string (human-readable description of what went wrong)
}
Flow: recordPress
- Test files:
main/app/__tests__/capture-button.test.tsx, main/app/__tests__/footer.test.tsx - Core files:
main/app/components/CaptureButton.tsx, main/app/components/footer.tsx
Types
CaptureButtonProps {
onGlassesUnavailable?: () => void (optional callback; opens device picker sheet)
}
SelectedDevice = "glasses" | "glasses-audio" | "phone" | ...
Paths
| path | input | output | path-type | notes | updated |
recordPress.alreadyRecording | button press | stops recording | happy path | existing behavior unchanged | |
recordPress.phoneMode | button press + phone selected | starts recording | happy path | existing behavior unchanged | |
recordPress.glassesConnected | button press + glasses selected + glasses connected | starts recording | happy path | existing behavior unchanged | |
recordPress.glassesUnavailable | button press + glasses selected + glasses NOT connected | opens device picker sheet | happy path (new) | fail-fast — no recording attempt | |
Pseudocode
handlePress():
if recording:
stopRecordingFromControl()
return
glassesMode = selectedDevice === "glasses" || selectedDevice === "glasses-audio"
if glassesMode && !isGlassesDeviceAvailable():
logger({ step: "capture_glasses_unavailable" })
onGlassesUnavailable?.()
return
startRecordingFromControl()
Logs
| Source | Location |
capture_glasses_unavailable | Existing in-app logger (same as capture_starting, capture_start_error) |
Deployment
- Mechanism:
local only - Deploy command:
cd main && npx expo start
- Notes: React Native app; no backend deploy required. Tests run with
npm test from main/.
Files Changed Summary
| File | Change |
main/app/components/CaptureButton.tsx | Add onGlassesUnavailable prop + useSelectedRecordingDevice hook + fail-fast guard with logging |
main/app/components/footer.tsx | Pass openDeviceSheet as onGlassesUnavailable to CaptureButton |
main/app/__tests__/capture-button.test.tsx | Mock recording-device-preference; add 2 new test cases for fail-fast path |
main/app/__tests__/footer.test.tsx | Upgrade CaptureButton mock to functional component; add 1 new test case |
N/A — no linked plans.