bugfix: G2 calendar events upload empty - #3666
Conversation
8ee6f46 to
70fc29f
Compare
4696cc0 to
2111dac
Compare
7f88d2e to
5be0b4c
Compare
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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.
|
hey, was this tested on real hardware? |
Hey @aisraelov , yeah it was |
|
Hey, looks good. Merging if CI passes. |
@aisraelov Looks like workflows need to be triggered still |

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_eventsis aBLUETOOTH_SETTING_KEY, butMantleManager.sendCalendarEvents()wrote the events straight to the native DeviceStore viaBluetoothSdk.setCalendarEvents(). Settings copy remained empty.Pushing the full settings sent an empty list.
Fix
MantleManagernow writes viaengine.settings.set(SETTINGS.calendar_events.key, shapedEvents).Also redacts
calendar_eventsin 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:After - the replay carries the 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.sendCalendarEventsnow callsengine.settings.set(SETTINGS.calendar_events.key, …)rather thanBluetoothSdk.setCalendarEvents(). Becausecalendar_eventsis aBLUETOOTH_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_eventsis redacted in settings debug logs (printableSettingValue) and in bug-report diagnostic context (event count only, no titles/locations).Tests:
MantleManager.test.tsasserts store +getBluetoothSettings()content, debouncedupdateBluetoothSettings, and replay on glasses connect;expo-calendarmock 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.