Skip to content

fix(agent-status): status follows the terminal, not the pane key it was spawned with - #24493

Closed
brennanb2025 wants to merge 6 commits into
mainfrom
brennanb2025/sta-8862-status-follows-terminal
Closed

brennanb2025 wants to merge 6 commits into
mainfrom
brennanb2025/sta-8862-status-follows-terminal

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​905 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​904
Prod 32 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​535 $\color{#cf222e}{\Huge{\mathbf{−}}}$​31 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​504

ELI5

When Orca restarts, your terminals keep running in the background. Sometimes Orca puts a surviving terminal back into a tab with a brand-new name. The agent inside that terminal (Claude, Codex, …) keeps reporting its status under the old tab's name, because that name was written into the terminal's environment when it started and a running program's environment can never change. So Orca filed the status under a tab that no longer exists, and the real tab showed nothing.

Now the background terminal host remembers which tab name it gave each terminal. When an agent reports under that old name, Orca looks up which terminal it belongs to and files the status on the tab that terminal is showing in now.

What Changed

The problem (GitHub #23753, Linear STA-8862). A terminal survived an Orca quit and relaunch, and the relaunch showed it in a tab with a new id. After that:

  • the live tab showed no agent status, and orca worktree ps listed the agent under a pane key no tab has;
  • on mobile, the long-press menu lost "Switch to chat view" (it needs a status on that tab);
  • running claude --resume in the same tab did not help, because it inherits the same old environment.

Why it happened. Every agent hook reports ORCA_PANE_KEY, the tab-and-pane position the terminal had when it was spawned. That value is frozen in the process environment. When the terminal later shows in a different tab, nothing connected the old key to the terminal, so status was filed by layout position instead of by terminal. After a restart the main process had also lost its in-memory "which terminal carried which key" map, so it could not even verify a move.

What you see now.

Before After
Agent status on a terminal that survived a restart into a new tab Missing; filed under the dead tab Shown on the tab the terminal is in
orca worktree ps row for that agent Old pane key Current pane key
Mobile "Switch to chat view" on that tab Missing Present
Status rows left under the dead key when the terminal exits Linger in last-status.json for up to 7 days Cleared with the terminal
A surviving terminal whose tab the renderer no longer has Re-created as a tab with a new id Re-created under its recorded tab id (unless you closed that tab)

Mechanism.

  1. The execution host records the key it exported. The terminal daemon (Session.envPaneKey, from ORCA_PANE_KEY at spawn) and the SSH relay (its existing PTY record's pane key) now publish it as an optional envPaneKey field on their PTY listings, next to the terminal handle they already publish. A reattach never re-exports environment, so the daemon keeps the spawn-time value. The main runtime stores it on its PTY record from that listing, and from the spawn itself for a fresh terminal. It lives exactly as long as the process: no TTL, nothing persisted beside the status store.
  2. Hook ingress resolves the key to the terminal's current pane. HTTP posts, spool replays and relay posts go through a new resolveHookPaneKey: the runtime finds the one live terminal that exported that key and returns the pane it is mounted in now (the same leaf lookup the in-app terminal status path already uses). Only a terminal on the host the post came from qualifies, and a local terminal whose exit was observed does not. Spool replay stays on the posted key, and the move below carries it over. It refuses to guess when the key still names a live pane, two live terminals exported it, or the terminal shows in more than one pane. Those posts land exactly where they do today.
  3. Rows already filed under the old key are moved by the host. When the runtime learns a terminal moved (registration, window-graph sync, inventory), the status store moves that key's rows and authority state to the current pane. This reuses the cache move transferPaneAuthority already does, extracted so it no longer has to mint an alias. If the current pane already has its own row, the newer of the two wins. The loser is dropped by its own key, never through the alias table.
  4. Cleanup is never routed. Clears, dismissals and retirements are handed layout keys, so a dead tab being cleaned up can never wipe the live tab's status.
  5. Exit clears both keys. The runtime's "pane keys this PTY owns" set now includes the exported key (unless another live terminal shows that pane or exported the same key), so the existing ended-process cleanup removes rows under it too.
  6. Presentation: a surviving terminal whose recorded tab the renderer no longer has comes back under that recorded id, the way the already-owned path does, so orca terminal list ids stay stable. A tab the user closed (a closed-tab record within its expiry) still gets a fresh tab. This is not the fix; it just keeps ids stable.

Hook scripts do not change. docs/reference/agent-status-store.md documents the new keying contract.

Why

The common pattern is that an agent event carries a host-owned terminal or session identity that does not depend on layout, and the layout maps that identity to a pane. Orca's event identity was the layout position itself. This change moves attribution to the execution host, which owns the process and already records its terminal handle, instead of asking the renderer to remember old-to-new mappings.

Alternatives considered:

  • An old-key → new-key alias table written when the renderer re-mints a tab. Rejected: it is a second guard beside the real fix (every path that can move a terminal needs its own call), the host could not verify the renderer's claim after a restart, it stores a fact the host can derive, and it does nothing for web or paired clients.
  • Never let a surviving terminal change tabs. Kept only as the presentation step above. It cannot remove the class: pane detach changes the tab by design, and id collisions still happen.

Differences from the common pattern:

  • Intended: hook posts keep carrying ORCA_PANE_KEY instead of a new terminal-id field. Safe because the host maps that key to a live terminal through its own record, so the key works as a process identity, and no hook script or agent integration has to change.
  • Temporary: status rows are still keyed by pane key, with the host routing to the current pane. Follow-up: key rows by terminal identity outright (stage 2 of STA-8862), then delete the UUID-to-UUID branch of the pane-key alias table.

Mixed versions (docs/reference/remote-wire-compatibility.md, Rule 1):

  • The daemon survives app updates and the relay updates on its own schedule. A new app with an old daemon or relay gets no envPaneKey and keeps today's behaviour (the saved binding when incarnation ids match, otherwise the row stays where it lands).
  • An old app with a new daemon or relay ignores the field: the listing admission copies only the fields it knows. tests/e2e/cross-version-wire/cross-version-pty-listing-env-pane-key.unit.test.ts runs the newest release's listing readers against rows carrying the field and pins that they read the rest of the row unchanged.
  • What the host publishes changes (Rule 3) only in that a status now appears on the live pane where readers saw none.

SSH, WSL and folder workspaces: the relay records the key it exported, and loss of contact never stops routing (only a certified exit does). A WSL hook relay is transport only, so its posts route to the local terminal. Identity is per terminal, so folder workspaces and git worktrees behave the same.

How this interacts with open work:

Linked Issue

Fixes #23753
Linear: STA-8862

Visual Proof

Real restart in an isolated rig (own HOME and profile, stub agent CLIs). A terminal survives quit and relaunch and is adopted into a tab with a new id. Its shell still exports the old pane key, and the agent hook is posted from that shell.

Before (main) After (this PR)
before: hook from the old key, tab and sidebar show no agent after: same hook, Terminal 1 and the sidebar show the working Claude status
Hook returns 204, the row is filed under the old key, and the live tab and sidebar show nothing. The same hook lands on the new tab: working spinner on Terminal 1, and "after restart – Claude" in the sidebar.

Testing

  • src/main/runtime/orca-runtime-terminal-env-pane-key.test.ts: the restart scenario through the real runtime and status store — inventory with the field, reattach into a new tab, hook post under the old key lands on the new pane; rows filed before the host knew are moved (both orders); exit clears both keys; an old daemon/relay without the field keeps today's behaviour.
  • src/main/agent-hooks/server-terminal-pane-routing.test.ts: HTTP hook ingress routing, row move, stale-row drop, and that cleanup of the dead key never reaches the live pane.
  • src/main/runtime/terminal-env-pane-key-routing.test.ts: every refuse-to-guess case.
  • Daemon (terminal-host-env-pane-key.test.ts), relay (pty-handler-ownership-attestation.test.ts), listing admission and the cross-version suite.
  • worktree-agent-activation-gate.test.ts: recorded tab recreated under its id; a closed tab still mints.
  • Ablation: with main's production code, 17 of the new tests fail; with the wiring kept and only the routing disabled, 9 of the 11 routing tests fail on their assertions (the 2 that pass are the "old host / cannot route" fallbacks, which must behave the same both ways).
  • tsc node and web projects clean; changed-lines quality gate and React Doctor clean.

Real-restart QA (macOS, local isolated rig, QA driver Codex gpt-6.1-sol high for the BEFORE arm and the repeat runs; Grok was out of balance (HTTP 402). Two AFTER runs used Codex gpt-5.6-terra medium, because the driver order in the global instructions read that way during the session):

Step Before (main) After (this PR)
Surviving terminal re-adopted into a new tab after relaunch yes, same PTY, shell still exports old key yes, same PTY, shell still exports old key
Hook from the surviving shell row under the old key; live tab shows nothing row under the new key; tab and sidebar show it
Rows already filed before the restart (row cleared by the closed-tab mark in this setup) moved to the new key when the tab was re-minted without a closed mark (earlier run); cleared with the closed mark, same as main
Terminal exits old-key row survives, still in last-status.json no row under either key; last-status.json has neither

Not covered by the rig: orca worktree ps and mobile were not exercised directly. They read the same store snapshot the rig checked, and the runtime tests cover the routing. SSH relay posts are covered by unit tests, not a live remote. Two early AFTER runs dropped the very first hook post in a brand-new profile. A targeted repeat (3 fresh profiles, posting after the terminal settled with no dialog open) produced the row every time, so I attribute it to posting during onboarding and terminal creation, not to this change. Real ~/.claude and ~/.codex config hashes were identical before and after every run. The live AFTER runs used commit 4adee50 (first review round). The second round changed only the store-side move and exit-cleanup rules, and those are covered by the unit and runtime tests above, not by a new live run.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Review

/code-review (high) found 10 issues. Fixed:

  • reconcile always dropped the moved row (it now keeps the newer row);
  • dropping went through the alias table (it now uses the raw key);
  • relay posts were routed across hosts (routing is now scoped to the posting host);
  • a dead local record counted as live (an observed local exit now stops routing);
  • the tombstone check skipped the expiry and the worktree (it now uses hasClosedTerminalTabRecord);
  • the move didn't consume retirement restart ids the way transferPaneAuthority does (it now does);
  • comments were verbose (trimmed).

A second /code-review (high) pass on the fixes found 10 more issues. Fixed:

  • a newer moved row now fully replaces the pane's older state, not just its row;
  • aliases that pointed at the moved key are repointed, and its restart-token entry is cleared;
  • exit cleanup skips a key that another live terminal shows or also exported;
  • spool replay stays on the posted key, because its launch-token fence is recorded there;
  • the runtime now hands the store resolved moves from one indexed pass, scoped to the terminal's host (this was O(n²) per graph sync);
  • a relay that publishes a non-string key can no longer abort the inventory refresh;
  • the closed-tab check matches by tab id alone, as main does.

Kept from pass 2:

  • A new incarnation inheriting the old exported key when the record had no incarnation. Kept: resetting there would also wipe a key learned at spawn whenever a host reports its incarnation late. Every daemon and relay that publishes the key also reports incarnations.
  • Two routing sources. Temporary, as above.

PR review bots (Greptile, CodeRabbit, pullfrog) raised 6 more issues. All are fixed, with tests:

  • WSL relay posts (wsl:<distro>) are treated as the local host they relay for, so routing works for WSL terminals;
  • the winning row is chosen by evidence age, not delivery time, so a stale spool replay can't replace a live row;
  • the winning row keeps its own launch authority;
  • reconcile follows the alias this terminal's own pane move minted;
  • a row another host filed on the destination pane is never replaced.

Kept from pass 1, with reasons:

  • Late posts from a closed pane's surviving process are accepted on the pane it moved to. Intended: routing happens only for a live terminal mounted in that pane right now, so the agent is visible there and its status belongs there.
  • A second routing source beside the alias table. Temporary: the resolver is derived from the host's process record rather than stored. Stage 2 keys rows by terminal identity and deletes the UUID-to-UUID alias branch, as described under "Differences".
  • Cost of the reconcile pass. It scans only terminals whose exported key differs from their current pane (normally none) and checks the store's cheap "anything under this key?" first.

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

Security: the exported key comes from the execution host's own spawn record, not from a renderer claim; listing admission bounds it like the terminal handle. Cross-platform: no path or shortcut changes. SSH: covered above. Mobile: no client change; the row lands on the live pane. Performance: resolution scans the runtime's PTY records only for posts and on layout changes, and is a no-op when no terminal has moved.

Author

  • X / Twitter: @BrennanKB5

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)

@brennanb2025
brennanb2025 force-pushed the brennanb2025/sta-8862-status-follows-terminal branch 3 times, most recently from 4adee50 to 3165f83 Compare October 2, 2026 03:49
@brennanb2025
brennanb2025 marked this pull request as ready for review October 2, 2026 04:03
@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Refactors how agent status tracks terminals across pane changes.

The PR appears safe to merge based on the reviewed changes and resolved findings.

Summary

The PR routes agent hooks from a terminal’s spawn-time pane key to its current pane and reconciles status already filed under the old key. The change since the previous review moves the destination-host check ahead of alias and retirement-state mutations, addressing the remaining finding. All three previous Greptile threads are resolved.

  • Adds host-published spawn keys, runtime routing, status reconciliation, and exit cleanup.
  • Preserves recorded tab IDs during adoption unless the tab was closed.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  H[Agent hook: spawn-time pane key] --> R[Runtime resolves live terminal]
  R --> P[Current pane]
  S[Status under old key] --> M[Host-scoped reconciliation]
  M --> P
Loading

Reviews (3) · Last reviewed commit: "fix(agent-status): check the destination..."

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered

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: 7e13d108-a47c-4ac4-886a-24336bff1570

📥 Commits

Reviewing files that changed from the base of the PR and between 3ee16ec and e103f0d.

📒 Files selected for processing (2)
  • src/main/agent-hooks/server-terminal-pane-routing.test.ts
  • src/main/agent-hooks/server/server-terminal-pane-routing.ts

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


📝 Walkthrough

Walkthrough

The change carries the pane key exported to a PTY process through daemon and relay listings into runtime records. The runtime resolves eligible keys to current terminal panes and reconciles agent-hook rows when pane bindings change. Hook ingestion uses terminal-aware resolution, while spool replay retains alias-based normalization. The renderer can also adopt a PTY under its recorded tab when that tab is absent and has no closure tombstone.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to e103f

The change routes agent status to the pane where a surviving terminal is currently mounted. No concrete merge-blocking risk remains from the supplied evidence.

Security Architecture Review

Security architecture risk: 🔵 Low · up to e103f

The inspected routing preserves authentication and execution-host checks and rejects ambiguous terminal ownership. No security bypass was established. Remaining uncertainty concerns recovery after interrupted state migration and ownership when an old pane identity is reused.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The inspected transition affects source and destination pane status, cached hook evidence, provider-session attribution, launch-authority commitments, and aliases targeting the source. Runtime move collection scopes carrier identity by execution host and rejects duplicate carriers or competing live source panes.

Trust Boundaries and Controls

  • observed — Local HTTP hook authentication precedes normalization. Remote ingestion retains connection provenance validation and applies status disposition after routing, including launch-token and retired-pane controls. Spool replay intentionally avoids live terminal routing.

Resilience and Maintainability Implications

  • inferred — An exception from alias persistence could interrupt reconciliation after alias repointing but before authority migration. The production callback assigns the alias entries and schedules persistence; no concrete throwing path was established. This remains a bounded recovery uncertainty rather than a verified security failure.

Hardening Proposals

  • proposed — Consider making the terminal-ownership requirement explicit for raw authority migration, through process-incarnation validation or a documented cleanup guarantee, and containing persistence notification failures so aliases and authority cannot diverge. These are defensive design proposals, not observed vulnerabilities.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 39 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: agent status now follows the terminal instead of its original pane key.
Description check ✅ Passed The description is complete and directly addresses the template. It includes the user impact, implementation details, rationale, linked issue, visual proof, testing, compatibility, review notes, and c…
Linked Issues check ✅ Passed PASS. The PR meets the coding requirements in [#23753]. It preserves the spawn-time ORCA_PANE_KEY, resolves it to the terminal's current pane after restart, moves existing status rows, and clears ro…
Out of Scope Changes check ✅ Passed PASS. The changes stay within [#23753]. Daemon, relay, PTY, runtime, agent-hook, renderer, compatibility documentation, and test changes implement restart-safe terminal identity and agent-status routi…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 62999f1b-29bb-4b02-a363-bfaaaacd9b98

📥 Commits

Reviewing files that changed from the base of the PR and between 9e34bd0 and 3165f83.

📒 Files selected for processing (40)
  • docs/reference/agent-status-store.md
  • src/main/agent-hooks/server-terminal-pane-routing.test.ts
  • src/main/agent-hooks/server/server-authority-aliases.ts
  • src/main/agent-hooks/server/server-authority-fences.ts
  • src/main/agent-hooks/server/server-cleanup.ts
  • src/main/agent-hooks/server/server-ingest-normalization.ts
  • src/main/agent-hooks/server/server-ingest-remote.ts
  • src/main/agent-hooks/server/server-state.ts
  • src/main/agent-hooks/server/server-terminal-pane-routing.ts
  • src/main/daemon/daemon-pty-session-inventory.ts
  • src/main/daemon/session-options.ts
  • src/main/daemon/session.ts
  • src/main/daemon/terminal-host-env-pane-key.test.ts
  • src/main/daemon/terminal-host-session-create.ts
  • src/main/daemon/terminal-host-session-listing.ts
  • src/main/daemon/types.ts
  • src/main/ipc/pty/ipc/spawn-commit.ts
  • src/main/orcad/orcad-entry.ts
  • src/main/providers/pty-process-info.ts
  • src/main/providers/pty-process-list-admission.test.ts
  • src/main/providers/pty-process-list-admission.ts
  • src/main/runtime/agent-status-store-wiring.test-fixture.ts
  • src/main/runtime/orca-runtime-get-orchestration-dispatch-authority.ts
  • src/main/runtime/orca-runtime-on-pty-exit.ts
  • src/main/runtime/orca-runtime-preserved-branch-cleanup.ts
  • src/main/runtime/orca-runtime-record-pty-worktree.ts
  • src/main/runtime/orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts
  • src/main/runtime/orca-runtime-register-pty.ts
  • src/main/runtime/orca-runtime-state-fields.ts
  • src/main/runtime/orca-runtime-sync-window-graph.ts
  • src/main/runtime/orca-runtime-terminal-env-pane-key.test.ts
  • src/main/runtime/runtime-terminal-state-records.ts
  • src/main/runtime/terminal-env-pane-key-routing.test.ts
  • src/main/runtime/terminal-env-pane-key-routing.ts
  • src/main/startup/main-process-runtime-service.ts
  • src/relay/pty-handler-ownership-attestation.test.ts
  • src/relay/pty-handler.ts
  • src/renderer/src/lib/worktree-agent-activation-gate.test.ts
  • src/renderer/src/lib/worktree-agent-live-surface-adoption.ts
  • tests/e2e/cross-version-wire/cross-version-pty-listing-env-pane-key.unit.test.ts

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

@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

The routing never engages for WSL-relayed hooks, so the fix is inert on Windows/WSL folder workspaces, and a stale spool replay can overwrite a live row during reconciliation. Details inline.

Reviewed changes

  • Execution host records the key it exported — the daemon (Session.envPaneKey) and SSH relay publish an optional envPaneKey on PTY listings; the runtime stores it on its PTY record (spawn, inventory, or registration) and clears it on certified exit.
  • Hook ingress resolves the exported key to the terminal's current pane — resolveHookPaneKey (HTTP and relay) routes to the live terminal's current pane via resolveTerminalPaneForEnvPaneKey; spool replay deliberately stays on the posted key.
  • Store relocates filed rows — new reconcileMovedTerminalPaneKeys moves rows, authority, and aliases to the current pane via the extracted movePaneAuthorityState; transferPaneAuthority now reuses that extraction without minting an alias.
  • Renderer adoption — a recorded tab the renderer no longer has is recreated under its recorded id unless a closed-tab tombstone exists.
  • Wire compatibility — envPaneKey is optional and bounds-checked; a cross-version suite pins old/new reader skew.
  • Tests — new runtime, daemon, relay, ingress-routing, admission, and cross-version suites.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

…as spawned with (STA-8862)

A terminal that survives a restart keeps exporting the ORCA_PANE_KEY it was
spawned with, so hook status landed on a tab that no longer exists. The
daemon and SSH relay now record that exported key with the process and
publish it as an optional listing field; the hook server resolves a posted
key to the pane the live terminal shows now, moves rows already filed under
it, and clears both keys on exit. A recorded tab the renderer lost comes
back under its own id.
Keep the newer row when the moved key and the live pane both report, drop the
loser by its own key, scope ingress routing to the posting host, stop routing
for a local record whose exit was observed, and honor the tombstone TTL.
The runtime hands the store resolved moves from one indexed pass, scoped to
the terminal's host. A newer moved row fully replaces the pane's older state.
Aliases that targeted the moved key are repointed. Exit cleanup skips a key
another live terminal shows or exported. Spool replay stays on the posted key.
Inventory tolerates a non-string exported key. The closed-tab check uses the
tab id alone.
Treat a WSL relay connection as the local host it relays for. Judge which row
wins by the age of its evidence, not its delivery, so a stale spool replay
cannot replace a live row. Keep the winning row's own launch authority. Follow
the alias this terminal's own pane move minted, so its state is moved too.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/sta-8862-status-follows-terminal branch from 3165f83 to 6b296cf Compare October 2, 2026 04:54
…liases or fences

A move rejected because the destination pane holds another host's row had
already repointed and persisted the aliases, so later posts falling back to
an alias could land on the other host's pane.

@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.

✅ No new issues found.

Reviewed changes

  • Destination host is checked before any mutation — reconcileMovedTerminalPaneKeys now rejects a move whose destination pane already carries another host's row before consuming retired restart ids or repointing aliases, so a rejected move leaves fences and aliases exactly as they were (server-terminal-pane-routing.ts:48-55).
  • Regression coverage — a new test proves a rejected cross-host move does not repoint an alias that falls back onto the exported key (server-terminal-pane-routing.test.ts:181).
  • Prior review feedback confirmed addressed — the three threads from the last pullfrog review are fixed in 6b296cf and retired: WSL relay ids normalize to the local host, the reconcile winner is judged by evidence age (evidenceObservedAt ?? receivedAt), and an alias minted for the same ptyId is followed.

Local verification: server-terminal-pane-routing.test.ts passes 12/12; the PR's other new suites pass (90 passed / 5 skipped). The cross-version suite fails only because the checkout is shallow with no release tags — an environment limitation, not a code issue.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing this older implementation and rebuilding status routing at the current terminal owner. Status reports reaching a dead tab after restart remain tracked in #23753.

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.

[Bug]: Agent status goes to a dead tab after Orca restart — surviving shells keep the old ORCA_PANE_KEY, mobile hides Switch to chat view

1 participant