From 1003fff7ca0dab1f7ae26029cf396b9e0d9778ca Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 14 Aug 2026 14:36:47 -0700 Subject: [PATCH] Prevent cross-coordinator title replays --- .../agent-completion-coordinator.test.ts | 62 +++++++++++++++++++ .../agent-completion-coordinator.ts | 6 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts b/src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts index b699f0ace5e..b893af70f68 100644 --- a/src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts +++ b/src/renderer/src/components/terminal-pane/agent-completion-coordinator.test.ts @@ -1469,6 +1469,68 @@ describe('agent completion coordinator', () => { expect(dispatchCompletion).toHaveBeenCalledTimes(1) }) + it('does not let a pane title duplicate another coordinator stamped completion', () => { + const dispatchCompletion = vi.fn() + const hookCoordinator = createAgentCompletionCoordinator({ + paneKey: 'tab-1:leaf-1', + getPtyId: () => 'pty-1', + getSettings: () => null, + inspectProcess: vi.fn(), + dispatchCompletion, + isLive: () => true + }) + const paneCoordinator = createAgentCompletionCoordinator({ + paneKey: 'tab-1:leaf-1', + getPtyId: () => 'pty-1', + getSettings: () => null, + inspectProcess: vi.fn(), + dispatchCompletion, + isLive: () => true + }) + + hookCoordinator.observeHookStatus({ + state: 'working', + prompt: 'review the PR', + agentType: 'claude', + stateStartedAt: 1_700_000_000_000 + }) + hookCoordinator.observeHookStatus({ + state: 'working', + prompt: 'review the PR', + agentType: 'claude', + stateStartedAt: 1_700_000_000_000, + turnCompletedAt: 1_700_000_005_000 + }) + expect(dispatchCompletion).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(2_000) + paneCoordinator.observeTitleWorking() + paneCoordinator.observeClassifiedTitleCompletion('Claude done') + + expect(dispatchCompletion).toHaveBeenCalledTimes(1) + + hookCoordinator.observeHookStatus({ + state: 'working', + prompt: 'continue the review', + agentType: 'claude', + stateStartedAt: 1_700_000_010_000 + }) + hookCoordinator.observeHookStatus({ + state: 'done', + prompt: 'continue the review', + agentType: 'claude', + stateStartedAt: 1_700_000_020_000 + }) + vi.advanceTimersByTime(HOOK_DONE_QUIET_MS) + + expect(dispatchCompletion).toHaveBeenCalledTimes(2) + + paneCoordinator.observeTitleWorking() + paneCoordinator.observeClassifiedTitleCompletion('Claude done') + + expect(dispatchCompletion).toHaveBeenCalledTimes(3) + }) + it('does not let a vetoed gated Stop swallow the later all-clear', () => { const dispatchCompletion = vi.fn() const coordinator = createAgentCompletionCoordinator({ diff --git a/src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts b/src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts index 73395fdca73..02b89352103 100644 --- a/src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts +++ b/src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts @@ -328,6 +328,8 @@ export function createAgentCompletionCoordinator( options.paneKey, optionsOverride.completionIdentity.lastTurnCompletedAtNotified ) + } else { + handledTurnCompletedAtByPaneKey.delete(options.paneKey) } } if ( @@ -741,7 +743,9 @@ export function createAgentCompletionCoordinator( clearPendingCodexAttention() workingStatusObserved = true requiresFreshWorking = false - lastCompletionIdentityByPaneKey.delete(options.paneKey) + if (!handledTurnCompletedAtByPaneKey.has(options.paneKey)) { + lastCompletionIdentityByPaneKey.delete(options.paneKey) + } currentTurn += 1 dropPendingTitle() return true