Skip to content

Plan: Fail Fast When Glasses Not Connected in Glasses Mode

Plan Metadata

  • 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.