Skip to content

fix: pr-bug-scan validated finding from #4518#4736

Open
buf0-bot[bot] wants to merge 1 commit into
mainfrom
bot/pr-bug-scan-4518-1780718015
Open

fix: pr-bug-scan validated finding from #4518#4736
buf0-bot[bot] wants to merge 1 commit into
mainfrom
bot/pr-bug-scan-4518-1780718015

Conversation

@buf0-bot
Copy link
Copy Markdown
Contributor

@buf0-bot buf0-bot Bot commented Jun 6, 2026

Automated fix-PR from pr-bug-scan for parent #4518.

Fixer status: FIXED_WITH_CODE_PROOF
Summary: Serialize show/dismiss per (hostId, notificationId) so a dismiss arriving while schedule is awaiting cannot no-op against an empty map.
Blocked proof link: mobile/src/notifications/mobile-notifications.ts:114-117
Typecheck: skipped

Proof (from validator)

  • C1 — proof_type: code_analysis
    • trigger: Two desktop events arrive on the same RPC stream for the same notificationId in rapid succession: a 'notification' event (re-fire/replace) followed by a 'dismiss' event, while the first 'notification' event for that id is also still in-flight.
    • observable: Local push notification persists on the device even though the desktop already acknowledged/dismissed the corresponding agent state; tapping it routes to a stale worktree.
    • path:
      • mobile/src/notifications/mobile-notifications.ts:159 — subscribe callback dispatches both events without awaiting: void showLocalNotification(...) and void dismissLocalNotification(...)
      • mobile/src/notifications/mobile-notifications.ts:88-91 — second showLocalNotification deletes the existing identifier from scheduledNotificationIdsByHostAndNotificationId BEFORE awaiting scheduleNotificationAsync
      • mobile/src/notifications/mobile-notifications.ts:93 — control yields at await Notifications.scheduleNotificationAsync(...); map has no entry for storedKey
      • mobile/src/notifications/mobile-notifications.ts:114-117 — dismissLocalNotification runs in this gap, scheduledNotificationIdsByHostAndNotificationId.get(storedKey) is undefined, returns early without calling Notifications.dismissNotificationAsync
      • mobile/src/notifications/mobile-notifications.ts:102-104 — showLocalNotification resumes and writes the new scheduledIdentifier into the map, leaving a live OS notification with no pending dismiss

Generated by pr-bug-scan (proof-machine architecture). Human approval required before merge.

Serialize show/dismiss per (hostId, notificationId) so a dismiss arriving while schedule is awaiting cannot no-op against an empty map.
Copy link
Copy Markdown
Contributor

@pullfrog pullfrog Bot left a comment

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — serializes show/dismiss operations per (hostId, notificationId) key so a dismiss event arriving while a show is in-flight cannot no-op against an empty tracking map, closing the race condition from #4518.

  • Add runSerializedByStoredKey — chains per-key operations via .then() on a Map<string, Promise<void>>; cleans up entries when the chain settles with no newer operation pending.
  • Wrap showLocalNotification schedule logic — runs through the serializer when storedKey exists, preserving the existing fast path for notifications without ids.
  • Wrap dismissLocalNotification — routes the identifier lookup, map delete, and platform dismiss through the serializer.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

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.

0 participants