Improve Mentra Live pairing UX (OS-1615)#3224
Conversation
Deploying mentra-store-dev with
|
| Latest commit: |
628fb03
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f193e28b.augmentos-appstore-2.pages.dev |
| Branch Preview URL: | https://nicolo-os-1615-improve-mentr.augmentos-appstore-2.pages.dev |
Deploying dev-augmentos-console with
|
| Latest commit: |
628fb03
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://263f100c.dev-augmentos-console.pages.dev |
| Branch Preview URL: | https://nicolo-os-1615-improve-mentr.dev-augmentos-console.pages.dev |
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Deploying prod-augmentos-account with
|
| Latest commit: |
628fb03
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://2a9c50ca.augmentos-e84.pages.dev |
| Branch Preview URL: | https://nicolo-os-1615-improve-mentr.augmentos-e84.pages.dev |
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Deploying mentra-live-ota-site with
|
| Latest commit: |
628fb03
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a622ce47.mentra-live-ota-site.pages.dev |
| Branch Preview URL: | https://nicolo-os-1615-improve-mentr.mentra-live-ota-site.pages.dev |
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…roid Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
bugbot run |
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="mobile/src/app/pairing/scan.tsx">
<violation number="1" location="mobile/src/app/pairing/scan.tsx:169">
P2: Pressing 'Try Again' on iOS navigates two screens back. goBack() in handleTryAgain triggers the beforeRemove listener registered by focusEffectPreventBack(..., iosDontPreventBack=true), which also calls goBack(). The listener should handle cleanup (disconnect/forget) without an additional back navigation, or handleTryAgain should restart the scan in place instead of navigating.</violation>
</file>
<file name="mobile/modules/bluetooth-sdk/ios/Source/DeviceManager.swift">
<violation number="1" location="mobile/modules/bluetooth-sdk/ios/Source/DeviceManager.swift:1210">
P1: Pairing finalize/abort and wipe-media commands silently fail when the connected glasses aren't Mentra Live, because these methods use `(sgc as? MentraLive)?.sendXxx()` — an optional cast that silently discards the command when the cast fails. This is inconsistent with other MentraLive-only commands (`sendGalleryMode`, `sendCameraFovSetting`, etc.) that use `liveSgc()` which throws `BluetoothSdkError(unsupported_device)`. The risk is highest for `sendPairingFinalize` and `sendPairingAbort` (fire-and-forget with no pending-response timeout — the caller gets zero feedback), and for `sendWipeMediaForPairing` the failure only surfaces after a timeout. If the app is coordinating a pairing flow across steps, the silent failure means the JS layer thinks the command was sent while the glasses never received it.
Consider using the `liveSgc()` pattern like the rest of the file, or at minimum adding a `guard` with a Bridge.log warning.</violation>
</file>
<file name="mobile/modules/bluetooth-sdk/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt">
<violation number="1" location="mobile/modules/bluetooth-sdk/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt:473">
P2: Android `wipeMediaForPairing` uses blocking `CountDownLatch.await()` inside `PendingResponse`, which holds the Expo AsyncFunction worker thread until timeout if the glasses never respond (e.g., disconnect mid-wipe). The iOS side uses cancellable Swift structured concurrency. Consider wrapping the call in a coroutine with a cooperative cancellation scope so the thread can be released promptly on disconnect or abort.</violation>
</file>
<file name="agents/OS-1615-mentra-live-pairing-ux-plan.md">
<violation number="1" location="agents/OS-1615-mentra-live-pairing-ux-plan.md:16">
P1: According to linked Linear issue OS-1615, BT classic radio lifecycle hardening is a required part of this work, but this plan’s stated end state and implementation steps omit it. If implementation follows this doc as written, the always-on classic discoverable/connectable exposure called out in the issue can remain unaddressed. Consider adding explicit BT classic lifecycle tasks and acceptance criteria to this plan.</violation>
<violation number="2" location="agents/OS-1615-mentra-live-pairing-ux-plan.md:185">
P2: The pairing gesture is defined as a 10-second power+camera hold, but later sections still instruct a 5-tap camera flow. That inconsistency can lead to mismatched firmware/app behavior and incorrect user guidance during pairing. Align all references to one gesture path (or explicitly document both if intentional) so scan timeout copy, prep instructions, and implementation order all match.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
bugbot run |
Co-authored-by: Cursor <cursoragent@cursor.com>
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 7 total unresolved issues (including 6 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5066707. Configure here.
| if (!connectingRef.current) { | ||
| setScanTimedOut(true) | ||
| void BluetoothSdk.stopScan() | ||
| } |
There was a problem hiding this comment.
Timeout hides device list
Medium Severity
The 15s Mentra Live scan timer sets scanTimedOut whenever connectingRef is false, without checking whether devices were already discovered. The UI then shows “No glasses found” and hides the multi-device picker.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 5066707. Configure here.
- scan.tsx: reset connectingRef on permission denial so 15s timeout still fires - loading.tsx: dedicated effect catches late pairing_info and cancels pending navigation - loading.tsx: in-flight guard on checkGalleryAndHandleOwnershipTransfer - loading.tsx: back/cancel aborts pairing transfer when ownership transfer is in progress - en.ts: align noGlassesFoundHint copy with 5x click gesture - WipeMediaCommandHandler: children==null returns false (wipe_media_result.success=false) - MentraLive.swift: forget() clears PREFS_DEVICE_NAME to prevent pairing filter bypass on rescan Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="mobile/src/app/pairing/loading.tsx">
<violation number="1" location="mobile/src/app/pairing/loading.tsx:268">
P2: If the user exits this screen during the 1-second delay before success navigation (for example, by tapping the header back button or swiping back on iOS), the pending `setTimeout` is never cleared on unmount. When it eventually fires, `replace('/pairing/success')` runs from a stale context and can unexpectedly jump the user to the success screen. Consider adding a cleanup effect that clears `navigationTimerRef.current` when the component unmounts.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
|
||
| hasNavigatedRef.current = true | ||
| setTimeout(() => { | ||
| navigationTimerRef.current = setTimeout(() => { |
There was a problem hiding this comment.
P2: If the user exits this screen during the 1-second delay before success navigation (for example, by tapping the header back button or swiping back on iOS), the pending setTimeout is never cleared on unmount. When it eventually fires, replace('/pairing/success') runs from a stale context and can unexpectedly jump the user to the success screen. Consider adding a cleanup effect that clears navigationTimerRef.current when the component unmounts.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mobile/src/app/pairing/loading.tsx, line 268:
<comment>If the user exits this screen during the 1-second delay before success navigation (for example, by tapping the header back button or swiping back on iOS), the pending `setTimeout` is never cleared on unmount. When it eventually fires, `replace('/pairing/success')` runs from a stale context and can unexpectedly jump the user to the success screen. Consider adding a cleanup effect that clears `navigationTimerRef.current` when the component unmounts.</comment>
<file context>
@@ -234,14 +258,14 @@ export default function GlassesPairingLoadingScreen() {
hasNavigatedRef.current = true
- setTimeout(() => {
+ navigationTimerRef.current = setTimeout(() => {
replace("/pairing/success", {deviceModel: deviceModel})
}, 1000)
</file context>
|
bugbot run |


Summary
pairing_info,wipe_media_result, and pairing transfer commands.wipe_mediahandler on asg_client to erase gallery during ownership transfer.Test plan
connectDefault()without scan list.Made with Cursor