Revert "fix(agent-hooks): bind agent status to the pane its session was spawned into (STA-2069)" (#14615) - #15295
Conversation
📝 WalkthroughWalkthroughThe PR removes session-to-pane binding storage, attribution, cleanup, exports, mocks, and related tests. Agent hook routing now uses pane-key aliases and envelope worktree IDs. PTY spawn paths no longer inject Claude session IDs. Claude startup tests now expect the plain command. Shared Claude executable and startup-shell helpers, session-pinning logic, and related tests are deleted. Resume-command handling now performs local Claude executable detection and shell syntax validation. Merge Risk: 🟡 Moderate · up to The command-resume parsing change can rewrite unrelated shell commands in some valid invocations, potentially launching or resuming the wrong command. This bounded correctness issue should be fixed and covered by a regression test before merge. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 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 |
|
Adding the measurement behind reason 4 in the description, since a code-reading argument is not evidence. A failing test against Result on main: 2 failed, 1 passed. The event lands on pane A although the post named pane B: The third case is a control — identical payload, no binding — and it passes, so the fixture is routable and the assertions can be reached. The test observes its own failure case. Causation isolated. Neutralizing only
So the lever moves this correction and nothing else. Every link in the chain verified, including one that could have killed the claim.
Blast radius, stated fairly. The binding is cleared by PTY teardown (so pane close clears it), by connection disconnect — note that clear sits outside the And resuming into a different worktree is a first-class feature ( Not added as a test in this PR: it calls One clarification on a separate point: |
|
The The relay builds correctly in the e2e build job — all seven targets — but So every consumer that actually starts a relay fails with It surfaces on this PR only because the diff touches SSH-adjacent code, which makes the Will rerun this lane once #15303 lands. |
|
Second failure ( |
…as spawned into (STA-2069) (#14615)" This reverts commit 15efc87. The premise does not reproduce, the fix does not reach the failure that does, and the correction it installs can misattribute status on a path that worked before. 1. PREMISE FALSE. #14615 asserts Claude Code >= 2.1.206 hosts TUI sessions as workers under a shared daemon. On 2.1.233 `claude daemon status` reports "not running" while 69 interactive sessions are live, and all 68 running clients are direct children of their own pane's shell. Measured across the fleet: 68 distinct pane keys, ZERO collisions. The 19 keys with no live pane also have a dead ORCA_TERMINAL_HANDLE (pane closed, process outlived it). Terminals live under a *different* pane key: 0. Foreground attribution was never broken, so the correction has nothing to correct. 2. DOES NOT FIX THE REAL BUG. The failure #9236 describes is real but scoped to BACKGROUNDED sessions: a `--bg` / `/background` worker inherits the dispatching pane's whole ORCA_* set, and the hook script self-heals its endpoint while taking paneKey straight from ambient env, so the POST lands under the wrong pane. #14615 mints a binding only for launches Orca constructs, so a typed `claude --bg` produces none. That needs a `kind: "bg"` guard at ingest instead; #9236 is reopened for it. 3. INTRODUCES A MISATTRIBUTION. Bindings are created at two spawn sites and removed only on PTY death. A pane whose user exits Claude keeps its PTY, so the binding survives; resuming that session in another pane does not rebind (`--resume` is a session selector, so the pin declines), and resolveBoundPaneOverride then rewrites paneKey and tabId onto the ORIGINAL pane despite a correct posted key. That is the reported symptom, on a path that previously worked. Kept #14706's observations.rebind() in the conflicting hunk — it postdates #14615 and is not part of this revert; only #14615's comments were dropped and the surviving rationale reworded to alias resolution, which is what actually makes paneKey differ from envelope.paneKey now.
1c5349a to
d82a5cf
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/shared/agent-resume-launch-command.ts (1)
28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShorten the new parser comments. Keep each comment to one concise statement of the safety constraint.
src/shared/agent-resume-launch-command.ts#L28-L31: Reduce the command-position explanation to one line.src/shared/agent-resume-launch-command.ts#L103-L107: Reduce the modelability explanation to one line.As per coding guidelines, "
**/*.{ts,tsx,js,jsx}: Comments must be concise, non-obvious, and brief—prefer one line; do not explain obvious behavior or walk through code."Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b7b01234-30c1-4812-8132-49a3d3de109f
📒 Files selected for processing (16)
src/main/agent-hooks/agent-session-pane-bindings.test.tssrc/main/agent-hooks/agent-session-pane-bindings.tssrc/main/agent-hooks/server-agent-session-pane-attribution.test.tssrc/main/agent-hooks/server.tssrc/main/ipc/pty-ipc-mock-registry.tssrc/main/ipc/pty-ipc-suite-environment.tssrc/main/ipc/pty-login-shell-startup-commands.test.tssrc/main/ipc/pty.tssrc/main/pty/spawn-startup-shell.test.tssrc/main/pty/spawn-startup-shell.tssrc/shared/agent-hook-listener.tssrc/shared/agent-resume-launch-command.tssrc/shared/claude-launch-executable-token.tssrc/shared/claude-session-pin-launch-command.test.tssrc/shared/claude-session-pin-launch-command.tssrc/shared/tui-agent-startup-shell.ts
💤 Files with no reviewable changes (11)
- src/main/ipc/pty-ipc-suite-environment.ts
- src/shared/claude-launch-executable-token.ts
- src/main/agent-hooks/server-agent-session-pane-attribution.test.ts
- src/main/pty/spawn-startup-shell.ts
- src/main/agent-hooks/agent-session-pane-bindings.ts
- src/shared/claude-session-pin-launch-command.ts
- src/shared/tui-agent-startup-shell.ts
- src/main/pty/spawn-startup-shell.test.ts
- src/shared/agent-hook-listener.ts
- src/main/agent-hooks/agent-session-pane-bindings.test.ts
- src/shared/claude-session-pin-launch-command.test.ts
Included review availability: Your plan includes up to 10 reviews per rolling hour; 0 remain after this review.
| if (token === '--') { | ||
| commandPosition = true | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Restrict command-position resets to supported wrappers.
Line 50 treats every -- as a wrapper delimiter. For echo -- claude --resume stale, this function identifies claude as an executable and rewrites the unrelated command.
Track recognized wrapper syntax before restoring command position. Add a regression test that keeps this command append-only.
…as spawned into (STA-2069) (stablyai#14615)" (stablyai#15295) Reverts stablyai#14615. Its premise does not reproduce, it does not reach the failure that does, and the correction it installs can misattribute status on a path that worked before. 1. PREMISE FALSE. stablyai#14615 asserts Claude Code >= 2.1.206 hosts TUI sessions under a shared daemon. On 2.1.233 `claude daemon status` reports "not running" with 69 live interactive sessions, and every client is a direct child of its own pane's shell. Measured across the fleet: 68 distinct pane keys, zero collisions. Foreground attribution was never broken. 2. DOES NOT FIX THE REAL BUG. The failure in stablyai#9236 is real but scoped to BACKGROUNDED sessions, whose workers inherit the dispatching pane's whole ORCA_* set. stablyai#14615 mints a binding only for launches Orca constructs, so a typed `claude --bg` produces none. Fixed properly in stablyai#15304. 3. INTRODUCES A MISATTRIBUTION. Bindings are removed only on PTY death, and a user who exits Claude keeps the pane's PTY. Resuming that session in another pane does not rebind (`--resume` is a session selector, so the pin declines), and resolveBoundPaneOverride then rewrites paneKey and tabId onto the ORIGINAL pane despite a correct posted key. Demonstrated with a failing test against main; causation isolated to resolveBoundPaneOverride. Kept stablyai#14706's observations.rebind() in the conflicting hunk — it postdates stablyai#14615 and is not part of this revert.
…as spawned into (STA-2069) (stablyai#14615)" (stablyai#15295) Reverts stablyai#14615. Its premise does not reproduce, it does not reach the failure that does, and the correction it installs can misattribute status on a path that worked before. 1. PREMISE FALSE. stablyai#14615 asserts Claude Code >= 2.1.206 hosts TUI sessions under a shared daemon. On 2.1.233 `claude daemon status` reports "not running" with 69 live interactive sessions, and every client is a direct child of its own pane's shell. Measured across the fleet: 68 distinct pane keys, zero collisions. Foreground attribution was never broken. 2. DOES NOT FIX THE REAL BUG. The failure in stablyai#9236 is real but scoped to BACKGROUNDED sessions, whose workers inherit the dispatching pane's whole ORCA_* set. stablyai#14615 mints a binding only for launches Orca constructs, so a typed `claude --bg` produces none. Fixed properly in stablyai#15304. 3. INTRODUCES A MISATTRIBUTION. Bindings are removed only on PTY death, and a user who exits Claude keeps the pane's PTY. Resuming that session in another pane does not rebind (`--resume` is a session selector, so the pin declines), and resolveBoundPaneOverride then rewrites paneKey and tabId onto the ORIGINAL pane despite a correct posted key. Demonstrated with a failing test against main; causation isolated to resolveBoundPaneOverride. Kept stablyai#14706's observations.rebind() in the conflicting hunk — it postdates stablyai#14615 and is not part of this revert.
ELI5
We shipped a fix for a bug based on how we thought Claude Code worked. It doesn't work that way. The thing we "fixed" was never broken, the thing that is broken isn't covered, and the fix can send an agent's status to the wrong pane in a case that used to work. So: undo it.
What Changed
Reverts #14615 (
15efc87e35) in full — the minted--session-idpin, theAgentSessionPaneBindingstable, and the two ingest-side pane corrections.One deliberate deviation from a pure revert: the conflicting hunk in
src/main/agent-hooks/server.tscontainsthis.observations.rebind(paneKey), which comes from #14706 (26bdfc0fe4), not #14615. That call is kept. Only #14615's comments were dropped, and the surviving rationale reworded — post-revert,paneKeydiffers fromenvelope.paneKeybecause of legacy-alias resolution, not daemon correction.Why
1. The premise does not reproduce. #14615 states Claude Code >= 2.1.206 hosts TUI sessions as workers under a shared daemon that forwards only its own allowlisted env. On 2.1.233,
claude daemon statusreportsnot runningwhile 69 interactive sessions are live, and every running client is a direct child of its own pane's shell. Every daemon reference in the CLI changelog scopes to background sessions; the three acknowledged stale-env fixes all read "from the shell that started the background daemon."2. Foreground attribution was never broken. Reading the environment of all 69 live clients against 200 Orca terminals: 68 distinct pane keys, zero collisions. The 19 keys with no live pane also have a dead
ORCA_TERMINAL_HANDLE— the pane closed and the process outlived it; key correct, pane gone. Terminals still live under a different pane key: 0. No adoption drift, no re-keying. 65 of 69 clients predate the current Orca main process, so app restart is well covered by accident and shows no drift either. A correction with nothing to correct is pure risk.3. It does not reach the failure that does reproduce. The real bug is scoped to backgrounded sessions. Reproduced in an isolated config dir — pane A starts a background session, pane B starts one on the same daemon, and pane B's worker inherits pane A's entire
ORCA_*set:The tree is
daemon run (ppid 1) -> bg-pty-host -> bg-spare, detached from every pane PTY. It fails silently and successfully becausesrc/main/claude/hook-service.ts:100-116re-sources$ORCA_AGENT_HOOK_ENDPOINT— port and token self-heal so the POST lands — whilepaneKey=${ORCA_PANE_KEY}comes straight from ambient env with no refresh path. #14615 mints a binding only for launches Orca constructs, so a typedclaude --bgor an in-session/backgroundproduces none.4. It introduces a misattribution on a path that worked. Bindings are created at two spawn sites (
pty.ts:5477,:7163) and removed only on PTY death (pty.ts:2164). A user who exits Claude keeps the pane's PTY, so the binding survives. Resuming that session in another pane does not rebind —--resumeis inisClaudeSessionSelector, so the pin declines — andresolveBoundPaneOverridethen rewritespaneKeyandtabIdonto the original pane despite a correct posted key. Status lands on the wrong pane: the exact symptom #14615 set out to fix.Linked Issue
Reopened #9236 with the reproduction and the correct mechanism. This PR removes the incorrect fix; the actual fix is a
kind: "bg"guard at ingest and will land separately.Refs #9236
Visual Proof
N/A— this restores previously shipped behavior. There is no UI change; the observable difference is that hook events keep the pane key they post, which is what the fleet measurement above shows is already correct.Testing
macOS. Verified against the real Claude Code fleet on this machine (2.1.227 / 2.1.233, 69 live sessions), not against mocks:
claude daemon status->not runningwith 69 live interactive sessions; nodaemon runorbg-pty-hostprocess for any foreground pane.orca terminal list(200 terminals): 68 distinct pane keys, 0 collisions, 0 terminals live under a different key.CLAUDE_CONFIG_DIRon its own daemon socket; probe daemon stopped and all probe state removed afterward.Gates, run locally on the rebased branch:
pnpm typecheck— exit 0. Proven live, not assumed: injectingconst x: number = "s"made it exit 1 with TS2322, then reverted. This repo's typecheck prints nothing on success, so a silent pass is otherwise indistinguishable from a dead gate.vitest run --config config/vitest.config.tsoversrc/main/agent-hooks,src/shared/agent-hook-listener,src/main/ipc/pty,src/main/pty,src/shared/tui-agent-startup-shell,src/shared/agent-resume-launch-command— 162 files, 1772 passed, 13 skipped.oxfmt --check— clean, echoed "on 9 files" so the list actually arrived.Revert-safety checks, because a revert can silently delete a later commit's work:
Enumerated every commit touching each of the 16 reverted paths after
15efc87e35. Three files had one:server.tsandagent-hook-listener.ts(feat(agent-status): step 1 — stamp observation provenance and ordering at every status ingress (STA-4293) #14706),pty.ts(fix(runtime): stop one unreachable relay from freezing every workspace as active (STA-517) #14649).server.ts— conflict; kept feat(agent-status): step 1 — stamp observation provenance and ordering at every status ingress (STA-4293) #14706'sobservations.rebind(confirmed viagit log -Sthat feat(agent-status): step 1 — stamp observation provenance and ordering at every status ingress (STA-4293) #14706 introduced it, and that fix(agent-hooks): bind agent status to the pane its session was spawned into (STA-2069) #14615's diff never mentionsrebind).agent-hook-listener.ts— auto-merged; feat(agent-status): step 1 — stamp observation provenance and ordering at every status ingress (STA-4293) #14706'sisNewTurnEventconfirmed still present, only fix(agent-hooks): bind agent status to the pane its session was spawned into (STA-2069) #14615'sreadHookBodyProviderSessionIdremoved.pty.ts— auto-merged; fix(runtime): stop one unreachable relay from freezing every workspace as active (STA-517) #14649's STA-517 markers all confirmed present (STA-517,onSshInventoryUnavailable,listProcessesWithHostScope,deadlineMs,markSshInventoryUnverifiable), and the full diff versus main contains only fix(agent-hooks): bind agent status to the pane its session was spawned into (STA-2069) #14615's removals.Zero dangling references to any removed module or symbol.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
No tests added: this restores the prior state, and the three test files it removes are #14615's own. The behavior being restored is covered by the 1772 existing tests above.
Review
Agent skill upstream boundary
Notes
AgentSessionPaneBindingswas in-memory only.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)Author