Skip to content

Improve Mentra Live pairing UX (OS-1615)#3224

Open
nic-olo wants to merge 35 commits into
devfrom
nicolo/os-1615-improve-mentra-live-pairing-ux
Open

Improve Mentra Live pairing UX (OS-1615)#3224
nic-olo wants to merge 35 commits into
devfrom
nicolo/os-1615-improve-mentra-live-pairing-ux

Conversation

@nic-olo

@nic-olo nic-olo commented Jun 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Add Mentra Live pairing flow: prep guide, pairing-mode scan filter, auto-connect, and required media wipe on ownership transfer.
  • Extend Bluetooth SDK (iOS/Android) with pairing_info, wipe_media_result, and pairing transfer commands.
  • Add wipe_media handler on asg_client to erase gallery during ownership transfer.

Test plan

  • Flash BES firmware from companion PR and pair a factory-reset Mentra Live (auto pairing mode on boot).
  • Hold power+camera 10s on bonded glasses; confirm LED/voice and discoverability in scan.
  • Verify auto-connect on scan and 15s timeout + Try Again when no unit found.
  • Transfer ownership: confirm wipe prompt, successful wipe + finalize, and decline aborts pairing.
  • Confirm bonded phone reconnect still works via connectDefault() without scan list.

Made with Cursor

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying mentra-store-dev with  Cloudflare Pages  Cloudflare Pages

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

View logs

Comment thread mobile/modules/bluetooth-sdk/ios/Source/sgcs/MentraLive.swift Outdated
Comment thread mobile/src/app/pairing/loading.tsx
Comment thread mobile/src/app/pairing/scan.tsx
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying dev-augmentos-console with  Cloudflare Pages  Cloudflare Pages

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

View logs

nic-olo and others added 4 commits June 22, 2026 16:37
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>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying prod-augmentos-account with  Cloudflare Pages  Cloudflare Pages

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

View logs

nic-olo and others added 4 commits June 22, 2026 16:38
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>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying mentra-live-ota-site with  Cloudflare Pages  Cloudflare Pages

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

View logs

nic-olo and others added 14 commits June 22, 2026 16:39
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>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

bugbot run

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread mobile/src/app/pairing/loading.tsx
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

bugbot run

Comment thread mobile/src/app/pairing/loading.tsx
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

bugbot run

Comment thread mobile/src/app/pairing/loading.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Fix All in Cursor

❌ 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()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

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>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(() => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

bugbot run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant