Stop duplicate agent-task-complete notifications from background plugin hooks#6595
Stop duplicate agent-task-complete notifications from background plugin hooks#6595nwparker wants to merge 3 commits into
Conversation
…in hooks Claude-Mem and similar Claude Code plugins run background hooks (PreToolUse/PostToolUse/Stop) on the same pane AFTER the user's turn has already finished. The renderer's agent-completion coordinator treated ANY 'working' frame as the start of a new turn, so each background memory write produced a fresh working->done cycle that re-armed and re-fired a completion notification the user had already seen. Plumb the existing main-process turn-boundary signals (hookEventName and hasExplicitPrompt, already on the AgentHookServer listener payload) through the agentStatus:set IPC to the coordinator. A 'working' frame now only starts a new turn when it carries an explicit user prompt or a non-tool-progress hook event; a tool-progress frame (PreToolUse/PostToolUse) that arrives after we already notified completion for the current turn is treated as background churn and suppressed, along with the matching background 'done' and the title-spinner flicker Claude renders during the memory write. Agents that emit no hook event name (Codex, title-only sources) and genuine new UserPromptSubmit turns keep re-arming exactly as before. Fixes #4630
|
Warning Review limit reached
Next review available in: 22 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
EvidenceTest commands + outputCoordinator regression tests (new End-to-end notification path + main hook server, all green: Lint + typecheck (scoped to changed files / their projects): Before/after proof (the tests catch the regression)I temporarily disabled the suppression guard (forced the new Restoring the fix makes all four pass. This is the literal #4630 symptom: a second
Electron / CDPLaunched the dev build for this worktree over CDP and confirmed it compiles and runs cleanly with the change: attached instance identity |
…595-zero-tradeoff # Conflicts: # src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts
|
Takeover update for PR #6595:
|
|
CI follow-up: GitHub PR Checks Only annotation: a React hook dependency warning in |
Description
Fixes #4630 by preventing background Claude-Mem/plugin hooks after a completed turn from starting a second completion-notification cycle without a fresh user prompt.
Evidence of fix
Before: a normal Claude
UserPromptSubmit → Stopdispatched one completion, then a Claude-Mem-shapedPostToolUse → Stopbackground cycle on the same pane dispatched a second notification (2 actual vs 1 expected).After: the regression loop keeps the first completion and ignores the post-completion background cycle until a new prompt begins. Focused tests, lint/typechecks, and GitHub
verifypass.ELI5
After Claude said “done,” a memory plugin kept tidying up in the background. Orca mistook that cleanup for a brand-new task finishing and rang the bell again. The fix requires a new user request before another completion bell.
User-facing trade-offs
Summary
Fixes #4630.
Claude-Mem and similar Claude Code plugins can run background tool hooks on the same pane after the user-initiated turn has already completed. The completion coordinator previously treated every later
working -> donehook cycle as a fresh turn, so a post-turn memory write could re-arm and fire anotheragent-task-completenotification.This PR keeps the bug fix and now applies cleanly on
origin/main:hookEventName,hasExplicitPrompt) through liveagentStatus:setIPC and snapshot IPC.workingframes that arrive with no explicit prompt after Orca already notified completion for that same turn, plus the matching backgrounddoneand title-spinner flicker.Tradeoffs / regressions
No known tradeoff remains.
The suppression is intentionally narrow: it requires a known tool-progress hook event (
PreToolUse,PostToolUse, orPostToolUseFailure), no explicit prompt, and an already-notified hook completion for the current turn. A laterUserPromptSubmitclears the suppression and re-arms completion normally. SSH/relay hook events preserve the metadata; paths that do not carry hook identity simply do not engage the suppression.The previous snapshot-symmetry caveat is gone:
agentStatus:getSnapshotnow forwardshookEventName/hasExplicitPrompttoo, andserver.test.tscovers that contract.Testing
./node_modules/.bin/vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts src/renderer/src/hooks/agent-hook-completion-notifications.test.ts src/renderer/src/hooks/useIpcEvents.test.ts src/main/agent-hooks/server.test.ts— 4 files, 371 tests passed./node_modules/.bin/oxlint src/main/agent-hooks/server.ts src/main/agent-hooks/server.test.ts src/main/index.ts src/renderer/src/components/terminal-pane/agent-completion-coordinator-types.ts src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts src/renderer/src/hooks/agent-hook-completion-notifications.test.ts src/renderer/src/hooks/useIpcEvents.ts src/renderer/src/hooks/useIpcEvents.test.ts src/shared/agent-status-types.ts src/shared/agent-tool-progress-hooks.ts./node_modules/.bin/tsgo --noEmit -p config/tsconfig.node.json./node_modules/.bin/tsgo --noEmit -p config/tsconfig.tc.web.jsonorigin/main(8c49426224) and resolved the only conflict inagent-completion-coordinator.test.tsReviewer notes
Added/updated focused coverage:
agent-completion-coordinator.test.ts: user turn notifies once; background Claude-Mem-style tool churn does not re-notify; a genuine second prompt still notifies; title-spinner flicker during background churn stays suppressed.agent-hook-completion-notifications.test.ts: end-to-end renderer notification observer path does not dispatch the duplicate background completion.useIpcEvents.test.ts:hookEventName,hasExplicitPrompt, andstateStartedAtare forwarded to the completion observer.server.test.ts: status snapshots preserve hook turn-boundary metadata.