Skip to content

fix(sidebar): an agent's row stays while it runs, whatever its tab title - #23948

Merged
brennanb2025 merged 11 commits into
mainfrom
brennanb2025/codex-agent-row-identity
Sep 30, 2026
Merged

brennanb2025 merged 11 commits into
mainfrom
brennanb2025/codex-agent-row-identity

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​488 $\color{#cf222e}{\Huge{\mathbf{−}}}$​15 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​473
Prod 13 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​269 $\color{#cf222e}{\Huge{\mathbf{−}}}$​67 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​202

ELI5

The sidebar shows a row for each agent running in a workspace, such as "Codex · Idle". Orca decided whether that row exists by reading the terminal tab's title. Codex renames its tab to the project name, and that title has no "codex" in it, so the row disappeared while Codex was still running. Now the row's agent comes from Orca's check of which program is running in the terminal, and the title only decides whether it shows as working or idle.

What Changed

The problem users see (#23767, part a): launch Codex in an Orca terminal. The sidebar briefly shows "Codex · Idle", then the entry disappears once Codex retitles the tab to the project name, even though Codex is still running and accepts input. Older reports of the same kind: #14464, #10258, #8711. The workaround was codex -c 'tui.terminal_title=["app-name","spinner","project"]'.

Scope: sidebar agent rows and dashboard counts on desktop, for every agent, not only Codex. The Git Bash title rule below also reaches the agent icons in the tab search palette, which share that helper. Not changed: mobile, which builds its own status on the host and still loses a Codex titled with the project name, and the worktree status dot.

How the sidebar decides each agent row, in order (the whole picture after this PR):

  1. Hook status first. Every pane that has an entry in Orca's agent status store (sent by the agent's status hook) gets its row from that entry: the agent, and its working, blocked, waiting or done state. This is the most accurate source and nothing below overrides it. If a hook's last update is more than 30 minutes old, a stale "working", "blocked" or "waiting" is shown as "unverifiable" while the terminal is still alive, or "idle" once it isn't. It is never handed to the title. Subagent rows hang off their parent's hook row. The pane is then marked as covered.
  2. Finished orchestration workers' parent panes are marked as covered too, so they don't get a second row.
  3. Title-based rows, only for panes with no hook status at all (agent status hooks turned off, or an agent Orca has no hook for). This is the only step this PR changes:
    • Which agent: the pane's live process check first (what Orca sees running in the terminal), then a title that names the agent, then the agent Orca launched in that tab (only while the title shows activity).
    • Working or idle: from the title, because it's the only activity signal these panes have.
    • When the row exists: a plain title such as the project name keeps a row only while a real process check shows the agent. So a retitled Codex keeps its row, and an exited one doesn't leave a ghost.
    • When the row goes away: the pane's process monitor sees the agent exit (checked every 0.75 s while the tab is visible, every 3 s in the background), the title becomes a shell or default title (Git Bash's MINGW64:/…-style prompt titles now count), or the terminal exits. For a hidden (parked) tab, an end-of-command marker triggers one process check before the row is cleared.
  4. Orchestration workers attributed to this worktree by the host, whose tab isn't in this window yet, still get their hook row (same rules as step 1).
  5. Recently finished agents ("done" rows kept for a while) are shown only for panes not already covered above.

Before: a row existed only if the title showed both activity and an agent name (worktree-title-derived-agent-rows.ts). The fallback to the agent Orca launched ran only after that check, and the rows didn't use Orca's existing check of which program is running in the pane (the tab icon already did).

After:

  • Hook status always comes first. For any pane that has hook status, the row's agent is the agent that sent the hook, and its working/needs-input/done state comes from the hook. The rules below apply only to panes with no hook status (agent status hooks off, or an agent without a hook). There, the row's agent comes from the pane's live process check first, then an agent-naming title, then the agent Orca launched (only while the title shows activity). This hook-less order is sidebar-only, not the shared resolver: it differs from the tab icon in that a shell title overrides the process check, and it ranks the title above the launch record. Temporary: STA-8819 moves agent identity into the one agent status store, so the sidebar and tab icon read one answer.
  • For those hook-less panes, the title is the backup activity signal (working, needs input or idle), because they have nothing better. It never overrides a hook.
  • A plain title keeps a row only while a real process check shows the agent. So a retitled Codex keeps its row, and a pane whose agent has exited doesn't leave a ghost row. The stored process entry now records where its agent came from (agentEvidence: a process check, or the launch record Orca restores when it reattaches a terminal after a restart). Rows trust only a process check, so a background tab whose Codex exited before a restart doesn't come back as "Codex · Idle".
  • The row goes away when the agent exits. Most Orca terminals don't send end-of-command markers, so the saved process check can't rely on them to be refreshed. Each visible or background tab already runs a process monitor for agent-finished notifications, which re-checks every 0.75 s while visible and every 3 s while in the background. It already recognises an exit (no agent and no child processes for 1.5 s, so a nested shell inside Codex doesn't count). That confirmed exit now also clears the saved check, and a Codex started again in the same terminal is recognised afresh. The row also goes away at once when the terminal exits, or when the title becomes a shell name, the tab's default title, or blank (Codex clears its title when it quits). Git Bash's MINGW64:/… prompt titles count as shell titles.
  • Side effect: a background tab running a hook-less agent is now watched by that monitor too, so when that agent exits you can get the same agent-finished notification a visible tab already gave. It fires once per exit.
  • For a hidden (parked) tab, Orca now checks the process once before clearing an agent at an end-of-command marker. Full-screen agents run nested shells that emit those markers too, and they would otherwise make a still-running background Codex lose its row.

Why

  • This is an interim fix. The longer-term design (STA-8819, which replaced fix(terminal): preserve agent identity when process evidence is unverifiable #23786) moves agent presence into the one agent status store, using hook identity and a check on the agent's own process id. Until then, this uses evidence Orca already has, without a new data path.
  • Why not keep the title as identity: Codex's title format is user-configurable, and it names the project, not the agent.
  • How this differs from a from-scratch design, and why: the shell-title override, the title ranking above the launch record, and the Git Bash title rule are intended for this interim fix, because Orca's process checks are periodic and settle over 1.5 s, so an exit title is the fastest signal. Retiring the saved check from the existing notification monitor, rather than merging the two per-terminal process observers, is also temporary. All of these go away with STA-8819, which moves presence into the agent status store with a check on the agent's own process id.
  • Why not take identity from the launch record alone: nothing retires it on WSL, SSH or a launch that failed, so it would leave ghost rows. An earlier revision did this, and review caught it.

Known limits:

  • WSL and tmux panes have no process reads, so the original bug remains there.
  • A pane Orca has not shown since it started (a Codex started from the orca CLI in a workspace you haven't opened, or background tabs after a restart) has no process check yet, so it behaves as before this PR until the tab is shown.
  • On SSH and other remote hosts, a hidden pane still clears at an end-of-command marker without a check, and the process monitor doesn't poll remote panes, so a remote agent that exits without a marker keeps its row until the tab is shown or the host reports the terminal exited. On a local hidden pane, one check that gets no answer clears the row until the tab is shown; the visible-pane path retries first.
  • If Codex exits while its tab has been hidden long enough for Orca to stop rendering it, and that terminal has no end-of-command markers, the row stays until you open or close that tab.
  • /quit clears the row at once (measured 0.1 s). Two cases wait for the process monitor to confirm the exit instead:
    • A shell that sets its own prompt title (oh-my-zsh, Ubuntu's bash): for about 5 s after /quit the row stays, labelled with the prompt title.
    • A killed or crashed Codex: the row stays about 4 s in a visible tab and about 11 s in a background tab.
  • After an exit, the tab icon can keep the Codex icon for up to about 15 s after the row is gone, as on main.
  • A hand-typed agent can briefly lose its row while a command starts.
  • In a terminal Orca launched an agent into but that sends no end-of-command markers (for example Git Bash or cmd on Windows, or a shell framework that replaces Orca's markers), pressing Enter at an idle prompt more than a few seconds after the agent exits can stop Orca from recognising the next agent typed there, so that run loses its row on retitle, as on main.
  • For a hand-typed Codex with no hooks, the row can say "Running" while the worktree status dot doesn't spin: the dot still needs the title or launch record to name the agent.

Linked Issue

Refs #23767 (part a; part b was the shared-server bug, fixed by #23900)

Related: #14464, #10258, #8711

Visual Proof

Live QA on an M4Air Mac with real Codex 0.158, agent status hooks off (the hook-less path this PR changes), in an isolated test build of Orca.

Before (main): Codex is still running in tab "rowqa", but the rowqa card has no agent row.
main: Codex running in tab "rowqa", no agent row

After (this PR):
Codex retitled the tab to "rowqa"; the card keeps the Codex row "rowqa · Idle"
Back on rowqa after another workspace was active; the row is still "rowqa · Idle 2m"
While Codex works, the row reads "Running"
After /quit of an Orca-launched Codex, the tab is back at a shell prompt and the card has no agent row
Codex typed by hand in a plain terminal without command markers keeps its row "rowqa · Idle"
After /quit there, then Enter and pwd, the card has no agent row
A shell that sets its own prompt title: after /quit, no agent row
Codex quit and typed again in the same plain terminal: the row "rowqa · Idle" is back

Testing

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

  • At the final head, 328 related test files (3,098 tests: terminal pane connection, foreground tracker, agent-completion monitor, parked panes, sidebar, dashboard, tab agent, and the foreground-confirmation unit tests under tests/e2e) pass. The full suite runs in CI.

  • New tests:

    • a row survives a retitle to the project name;
    • process identity for Codex, Claude, Gemini, OpenCode and Grok;
    • the title only drives activity;
    • removal on exit, a shell or default title, and terminal exit;
    • a stale launch record with a plain title leaves no row;
    • a parked end-of-command marker with the agent still running keeps the row, and a real exit clears it;
    • a reused pane gets the right agent;
    • Git Bash prompt titles count as shell titles;
    • a reattach after a restart restores the launch record without making a row, and a hidden pane that is shown again keeps its own earlier process check;
    • the per-worktree lookup returns only that worktree's panes;
    • after Codex exits, the row goes away with a blank title, a prompt title such as user@host: ~/repo, or an unchanged title after a kill; a nested shell inside Codex keeps the row; a Codex typed again in the same terminal gets its row back.
  • Several fail against the unfixed code.

  • Typecheck clean (web); oxlint, React Doctor and the changed-code quality gate clean.

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

  • Performance: the per-worktree row caches now include the pane's foreground-process data, so they refresh when it changes. The per-worktree lookup groups entries by tab once per update, so its cost doesn't grow with worktrees × panes.
  • use-tab-agent.ts has its own copy of the shell-title check and wasn't changed.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…r its title

Codex retitles its pane to the project name, so the sidebar's title-derived
row (which required the title to name an agent) vanished while Codex kept
running (#23767). Rows now take identity from the canonical pane resolver
over the pane's foreground-process read and launch record, then the title;
the title only decides idle/working/needs-input. The row still goes away when
the PTY exits, the process tracker proves the shell is back, or the title is a
shell or default title.
Review of the previous commit found ghost rows: the tab launch record is a
latch nothing clears on WSL, after an SSH exit, or for a launch that never
started, and a parked pane's process read went stale because only the
mounted tracker re-derives it.

- The launch record returns to main's role: a fallback only for titles that
  show activity, ranked below a title naming another agent (pane reuse),
  matching the tab icon's order.
- A parked pane's command boundary retires its unconfirmable process read,
  like the mounted ladder's unavailable path; reveal re-reads it.
…it Bash prompt titles as the shell

- A parked pane's end-of-command marker can be a nested shell's leak under a
  still-running full-screen agent, so confirm the foreground first (as the
  mounted ladder does) and retire the process read only on a shell or no
  answer. SSH/remote parked panes hold no incarnation to fence a host read
  with, so they still retire.
- Git Bash emits no command marks; its `$MSYSTEM:$PWD` prompt title
  (MINGW64:/c/repo) is now shell evidence, so a stale Codex read there no
  longer keeps a ghost row after Codex exits.
…r-worktree foreground selector groups by tab

A daemon reattach seeds the pane's foreground entry with its launch agent, which
can outlive the process while Orca is closed. The entry now records where its
agent came from (agentEvidence), and the sidebar/dashboard title-derived rows
only keep a plain-title row on an actual process read. Routing and the tab icon
are unchanged.

selectPaneForegroundAgentsForWorktree grouped every pane key per worktree; it now
groups by tab once per map identity and skips worktrees with no tabs.
…the same agent

The reattach seed marked a returning parked Codex pane as launch-record
evidence, over the process read this session already took, so its row
blinked out on reveal and stayed hidden if the user left the tab before
the visible read landed. Keep the read when it names the same agent; the
seed still drops byte-routing trust.
Codex clears its title when it exits, and the tab then shows its default
title. A pane without shell command marks never re-reads its foreground
process, so the retained read kept a "Codex · Idle" row after /quit
(permanently for a hand-typed Codex; about 15 s while the marked-pane
confirm ladder ran). Treat a blank title like the default title it shows.
…ocess read

A hook-less pane keeps its sidebar row from the tracker's foreground-process
read, but nothing re-derived that read in a pane without OSC 133 command
marks. After Codex exited there, a "Codex · Idle" row stayed: permanently
when the shell titles its prompt, or when a killed Codex leaves its last
title.

The pane's agent-completion process monitor already confirms an agent's
exit (no agent and no child processes, held past its settle window). It now
reports that exit to the tracker, which retires its own process read and
runs the confirmed-shell path the visible-pty read uses. A tracker read that
names an agent seeds the monitor, so hidden panes and panes the monitor had
not polled yet are watched too. A command read in flight still decides the
pane, and launch records or other agents' reads are left alone.
…marked pane identifiable

The process-exit retire published shellForeground:true and left the one-shot
visible sample settled; a pane without command marks has no command start to
lift either, so a Codex typed again after quitting was never read and lost its
row on retitle. Publish shellForeground:false and reopen the sample.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status (head b7e9362)

The problem: launch Codex in an Orca terminal without agent status hooks. The sidebar shows "Codex · Idle", then the row disappears once Codex renames its tab to the project name, even though Codex is still running (#23767, part a). The row existed only while the tab title named the agent.

What changes for users: the row now stays while Codex runs and shows Working or Idle from the title. It goes away when Codex exits. This was confirmed live on an M4Air with real Codex 0.158: main loses the row at the retitle; this branch keeps it through tab switches, background workspaces and a restart. /quit clears the row in about 0.1 s, and a Codex started again in the same terminal gets its row back.

Fixed during review:

  • Stale row after a restart: a background tab whose Codex had already exited could come back as "Codex · Idle", because the launch record Orca restores on reattach was treated as a live process check. The stored entry now records whether its agent came from a real process check.
  • Stale row after exit: in terminals without end-of-command markers (most plain terminals), nothing re-checked the stored process after Codex quit, so "Codex · Idle" stayed. It lasted about 15 s, or forever with a shell that titles its prompt or a killed Codex. The existing per-terminal process monitor, which already powers agent-finished notifications, now clears the stored check when it confirms the agent exited. A blank title (Codex's own exit signal) clears it at once.
  • Relaunch: after that exit, a Codex typed again in the same plain terminal wasn't recognised. Fixed.
  • Reopening a hidden tab: it could make the row blink out, or stay hidden if you left the tab quickly. Fixed.
  • Performance: the per-workspace lookup scanned every pane for every workspace: 36 ms per update at 300 workspaces. It now groups entries by tab once per update.
  • CI: an old unit test's exact-shape assertion, and a new test cast without the required comment.

Deferred (known limits, listed in the description):

  • P2: a shell that sets its own prompt title shows the row, labelled with that title, for about 5 s after /quit.
  • P2: a killed Codex keeps its row about 4 s in a visible tab and about 11 s in a background tab.
  • P2: in a terminal Orca launched an agent into that has no end-of-command markers (for example Git Bash or cmd), an idle Enter a few seconds after the agent exits can stop the next agent typed there from being recognised, as on main.
  • P2: terminals Orca hasn't shown since it started (a Codex started from the orca CLI in an unopened workspace) behave as before this PR until shown.
  • P2: for a hand-typed hook-less Codex, the row can say Running while the worktree status dot doesn't spin.
  • P3:
    • A hidden local tab gives up after one process check that gets no answer.
    • Hidden SSH or remote tabs clear at an end-of-command marker without a check.
    • Dismissing a live hook-backed row swaps in an Idle row that can't be dismissed, as main already does for Claude.
    • A background tab running a hook-less agent can now give the same agent-finished notification a visible tab already gives.
    • The tab icon keeps the Codex icon for up to about 15 s after exit.
  • Unchanged: WSL, tmux and mobile.

Verified:

  • Live QA with real Codex on the M4Air, with hooks off: the retitle, Working/Idle, hidden and background tabs, restart, /quit in marked and unmarked terminals, a prompt-titling shell, kill -9 in visible and hidden tabs, relaunch in the same terminal, and main showing the bug. Screenshots are in the description.
  • At the final code head: 328 related test files (3,098 tests) pass, the web typecheck is clean, the changed-code quality gate reports 0 findings, and CI is green.
  • New tests fail with each fix removed.

Not verified:

  • Windows, Git Bash, WSL and SSH were not run live.
  • A restart that reattaches to a still-running Codex: the test app's terminal daemon didn't survive its own restart.
  • Whether the "macOS is blocking Orca notifications" toast shown about 4 s after Codex starts is new with this PR or already on main.

@brennanb2025
brennanb2025 marked this pull request as ready for review September 30, 2026 02:37
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Sidebar agent rows now track foreground process reads.

The PR should not merge until parked-pane handling preserves a running agent’s identity when foreground inspection cannot give an answer.

Findings

  1. P1 Unconfirmed Exit Hides Agent ▶

Summary

The PR makes desktop sidebar and dashboard agent rows use pane process evidence when a tab title no longer names its agent. It also tracks evidence provenance, retires reads through the completion monitor, and updates parked-pane handling and worktree caches.

  • Parked-pane confirmation can discard a running agent’s identity when inspection is unavailable or skipped.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  P[Pane foreground read] --> E[Foreground entry with evidence]
  E --> R[Sidebar and dashboard row]
  T[Tab title] --> R
  M[Completion monitor] -->|confirmed local exit| E
  D[Parked command-finished marker] --> C[One foreground confirmation]
  C -->|shell or null| E
Loading

Reviews (1) · Last reviewed commit: "test(terminal): justify the pane binding..."

Comment on lines +176 to +177
if (processName === null || isShellProcess(processName)) {
state.setPaneForegroundAgent(paneKey, { agent: null, shellForeground: false })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unconfirmed Exit Hides Agent When a parked pane receives a command-finished marker from a nested shell, a foreground check can return null because inspection is unavailable even while the agent is running. This branch treats null as an exit and clears the agent’s process identity, so its sidebar row can disappear until the pane is shown again. A parked remote pane also reaches this branch with null because its check is skipped. Please keep a missing answer distinct from a confirmed exit.

Knowledge Base Used: Terminal runtime and interaction

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: fcb24cf2-e123-4f82-b324-771a3f79542e

📥 Commits

Reviewing files that changed from the base of the PR and between a57863b and b7e9362.

📒 Files selected for processing (33)
  • src/renderer/src/components/dashboard/build-dashboard-snapshot.rows-cache.test.ts
  • src/renderer/src/components/dashboard/useAgentBucketCounts.gate.test.ts
  • src/renderer/src/components/dashboard/useAgentBucketCounts.ts
  • src/renderer/src/components/dashboard/worktree-agent-rows-cache.ts
  • src/renderer/src/components/sidebar/title-derived-pane-agent-identity.ts
  • src/renderer/src/components/sidebar/useWorktreeAgentRows.ts
  • src/renderer/src/components/sidebar/worktree-agent-rows.ts
  • src/renderer/src/components/sidebar/worktree-card-status-inputs.test.ts
  • src/renderer/src/components/sidebar/worktree-card-status-inputs.ts
  • src/renderer/src/components/sidebar/worktree-title-derived-agent-rows.test.ts
  • src/renderer/src/components/sidebar/worktree-title-derived-agent-rows.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-inspection-result.ts
  • src/renderer/src/components/terminal-pane/agent-completion-process-monitor.ts
  • src/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.test.ts
  • src/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.ts
  • src/renderer/src/components/terminal-pane/pane-foreground-process-exit-retire.test.ts
  • src/renderer/src/components/terminal-pane/parked-terminal-command-status.test.ts
  • src/renderer/src/components/terminal-pane/parked-terminal-command-status.ts
  • src/renderer/src/components/terminal-pane/pty-connection-command-finished-cleanup.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection-foreground-agent-routing.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection-foreground-agent-sampling.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection-foreground-process-exit.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection-foreground-routing-confirmation.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.ts
  • src/renderer/src/components/terminal-pane/pty-connection/sleeping-record-access.ts
  • src/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.ts
  • src/renderer/src/store/slices/pane-foreground-agent.test.ts
  • src/renderer/src/store/slices/pane-foreground-agent.ts
  • src/shared/shell-process-detection.test.ts
  • src/shared/shell-process-detection.ts
  • tests/e2e/terminal-foreground-confirmation.unit.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Foreground-agent entries now distinguish process-read evidence from launch-record evidence. Confirmed process exits and parked command boundaries can retire foreground-agent state. Sidebar and dashboard row builders now consume pane foreground-agent data, and dashboard cache reuse accounts for changes to that data. Tests cover evidence lifecycle, row resolution, shell-title detection, and cache updates.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to b7e93

The change keeps a sidebar row while an agent runs and removes it once the agent exits. The supplied tests cover exit, restart, split-pane, and cache-invalidation cases. The remaining risk is the untested Windows, Git Bash, WSL, and SSH paths, which the author already listed, and I found no concrete defect there.

Security Architecture Review

Security architecture risk: 🔵 Low · up to b7e93

The inspected changes preserve separation between display labels and input authority, and add safeguards against stale session identity. No new security issue was established, but incomplete coverage limits confidence in the broader change.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected effects are pane-scoped identity, display, and input-mode recovery. Sleeping-record cleanup can additionally remove matching provider-session aliases within the same worktree. This bounds the inspected state impact but does not establish complete cross-service or environment coverage.

Trust Boundaries and Controls

  • observed — Tracker results are rejected after disposal, generation changes, PTY rebinding, or remote-incarnation changes. Remote exit handling requires admitted host evidence with PTY, incarnation, authority-generation, and observation-order checks; compatibility names alone do not establish remote exit.

Resilience and Maintainability Implications

  • observed — Confirmed-exit recovery clears stale identity hints and resets terminal input modes through a writer that updates the keyboard-mode mirror and retains replay suppression. This preserves the control preventing terminal-generated replay replies from reaching the surviving shell.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 31 functions across 33 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary sidebar behavior change: an agent row remains visible while the agent runs, regardless of the terminal tab title.
Description check ✅ Passed The description is detailed and aligned with the template. It includes the user impact, implementation scope, rationale, linked issue, visual proof, testing, known limitations, and checklist status. E…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

One gap in the new process-exit retirement path can leave the ghost row this PR sets out to remove: onProcessExitConfirmed defers to an in-flight command read that can terminate without clearing the stored read (inline). A minor regex miss for Git for Windows SDK titles is inline too.

Reviewed changes

  • New sidebar pane-identity resolver — resolveTitleDerivedPaneAgent ranks a live process read above an agent-naming title, which ranks above the launch record (title showing activity only); resolveTitleDerivedAgentType moved alongside it.
  • Store evidence tag — PaneForegroundAgentEntry gains agentEvidence: 'process-read' | 'launch-record', included in the slice's equality check, so only a live read can back a plain-title row.
  • Exit retirement — the pane's completion monitor now reports a confirmed exit back to the tracker (onForegroundAgentExited → onProcessExitConfirmed), which clears a stored process read; a reattach keeps this session's read of the same agent.
  • Parked panes — retireForegroundAgentUnlessConfirmed makes one confirm read at a parked command boundary before retiring.
  • Git Bash titles — titleShowsNoAgent treats MINGW64:/…-style prompt titles as shell titles.
  • Cache/selector plumbing — per-worktree foreground selectors and dashboard/sidebar rows caches now key on paneForegroundAgentByPaneKey.

ℹ️ Nitpicks

  • src/renderer/src/lib/use-tab-agent.ts:26 keeps its own titleShowsNoAgent without the new MSYS branch, while tab-agent-from-signals.ts consumes the shared one. After this PR the tab-bar icon and the tab-search occupant can disagree on a Git Bash prompt title. The PR body notes this copy is unchanged; folding it onto the shared helper would remove the drift.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

const published = deps.getPublishedEntry?.()
if (
disposed ||
hasCommandReadInFlight() ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This deferral hands the pane to the in-flight command-finished read, but that read has a terminal branch which clears nothing: when it ends unverifiable (remoteEvidenceVerdict !== null && !== 'live', lines 248–251) it calls onCommandFinishedUnavailable() and returns without clearAgentEvidence() or a publish. By then the completion monitor has already set lastForegroundAgent = null and cleared hasAgentRunEvidence, so it can never re-confirm, and the stored process-read survives. On a stale agent-naming title — exactly the killed-Codex case this PR targets — the sidebar row lingers.

Technical details
# Confirmed exit can be swallowed by an in-flight command read

## Affected sites
- `pane-foreground-agent-tracker.ts:362-372` — `onProcessExitConfirmed` returns on `hasCommandReadInFlight()` without recording a pending exit.
- `pane-foreground-agent-tracker.ts:247-258` — the `command-finished` unverifiable branch returns without clearing evidence.
- `agent-completion-inspection-result.ts:171-194` — the monitor sets `lastForegroundAgent = null` and `clearAgentRunEvidence()` before firing `onForegroundAgentExited`, so it cannot re-confirm.
- `pane-foreground-process-reader.ts:44-47,74-78` — a local read that throws or returns a client-only unverifiable result maps to `remoteEvidenceVerdict = 'unverifiable'` with `processName = null`.

## Sequence
1. `process-read` entry published; tracker read armed the monitor (`lastForegroundAgent` set, `hasAgentRunEvidence` true).
2. Agent exits; shell emits `133;D`; `onCommandFinished` schedules a `command-finished` read → `hasCommandReadInFlight()` true.
3. Monitor cadence misses the agent twice over `LOCAL_PROCESS_EXIT_SETTLE_MS` and confirms exit; fires `onForegroundAgentExited` → `onProcessExitConfirmed`, which returns at line 367.
4. The command-finished read ends unverifiable (throw / client-only local result) → lines 248–251 return without clearing; tracker flags stay set on the entry.
5. Monitor state is now inert (`lastForegroundAgent` null, no run evidence); nothing re-derives the read until the tab is shown or the next command starts.

## Required outcome
- A monitor-confirmed exit must retire the stored process read even if a command read is in flight and later ends unverifiable.

## Suggested approach (optional)
- Record a pending confirmed exit on the tracker when the guard fires, and have the `command-finished` unverifiable terminal branch (and `dispose`) honor it by clearing/publishing; or move the `hasCommandReadInFlight()` check so an unverifiable completion falls through to `clearAgentEvidence()` when a monitor exit was reported.

## Open questions for the human (optional)
- Is a local confirm read rejecting realistic enough in practice, or is the client-only-unverifiable case the only one worth covering?

/** A shell name or the tab's neutral default title; blank titles are no evidence. */
// Why: Git for Windows' prompt titles the pane `$MSYSTEM:$PWD` (`MINGW64:/c/repo`), and Git Bash
// has no command marks, so that repaint is the only sign an agent there handed back to the shell.
const MSYS_PROMPT_TITLE_RE = /^(?:MINGW(?:32|64)|MSYS|UCRT64|CLANG(?:32|64|ARM64)):\//

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This matches stock MSYSTEM titles, but Git for Windows SDK installs set TITLEPREFIX=SDK-${MSYSTEM#MINGW} (so the title is SDK-MINGW64:/c/repo), and a user ~/.config/git/git-prompt.sh can override TITLEPREFIX entirely. Those panes are not read as shells, so a stalled process read isn't retired at the prompt — the case this rule exists to close. An optional (?:SDK-)? prefix (and a test row) would cover the SDK builds.

@brennanb2025
brennanb2025 merged commit fceca5c into main Sep 30, 2026
67 checks passed
machamy added a commit to machamy/orca that referenced this pull request Oct 4, 2026
Catches up stablyai/orca to e2c5414. Notable for this fork: Codex runs
without its shared background server in Orca terminals, terminal opens no
longer strip hooks from the real ~/.codex, and worktree creation stops
rescanning large repositories.

Upstream's foreground-process agent identity (stablyai#23948) replaces the fork's
title-based fallback, so worktree-title-agent-identity.ts is gone; the fork
keeps its regression cases against the new logic. The statusLine badge wrapper
now sits inside upstream's version-aware install plan and unwraps before a
retire. The duplicate agentSubagentsEqual module and the removed agent-trust
IPC registration are dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant