Repository navigation
fix(agent-status): keep an opencode run's Done when its command end retires the pane - #24252
brennanb2025 wants to merge 3 commits into
Conversation
…etires the pane A pane that holds launch authority (including the restored receipt any terminal list / worktree ps / mobile refresh mints) retires it at the 133;D that ends `opencode run`. Retirement closed the pane for status and the host's own process Done for that run was then suppressed, so the row went Working -> removed with no Done. The status store now accepts a process-lifetime Done on a retired pane when the run started before the retirement, without lifting the fence: late hook and OSC posts stay suppressed, and a closed tab still wins.
…not by the clock The previous commit admitted a process-lifetime Done on a retired pane when the run started before the retirement, comparing two wall-clock reads. A clock stepped backward during the run lost the Done again, and because retirement deletes the row the hook-yield rule reads, a process Done for a command a hook owned (OpenCode 1 `run` with its in-process plugin) was written where a live pane would yield. Retirement now records on its fence which run the pane showed (agent, state, who reported it, when), carried over when a later retirement of the same command end finds the rows already gone. A process Done on the retired pane is accepted once, only when that run was still live and no hook reported during it (the same rule a live pane applies). The fence is not lifted, a new run invalidates the record, and a closed tab still wins.
… retired A retirement that finds no rows inherited the previous fence's recorded run even after the pane had left retirement without a new-turn restart (moved to another tab and back). A later hook-owned run whose row was dismissed could then have its process Done admitted with an empty prompt, where main shows nothing. The carry-over now applies only while the pane is still retired under that fence, the same condition the key merge uses. Also drops the unused agent type from the recorded run and renames the clock tests to describe behaviour.
Review status: ready (e49dfb5)The problem. After anything lists terminals ( What changes for you. What the review changed. The first version accepted the Done when the run's start time came before the retirement's clock time. The review replaced that with the design in the PR body: when retirement deletes the rows, it now remembers the run that was live in the pane, and the run's Done settles that run once. Nothing compares clocks anymore.
Deferred (P3, not fixed here).
Seen in QA, not caused by this PR (the same on main without any refresh).
Verified.
Not verified. Windows, Linux and SSH were not run live; the detector only runs for local panes, so SSH and WSL are unchanged by design. The OpenCode 2 TUI's own hook status and a Claude TUI were not exercised in the QA rig. |
|
| if ( | ||
| event?.endedProcessRunStartedAt !== undefined && | ||
| retirementFence?.retiredRun !== undefined && | ||
| retirementFence.retiredRun.state !== 'done' && | ||
| !this.hookOwnsProcessRun( | ||
| retirementFence.retiredRun.origin, | ||
| retirementFence.retiredRun.receivedAt, | ||
| event.endedProcessRunStartedAt | ||
| ) |
There was a problem hiding this comment.
Done can match another run If another terminal status replaces the OpenCode detector’s Working row before retirement—for example, an OSC Working row for a different agent—the fence captures that row. This branch still accepts the OpenCode process Done and publishes a completed OpenCode row that does not match the captured run. Matching the captured agent and run before admitting Done would prevent that misleading status.
Knowledge Base Used: Agent integrations and hooks
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (1)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (7)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review. 📝 WalkthroughWalkthroughPane retirement now records a status snapshot for the retired process run. Process-origin completion events include the ended run’s start time in status disposition. A matching completion can settle the captured run once when the run was not hook-owned. Added tests cover server status behavior and OpenCode completion through visible-pane events and daemon reports. Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to The change retains process Done after pane retirement while preserving hook ownership and retirement fences. No concrete merge-blocking risk remains identified; mergeable subject to normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change is narrowly scoped to preserving completion status for local terminal runs. Completion does not reopen retired authority, and closed tabs and hook-owned runs remain protected. No introduced security attack path was established, but incomplete evidence prevents a minimal-risk assessment. Retained concerns Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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 |
There was a problem hiding this comment.
ℹ️ No critical issues — one coverage gap worth a look.
Reviewed changes
- Settlement for a retired run —
retirePaneAuthoritycaptures the pane's live row asretiredRunand carries it over across repeated retirements while the pane stays retired;getAgentStatusDispositionaccepts a single process-lifetime Done to settle it, then re-fences. - Process-lifetime event routing —
processLifetimeEventand the sharedhookOwnsProcessRunhelper centralize the Working/Done and hook-ownership rules inserver-ingest-terminal.ts. - Tests — a new real-runtime integration test (
opencode-run-done-after-launch-authority-retirement.test.ts) plus ten cases inserver-process-lifetime-status.test.ts.
The mechanism is sound and well-scoped: I traced the accept branch against the restart branch, the closed-tab/fence.closed early return, the closed-only carry-over guard, and transfer/restore, and could not reach a state where a Done settles a run other than the one captured. The three integration tests and the key unit cases genuinely fail on base.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
| // Why: a new run supersedes the one the retirement captured. | ||
| if (retirementFence) { | ||
| retirementFence.retiredRun = undefined | ||
| } |
There was a problem hiding this comment.
The PR body's ablation claim covers "new-run clearing", but removing these four lines does not fail any test: I re-ran the ablation and server-process-lifetime-status.test.ts (17/17) and opencode-run-done-after-launch-authority-retirement.test.ts (3/3) both still pass. The existing does not let a run captured before a newer run settle that newer run case does not isolate this branch because its second retirePaneAuthority already finds the owner key absent from closedAgentStatusPaneKeys (dropStatusEntry does not re-add the closed mark) and captures nothing regardless.
Technical details
# Isolate the restart-branch retiredRun clear
## Affected sites
- `src/main/agent-hooks/server/server-status-disposition.ts:168-171` — clears `retirementFence.retiredRun` when a new turn restarts a retired pane. No test fails without it.
- `src/main/agent-hooks/server/server-process-lifetime-status.test.ts:227` — the nearest case, but it passes on base and with the guard removed.
## Required outcome
- Either add a case that fails when `:168-171` is removed, or drop the "new-run clearing" guard from the ablation/complexity claims.
- The guard is only load-bearing when a restart clears `closedAgentStatusPaneKeys` for the pane/owner keys but leaves another key of the same fence closed (a multi-key/alias fence), so a later retirement via that key would otherwise carry the superseded run. A test that retires a pane spanning a legacy alias, restarts through one key, then retires the alias key would exercise it.
## Suggested approach (optional)
- Reuse the alias/transfer fixtures already used by `does not carry a run over from a retirement the pane has since left` to build a fence spanning two keys before restart.|
Closing: the premise was wrong. This PR made a finished The row disappearing after a terminal-list refresh, which this PR treated as a bug, is that intended drop. The actual inconsistency is that #23722's |




ELI5
When you run
opencode run "<prompt>"in an Orca terminal, the sidebar should show Working while it runs and Done when it finishes. In practice the row usually just disappeared at the end and never showed Done. That happened once anything had asked Orca for its terminal list:orca worktree ps,orca terminal list, an agent using orchestration, the phone app refreshing, or an automation. Now the Done shows up and stays.What it fixes and scope
opencode runin a terminal pane loses its Done, and its row disappears. This happens on main today, in visible tabs and in tabs hidden before the run ends. It was introduced when fix(opencode): report OpenCode 2 status from each pane's own TUI #23722 addedopencode runstatus, and a false "restored" mark (described below) makes it frequent.orca worktree psand mobile status rows foropencode runpanes on the local machine.opencode rundetector only runs for local panes), and stale or late updates from agents that already ended. Those are still ignored exactly as before.What Changed
Before (user): run
opencode run "…"in a pane after anything has listed terminals. The row shows Working, then disappears when the run ends. Done never appears.After (user): the row goes from Working to Done when the run ends and stays there, as it does without a terminal-list refresh. Running the next command in that pane clears it, as it already does for other agents.
Mechanism, before:
terminal list,worktree ps, orchestration, the mobile refresh and automations) marks every live terminal as having "restored launch authority" (orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts). That is Orca's record that it launched an agent in the pane. It does this even for panes it never launched anything in.opencode rundetector posts its Done on that same command end, after the retirement. The status store sees a closed pane and drops the Done. The sidebar then removes the Working row.Mechanism, after:
server-authority-fences.ts,server-status-disposition.ts,server-ingest-terminal.ts,server-types.ts,server-state.ts.Why
opencode rundetector, for the run the pane showed when it retired, gets through. Late hook posts and terminal status stay fenced.Differences from the common pattern
Known follow-ups, not in this PR
worktree psand mobile no longer have. This already happens on main, and theopencode runflow ends in agreement with this PR. It is left for its own change.Linked Issue
Follow-up to Linear STA-8629 and #23722. Found by the review QA of #24238. #24238 and the restart follow-up (branch
brennanb2025/opencode-run-reattach-liveness) depend on this for Done after a hidden-tab start or a restart.Visual Proof
Real OpenCode 2 (2.0.21) in Orca terminal panes on a remote macOS machine. Each run ran
orca worktree psfirst, thenopencode run "…"(about 35 s). Right sidebar collapsed.Before (main): Working while it runs, then the row is gone and there is no Done.
After (this PR, e49dfb5): Working while it runs, then Done that is still there 60 s later.
orca worktree psshows the same Done row.Tab hidden before the run ends (this PR): Done is kept while hidden and after the tab is reopened.
A second refresh after a kept Done, then a new run in the same pane (this PR): Working, then Done again.
Testing
Manually tested on a remote macOS machine with real OpenCode 2 in Orca terminal panes (see Visual Proof)
Automated tests added/updated, or explained why not below
New
opencode-run-done-after-launch-authority-retirement.test.tsconnects a real runtime to a real hook server, wired as in the app. It covers a terminal-list refresh followed byopencode runin the visible pane and in the hidden pane, plus the wall clock stepping back during the run. Without the fix all of them read "missing".server-process-lifetime-status.test.tsadds cases for:The existing test that keeps a lone process Done out of a retired pane is unchanged and passes.
Ablation: removing each guard (the accept, one-shot, already-ended check, hook check, new-run clearing, carry-over, still-retired check) fails exactly its test.
64 related test files pass (626 tests).
pnpm tc:node, oxlint and the changed-code quality check pass.Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)