Skip to content

fix(mobile): fire completion notifications for cloud task turns#2405

Merged
Gilbert09 merged 2 commits into
mainfrom
posthog-code/fix-mobile-cloud-turn-complete-notifications
May 28, 2026
Merged

fix(mobile): fire completion notifications for cloud task turns#2405
Gilbert09 merged 2 commits into
mainfrom
posthog-code/fix-mobile-cloud-turn-complete-notifications

Conversation

@Gilbert09
Copy link
Copy Markdown
Member

Problem

Mobile cloud tasks never fired completion notifications for normal turn
endings. The user would send a prompt, background the app, and the agent
would finish without the device ringing — because mobile only had two
notification paths and both missed the common case:

  1. The live logs path fired a notification only on
    _posthog/awaiting_user_input (agent blocking on the user). Plain
    _posthog/turn_complete (agent done, run still alive) was a no-op
    beyond clearing isPromptPending.
  2. The terminal-status path fired when the run went
    completed/failed/cancelled. But the final turn_complete log
    entry arrives via the SSE stream before the status update, and the
    log batch already clears awaitingPing — so the status branch's
    shouldPing = preState.awaitingPing check always read false by the
    time it ran. Effectively dead for normal completions.

Same shape of gap as desktop PR #2344 ("fire completion notifications
for cloud tasks"), where the cloud branch of
updatePromptStateFromEvents was clearing isPromptPending on
turn_complete but never calling notifyPromptComplete.

Changes

  • analyzeEntries now surfaces a distinct hasTurnCompleted flag for
    _posthog/turn_complete / _posthog/task_complete, in addition to
    the existing hasTurnEnd (which covers all four turn-end methods
    including awaiting_user_input and error).
  • The live logs branch of _handleCloudUpdate now fires the
    turn_complete local notification (plus the existing sound + haptic
    side effects, gated by pingsEnabled) when:
    • the batch isn't a historical snapshot, AND
    • the user was awaiting a ping (this-device-initiated action), AND
    • hasTurnCompleted is true, AND
    • hasAwaitingUserInput is false (which has its own dedicated banner).
  • Snapshots and gap-fill replay paths stay unaffected — those are
    historical entries and would re-notify for turns that completed in
    past app runs.
  • The dedup window in maybePresentLocalNotification (30s per task) and
    the focusedTaskId suppression continue to apply, so the new path
    doesn't double-fire when status terminal arrives shortly after
    turn_complete.

No desktop code changes; only the React Native app.

Test plan

  • pnpm --filter @posthog/mobile test — 25 files / 105 tests pass.
  • pnpm exec biome ci apps/mobile/src/features/tasks/stores/taskSessionStore.ts — clean.
  • npx tsc --noEmit in apps/mobile — no new errors introduced by this change (the pre-existing test-file errors are untouched).
  • Manual: send a prompt to a cloud task on a TestFlight build, background the app, confirm a " finished" banner fires when the agent completes its turn.
  • Manual: leave the task screen focused while the agent finishes — no notification (focused-task suppression).
  • Manual: receive an awaiting_user_input turn end — still get the "needs your input" banner, not the generic "finished" one.

Mobile previously fired a completion notification only when the entire
cloud run went terminal (status `completed`/`failed`/`cancelled`). For a
typical cloud run that's a long-lived sandbox processing successive
prompts, the canonical "your prompt is done" signal is the per-turn
`_posthog/turn_complete` event, not the run-level status — so users
sending a prompt and backgrounding the app got no banner when the agent
finished.

Worse, the existing terminal-status path was effectively dead for normal
completions: the final `turn_complete` log entry arrived before the
status update and cleared `awaitingPing`, so the status branch's
`shouldPing` check always read false by the time it ran.

Extend `analyzeEntries` to surface a distinct `hasTurnCompleted` flag for
`_posthog/turn_complete` / `_posthog/task_complete`, and fire the
`turn_complete` local notification (plus sound + haptic) from the live
`logs` branch when the user was awaiting a ping and the batch isn't also
an `awaiting_user_input` (which keeps its own dedicated banner). Mirrors
desktop PR #2344 — same gap, same shape of fix.

