Improve tab status reliability#392
Conversation
Single-PR plan to fix BLUE/GREEN/SOUND/CLEAR across all agent surfaces. Incorporates product decisions 1-5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold in 9 code-grounded corrections: fresh-agent key namespaces, codex exactly-once emit + 3-source dedupe, durable-completion transport (piggyback+ack, seq idempotency), SDK streamingActive clear, opencode deadman placement, reject terminal-scoped emit, gemini/kimi normalizer, sendInput/term.onData clear split, codex onTurnStarted tap point. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Durable seq+persist (no double-notify), agent-chat clear-on-engage, pending-permission engage-clear, opencode reject no-emit, codex onTurnStarted via registry event, drop claude resume seeding, update broken codex client test, fix Task 3 red-test action shapes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
emit-on-idle back-to-back test, seq deferred to Task 4, applyServerCompletion as pane-resolving thunk, removePermission action name, strip-escapes before clear predicate, preserve activityRemove on candidate-idle, clear all panes of tab on focus, permission-response clears attention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sed) Tab-navigation clears all panes; rename server seq to completionSeq (distinct from pendingEvents[].seq); fix stale claude resume-seeding file-map line. 3 plan reviews run; 20 defects fixed pre-implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…elector Task 1: recordTurnComplete now ignores older-or-equal at (replay-safe); add selectPaneBySessionKey + export the 4 pane-activity helpers for the SDK turn-complete bridge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 2: CodexActivityTracker emits 'turn.complete' once per real turn-end (emit-on-idle, per-turn dedupe across BEL/JSONL/onTurnCompleted); wire it to broadcastTerminalTurnComplete (provider codex); add 'codex' to the WS provider enum; drop codex from the client BEL gate so replayed scrollback can no longer re-green/re-chime. Updated the codex client test + added a codex replay test + a server integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…waiting-for-approval) Task 3: useAgentSessionTurnCompletion watches SDK pane busy/pending edges and dispatches recordTurnComplete on busy->idle (turn complete) and 0->>=1 pending permission/question (waiting-for-approval), keyed by the provider:sessionId session key. Never fires on first observation (no spurious green on restore/hydration). Mounted in App alongside useTurnCompletionNotifications. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Signal consistency) Task 9: export normalizeTurnCompleteSignalMode and drop the gemini/kimi pass-through so the client stops implying a turn-complete capability the shared supportsTurnSignal gate rejects. No functional change (count was already 0); status-inert is now explicit + tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…odex-activity Task 12: add resetOpencodeActivityOverlay() to the bootstrap auth-failure handler so all three activity overlays clear symmetrically; delete the unused src/lib/tab-codex-activity.ts duplicate busy resolver. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 10: green dismisses on a pane click OR a real keystroke in BOTH attentionDismiss modes, and clears the tab + ALL its panes (not just the active one). - dismissTabGreen thunk: single source of truth (tab + all panes). - PaneContainer.handleFocus, useTurnCompletionNotifications switch-clear, TabBar onClick all route through it. - TerminalView: clear moved out of shared sendInput into term.onData via isEngagementInput (strips ANSI escapes so arrows don't clear; printable/ Enter/paste do; synthetic scroll/DECRQM never clear). - FreshAgentView + AgentChatView: clear on message submit AND on permission/question response (covers keyboard approval bypassing focus). - TabItem single dot: drop the redundant '&& tab.status===running' (busy is already authoritative) at both sites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 8: terminal.session.bound fires before terminal.created in production, so onBound's bindSession was a no-op and dropped the sessionId. Lazily trackTerminal (idempotent) in onBound so the record + sessionId survive regardless of event order. (Resume-busy seeding was cut earlier — no unresolved-turn source, would risk false-blue.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 6: sdk-bridge broadcasts sdk.status:idle in the shared stream-end finally branch (covers natural end AND error, since wasAborted is false for both). Client setSessionStatus now clears streamingActive on idle/ exited, and sessionError clears streamingActive + resets running->idle for non-RESTORE codes -- because busy = streamingActive || running, an idle status alone left the pane stuck blue. Both agent-chat and fresh-agent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 744f2e748b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| this.emit('codex.turn.completed', { | ||
| terminalId: record.terminalId, | ||
| at: Date.now(), | ||
| } satisfies CodexTurnCompletedEvent) |
There was a problem hiding this comment.
Preserve Codex completions before durable binding
For a fresh Codex pane that has not yet been bound to a durable session, this new codex.turn.completed event is emitted before handleCodexTurnCompleted() runs the proof path that eventually calls bindSession() (see the following call on line 1717 and the bind in runCodexDurabilityProof). CodexActivityTracker.onTurnCompleted() ignores events when no tracker state exists yet, and this commit also disables the client-side Codex BEL fallback, so a first/short Codex turn can finish without ever producing terminal.turn.complete; the tab then gets no green attention or sound until a later turn after binding.
Useful? React with 👍 / 👎.
Summary
completionSeqmessages, including replay-safe client persistence and activity-list bootstrap completions.Verification
npm run lintpassed with 0 errors; existing warnings remain.timeout 1200s env FRESHELL_TEST_SUMMARY="tab-status-reliability full verify final" npm testpassed.timeout 1400s env FRESHELL_TEST_SUMMARY="tab-status-reliability check" npm run checkpassed.timeout 600s npm run buildpassed.timeout 120s npm run test:vitest -- run --config vitest.server.config.ts test/unit/server/coding-cli/codex-activity-wiring.test.ts test/server/ws-terminal-create-reuse-running-codex.test.ts.Notes