Skip to content

bugfix: G2 calendar events upload empty - #3666

Open
aheschl1 wants to merge 5 commits into
Mentra-Community:devfrom
aheschl1:andrew/settings-issue
Open

bugfix: G2 calendar events upload empty#3666
aheschl1 wants to merge 5 commits into
Mentra-Community:devfrom
aheschl1:andrew/settings-issue

Conversation

@aheschl1

@aheschl1 aheschl1 commented Aug 2, 2026

Copy link
Copy Markdown

my first PR! working on a lifecycle fix regarding pushing of calendar events to the ER G2

Fix: G2 dashboard calendar is empty after a cold start

Symptom

The Schedule widget on the G2 dashboard stayed empty after launching the app. Disconnecting and reconnecting
the glasses made the events appear.

Root cause

calendar_events is a BLUETOOTH_SETTING_KEY, but MantleManager.sendCalendarEvents() wrote the events straight to the native DeviceStore via BluetoothSdk.setCalendarEvents(). Settings copy remained empty.

Pushing the full settings sent an empty list.

Fix

MantleManager now writes via engine.settings.set(SETTINGS.calendar_events.key, shapedEvents).

Also redacts calendar_events in the settings log (stores/settings.ts). It printed event titles and street addresses.

Log evidence

Cold boot, glasses reachable.

Before - three [] pushes land on top of the 3 real events, then the replay clears:

MANTLE: CAL_DIAG: query calendars=12 events=5 window=2026-08-02T03:08:51Z..2026-08-05T05:08:51Z
MANTLE: CAL_DIAG: pushing 3 shaped event(s): [ ...redacted... ]
MANTLE: CAL_DIAG: push to native store resolved
GlassesSettingsSync: CAL_DIAG: pushAllBluetoothSettings pushing calendar_events count=0
GlassesSettingsSync: CAL_DIAG: pushAllBluetoothSettings pushing calendar_events count=0
GlassesSettingsSync: CAL_DIAG: pushDeviceSettingsOnConnect pushing calendar_events count=0
CORE: MAN: handleDeviceReady() Even Realities G2
CORE: G2: setDashboardMenu -- sending 7 items
CORE: G2: sendCalendarEvents -- 0 events

After - the replay carries the events:

CORE: MAN: handleDeviceReady() Even Realities G2
CORE: G2: setDashboardMenu -- sending 7 items
CORE: G2: sendCalendarEvents -- 3 events

Note

Medium Risk
Touches the glasses dashboard data path and Bluetooth settings sync on connect, but the change aligns calendar with other bluetooth settings and is covered by a focused integration test.

Overview
Fixes empty G2 Schedule widget after cold start by routing calendar sync through the JS settings store instead of a native-only write.

MantleManager.sendCalendarEvents now calls engine.settings.set(SETTINGS.calendar_events.key, …) rather than BluetoothSdk.setCalendarEvents(). Because calendar_events is a BLUETOOTH_SETTING_KEY, connect-time and boot full Bluetooth setting pushes take their value from the store; writing only to native DeviceStore left the store empty and later pushes overwrote real events with [].

Privacy: calendar_events is redacted in settings debug logs (printableSettingValue) and in bug-report diagnostic context (event count only, no titles/locations).

Tests: MantleManager.test.ts asserts store + getBluetoothSettings() content, debounced updateBluetoothSettings, and replay on glasses connect; expo-calendar mock defaults to denied permission unless a test opts in.

Reviewed by Cursor Bugbot for commit 4db8f3c. Bugbot is set up for automated code reviews on this repo. Configure here.

@aheschl1 aheschl1 changed the title bluetooth fix G2 calendar setting fix Aug 2, 2026
@aheschl1 aheschl1 changed the title G2 calendar setting fix bug: G2 calendar events upload empty Aug 3, 2026
@aheschl1
aheschl1 force-pushed the andrew/settings-issue branch from 8ee6f46 to 70fc29f Compare August 3, 2026 02:02
@aheschl1
aheschl1 marked this pull request as ready for review August 3, 2026 02:02
@aheschl1
aheschl1 requested a review from aisraelov as a code owner August 3, 2026 02:02
@aheschl1
aheschl1 force-pushed the andrew/settings-issue branch from 4696cc0 to 2111dac Compare August 3, 2026 02:04
@aheschl1
aheschl1 force-pushed the andrew/settings-issue branch from 7f88d2e to 5be0b4c Compare August 3, 2026 02:06

@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 and verified against the latest diff

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/services/MantleManager.ts">

<violation number="1" location="mobile/src/services/MantleManager.ts:1034">
P2: This fix routes calendar events through the in-memory settings store, which solves the original store-vs-native divergence. But it also changes persistence/ordering behavior worth confirming: `calendar_events` is declared `persist: false` and `saveOnServer: false`, and `sendCalendarEvents()` is fire-and-forget (`setupPeriodicTasks()` calls `this.sendCalendarEvents()` without awaiting it). On a cold start the store therefore holds the default `[]` until the async permission check + calendar fetch complete and write the store. Any full push that runs before that write completes — the pre-connect seed (`pushAllBluetoothSettings`) or the on-connect replay (`pushDeviceSettingsOnConnect`) — still sends an empty `calendar_events` to the glasses and clears the Schedule widget, which is the same symptom this PR is fixing (just transient). Previously the events were written directly to the persisted native DeviceStore, so they survived that window. If the on-connect replay can fire before the calendar fetch resolves, consider awaiting the seed/connect push on the calendar write, or gating full pushes on an 'events loaded' flag, so the empty-default window can't clobber the widget. The PR notes events still don't land consistently, so this trailing-empty window is worth ruling out.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// (pushAllBluetoothSettings before connect, pushDeviceSettingsOnConnect on
// the connected transition) overwrites the native DeviceStore copy with
// whatever the store holds.
const res = await engine.settings.set<CalendarEvent[]>(SETTINGS.calendar_events.key, shapedEvents)

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: This fix routes calendar events through the in-memory settings store, which solves the original store-vs-native divergence. But it also changes persistence/ordering behavior worth confirming: calendar_events is declared persist: false and saveOnServer: false, and sendCalendarEvents() is fire-and-forget (setupPeriodicTasks() calls this.sendCalendarEvents() without awaiting it). On a cold start the store therefore holds the default [] until the async permission check + calendar fetch complete and write the store. Any full push that runs before that write completes — the pre-connect seed (pushAllBluetoothSettings) or the on-connect replay (pushDeviceSettingsOnConnect) — still sends an empty calendar_events to the glasses and clears the Schedule widget, which is the same symptom this PR is fixing (just transient). Previously the events were written directly to the persisted native DeviceStore, so they survived that window. If the on-connect replay can fire before the calendar fetch resolves, consider awaiting the seed/connect push on the calendar write, or gating full pushes on an 'events loaded' flag, so the empty-default window can't clobber the widget. The PR notes events still don't land consistently, so this trailing-empty window is worth ruling out.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mobile/src/services/MantleManager.ts, line 1036:

<comment>This fix routes calendar events through the in-memory settings store, which solves the original store-vs-native divergence. But it also changes persistence/ordering behavior worth confirming: `calendar_events` is declared `persist: false` and `saveOnServer: false`, and `sendCalendarEvents()` is fire-and-forget (`setupPeriodicTasks()` calls `this.sendCalendarEvents()` without awaiting it). On a cold start the store therefore holds the default `[]` until the async permission check + calendar fetch complete and write the store. Any full push that runs before that write completes — the pre-connect seed (`pushAllBluetoothSettings`) or the on-connect replay (`pushDeviceSettingsOnConnect`) — still sends an empty `calendar_events` to the glasses and clears the Schedule widget, which is the same symptom this PR is fixing (just transient). Previously the events were written directly to the persisted native DeviceStore, so they survived that window. If the on-connect replay can fire before the calendar fetch resolves, consider awaiting the seed/connect push on the calendar write, or gating full pushes on an 'events loaded' flag, so the empty-default window can't clobber the widget. The PR notes events still don't land consistently, so this trailing-empty window is worth ruling out.</comment>

<file context>
@@ -1025,10 +1025,17 @@ class MantleManager {
+      // whatever the store holds. Writing straight to native left the store at
+      // its default `[]`, so those pushes wiped the events before the glasses
+      // were ready and G2's connect replay sent a calendar-clear instead.
+      const res = await engine.settings.set<CalendarEvent[]>(SETTINGS.calendar_events.key, shapedEvents)
+      if (res.is_error()) {
+        console.warn("MANTLE: Failed to sync calendar events to glasses", res.error)
</file context>

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

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5be0b4c. Configure here.

Comment thread mobile/src/services/MantleManager.ts
@aheschl1 aheschl1 changed the title bug: G2 calendar events upload empty bugfix: G2 calendar events upload empty Aug 3, 2026
@aisraelov

Copy link
Copy Markdown
Member

hey, was this tested on real hardware?

@aheschl1

aheschl1 commented Aug 4, 2026

Copy link
Copy Markdown
Author

hey, was this tested on real hardware?

Hey @aisraelov , yeah it was

@aisraelov
aisraelov enabled auto-merge August 8, 2026 02:39
@aisraelov

Copy link
Copy Markdown
Member

Hey, looks good. Merging if CI passes.

@aheschl1

aheschl1 commented Aug 8, 2026

Copy link
Copy Markdown
Author

Hey, looks good. Merging if CI passes.

@aisraelov Looks like workflows need to be triggered still

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.

2 participants