Skip to content

Improve tab status reliability#392

Merged
danshapiro merged 22 commits into
mainfrom
fix/tab-status-reliability
Jun 4, 2026
Merged

Improve tab status reliability#392
danshapiro merged 22 commits into
mainfrom
fix/tab-status-reliability

Conversation

@danshapiro
Copy link
Copy Markdown
Owner

Summary

  • Adds provider-backed blue status for Codex pending/busy, Opencode busy, Claude busy, and Fresh Agent/agent-chat runtime states.
  • Moves terminal turn-complete signaling to server-authoritative durable completionSeq messages, including replay-safe client persistence and activity-list bootstrap completions.
  • Adds green attention clearing through pane focus, tab visit, and real terminal input, plus Fresh Agent busy-to-idle and permission-wait completion events.
  • Documents the agent status indicator contract and updates the docs mock.

Verification

  • npm run lint passed with 0 errors; existing warnings remain.
  • timeout 1200s env FRESHELL_TEST_SUMMARY="tab-status-reliability full verify final" npm test passed.
  • timeout 1400s env FRESHELL_TEST_SUMMARY="tab-status-reliability check" npm run check passed.
  • timeout 600s npm run build passed.
  • Focused server tests passed: 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.
  • Targeted Playwright production milestone passed against isolated port 3955: Codex blue pending/idle, Codex replay suppression, green click clear, Fresh Agent completion and permission-wait sound/green, and Gemini/Kimi inertness.

Notes

  • The isolated production server used for the browser milestone was stopped after verification.

Codex and others added 22 commits June 4, 2026 15:04
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>
@danshapiro danshapiro merged commit 6e81e50 into main Jun 4, 2026
1 check passed
@danshapiro danshapiro deleted the fix/tab-status-reliability branch June 4, 2026 22:32
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +1713 to +1716
this.emit('codex.turn.completed', {
terminalId: record.terminalId,
at: Date.now(),
} satisfies CodexTurnCompletedEvent)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

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.

2 participants