Generated-By: PostHog Code
Task-Id: 4d2246b1-ddde-4ded-bb3c-a25109305477
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Comments Outside Diff (1)

  1. apps/mobile/src/features/tasks/stores/taskSessionStore.ts, line 942-973 (link)

    P2 _posthog/error still silently drops the failure notification

    A turn that ends with _posthog/error sets hasTurnEnd = true (clearing awaitingPing in state) but hasTurnCompleted = false, so the logs branch fires no ping. The status block then reads preState?.awaitingPing — which is already false because the error log was processed first — so it also stays silent. The updated comment on line 912 ("The terminal-status block below only fires when the whole run goes terminal without first emitting a turn_complete") implies the status block handles failures, but for an error log that precedes failed status the status block is effectively unreachable. A user whose task errors out therefore receives no banner. This race condition predates this PR and affects the same SSE-ordering assumption the PR fixes for turn_complete; adding _posthog/error to the hasTurnCompleted check (and using the task_failed notification kind for that case) would close the gap while the overall structure is being touched.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/mobile/src/features/tasks/stores/taskSessionStore.ts
    Line: 942-973
    
    Comment:
    **`_posthog/error` still silently drops the failure notification**
    
    A turn that ends with `_posthog/error` sets `hasTurnEnd = true` (clearing `awaitingPing` in state) but `hasTurnCompleted = false`, so the logs branch fires no ping. The status block then reads `preState?.awaitingPing` — which is already `false` because the error log was processed first — so it also stays silent. The updated comment on line 912 ("The terminal-status block below only fires when the whole run goes terminal without **first** emitting a turn_complete") implies the status block handles failures, but for an error log that precedes `failed` status the status block is effectively unreachable. A user whose task errors out therefore receives no banner. This race condition predates this PR and affects the same SSE-ordering assumption the PR fixes for `turn_complete`; adding `_posthog/error` to the `hasTurnCompleted` check (and using the `task_failed` notification kind for that case) would close the gap while the overall structure is being touched.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/mobile/src/features/tasks/stores/taskSessionStore.ts:942-973
**`_posthog/error` still silently drops the failure notification**

A turn that ends with `_posthog/error` sets `hasTurnEnd = true` (clearing `awaitingPing` in state) but `hasTurnCompleted = false`, so the logs branch fires no ping. The status block then reads `preState?.awaitingPing` — which is already `false` because the error log was processed first — so it also stays silent. The updated comment on line 912 ("The terminal-status block below only fires when the whole run goes terminal without **first** emitting a turn_complete") implies the status block handles failures, but for an error log that precedes `failed` status the status block is effectively unreachable. A user whose task errors out therefore receives no banner. This race condition predates this PR and affects the same SSE-ordering assumption the PR fixes for `turn_complete`; adding `_posthog/error` to the `hasTurnCompleted` check (and using the `task_failed` notification kind for that case) would close the gap while the overall structure is being touched.

Reviews (1): Last reviewed commit: "fix(mobile): fire completion notificatio..." | Re-trigger Greptile

Closes the companion gap to the previous commit: a turn ending with
`_posthog/error` hit the same SSE-ordering race that hid `turn_complete`
notifications — the error log cleared `awaitingPing` before the
terminal-status block could read it, so users whose task errored mid-run
got no banner.

Adds a `hasTurnFailed` flag for `_posthog/error` alongside the existing
`hasTurnCompleted`, and routes it to the `task_failed` notification
kind. Mutually exclusive with the other two so a batch carrying both
turn_complete and error (shouldn't happen but be defensive) prefers the
positive completion notification.

Spotted by Greptile review on the PR.

Generated-By: PostHog Code
Task-Id: 4d2246b1-ddde-4ded-bb3c-a25109305477
@Gilbert09 Gilbert09 added the Stamphog This will request an autostamp by stamphog on small changes label May 28, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Targeted mobile fix extending an existing notification pattern to cover cloud task turn completions. All notification kinds are already typed and handled, the priority guards prevent double-firing, and no API contracts or data models are touched.

@Gilbert09 Gilbert09 merged commit d3ead8f into main May 28, 2026
20 checks passed
@Gilbert09 Gilbert09 deleted the posthog-code/fix-mobile-cloud-turn-complete-notifications branch May 28, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant