Auto Phone Mode On Launch
Plan Metadata
- Plan type:
plan - Parent plan: N/A
- Depends on: N/A
- Status:
draft
Status semantics: - draft: Plan is being created or updated and is not final. - approved: Plan is approved but not yet applied in code. - documentation: Code currently exists and matches the plan contract.
Update rule: - When an existing plan is edited, set status to draft until re-approved.
System Intent
- What is being built: On every app launch (after wearables initialize), detect whether the glasses are currently connected. If glasses are not connected, automatically override the saved recording device preference to
"phone". If glasses are connected, honour the saved preference as-is. - Primary consumer(s):
AppInitcomponent (runs the initialization sequence);recording-device-preferencemodule (owns the preference state). - Boundary (black-box scope only):
WearablesModule.isAnyDeviceAvailable()is the glasses-connected signal — its internals are out of scope.AsyncStorageis the persistence layer — its internals are out of scope.
Stage Gate Tracker
- [x] Stage 1 Mermaid approved
- [x] Stage 2 Flows approved
- [ ] Stage 3 Logs + Deployment approved or skipped
Mermaid Diagram
graph TD
AppInit["AppInit\n(component)"]:::unchanged
hydrateRecordingDevicePreference["hydrateRecordingDevicePreference()\nrecording-device-preference.ts"]:::changed
AsyncStorage["AsyncStorage\n(persistence)"]:::unchanged
WearablesModule["WearablesModule.isAnyDeviceAvailable()\n(native bridge)"]:::unchanged
RecordingDeviceState["selectedRecordingDevice\n(in-memory store)"]:::unchanged
AppInit -->|"calls on init"| hydrateRecordingDevicePreference
hydrateRecordingDevicePreference -->|"read saved preference"| AsyncStorage
AsyncStorage -->|"storedDevice or null"| hydrateRecordingDevicePreference
hydrateRecordingDevicePreference -->|"check connection"| WearablesModule
WearablesModule -->|"isConnected: bool"| hydrateRecordingDevicePreference
hydrateRecordingDevicePreference -->|"set device (phone override if not connected)"| RecordingDeviceState
classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
classDef changed fill:#a8e6a3,stroke:#666,stroke-width:1px; Flows
Global Types
RecordingDevice = "glasses" | "glasses-audio" | "phone"
StandardError {
message: string (human-readable description of what went wrong)
}
Flow: hydrateRecordingDevicePreference
- Core files:
main/app/lib/recording-device-preference.ts - Test files:
main/app/__tests__/recording-device-preference.test.ts(new file)
Types
HydrateInput {
// no arguments — reads AsyncStorage and WearablesModule internally
}
HydrateOutput {
// void — side-effects only: updates selectedRecordingDevice in-memory and emits to listeners
}
Paths
| path | input | output | path-type | notes | updated |
|---|---|---|---|---|---|
hydrate.glasses-connected-saved-glasses | stored = "glasses", isAnyDeviceAvailable = true | selectedRecordingDevice = "glasses" | happy path | Glasses are connected; stored glasses preference is kept | yes |
hydrate.glasses-connected-saved-glasses-audio | stored = "glasses-audio", isAnyDeviceAvailable = true | selectedRecordingDevice = "glasses-audio" | happy path | Glasses are connected; stored glasses-audio preference is kept | yes |
hydrate.glasses-connected-saved-phone | stored = "phone", isAnyDeviceAvailable = true | selectedRecordingDevice = "phone" | happy path | User explicitly chose phone; kept even though glasses are connected | yes |
hydrate.glasses-not-connected-saved-glasses | stored = "glasses", isAnyDeviceAvailable = false | selectedRecordingDevice = "phone" | happy path (new override) | Glasses not connected; override saved glasses preference to phone | yes |
hydrate.glasses-not-connected-saved-glasses-audio | stored = "glasses-audio", isAnyDeviceAvailable = false | selectedRecordingDevice = "phone" | happy path (new override) | Glasses not connected; override saved glasses-audio preference to phone | yes |
hydrate.glasses-not-connected-saved-phone | stored = "phone", isAnyDeviceAvailable = false | selectedRecordingDevice = "phone" | happy path | Already phone; no change needed | yes |
hydrate.no-saved-preference-glasses-connected | stored = null, isAnyDeviceAvailable = true | selectedRecordingDevice = "glasses" (default) | happy path | No stored preference; default is glasses when connected | yes |
hydrate.no-saved-preference-glasses-not-connected | stored = null, isAnyDeviceAvailable = false | selectedRecordingDevice = "phone" | happy path | No stored preference; default is phone when not connected | existing behavior, unchanged |
hydrate.storage-read-error | AsyncStorage throws, isAnyDeviceAvailable = false | selectedRecordingDevice = "phone" (fallback) | error | Log error; fall through to connection-aware default | unchanged |
hydrate.storage-read-error-glasses-connected | AsyncStorage throws, isAnyDeviceAvailable = true | selectedRecordingDevice = "glasses" (fallback) | error | Log error; fall through to connection-aware default | unchanged |
Pseudocode
hydrateRecordingDevicePreference():
storedDevice = await AsyncStorage.getItem(STORAGE_KEY)
if isRecordingDevice(storedDevice):
isConnected = isGlassesDeviceAvailable()
// If stored preference is a glasses mode but glasses are not connected,
// override to phone for this session. Do NOT persist the override —
// the user's stored preference stays intact so the next launch with
// glasses connected will restore the saved glasses mode.
if (storedDevice === "glasses" || storedDevice === "glasses-audio") && !isConnected:
setSelectedRecordingDeviceValue("phone")
return
setSelectedRecordingDeviceValue(storedDevice)
return
// No stored preference — fall through to connection-aware default
fallbackDevice = getDefaultRecordingDevice() // already calls isAnyDeviceAvailable()
if selectedRecordingDevice !== fallbackDevice:
selectedRecordingDevice = fallbackDevice
emit()
Key design decision: the override is in-memory only — AsyncStorage is NOT updated when we override to phone. This preserves the user's explicit glasses preference for the next launch when glasses may be connected.
Logs
| Source | Location |
|---|---|
| App (JS) | Frontend logger — recording-device flow |
New log steps to add in hydrateRecordingDevicePreference:
| step | when |
|---|---|
recording_device_glasses_not_connected_override | Glasses preference stored but glasses not available; override to phone. Log { storedDevice, override: "phone" } |
Deployment
- Mechanism:
local only - Deploy command:
- Notes: JS-only change; no native rebuild required.
ONCE YOU GET APPROVAL FROM THE DEVELOPER, DELETE THIS LINE AND UPDATE THE STAGE GATE TRACKER
Handoff to Related Plan Reconciliation
After all stages are approved, apply .agent/skills/reconcile-plans/SKILL.md to propagate contract updates across linked plans.