Repository navigation
fix(terminals): negotiate explicit close intent for paired runtimes - #10129
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe RPC close handler now refuses reasonless closes from non-mobile clients while preserving explicit user and mobile close behavior. Encrypted authentication propagates runtime close-intent capabilities across runtime clients and connections. New fixtures bundle a legacy close client and support canaries across worktrees. A Playwright scenario exercises legacy closes across two daemon generations, records liveness and kill events, writes reconstruction data, and validates refusal and PTY survival. Reliability-gate metadata, assertions, evidence, budgets, and promotion criteria were updated accordingly. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
Adopt upstream stablyai/orca#10129: a reasonless session.tabs.close from an old paired runtime viewer is now refused + republished instead of destructively closing a live authoritative PTY across worktrees/daemon generations. Legacy mobile user-closes preserved; explicit-reason closes still close. Prod change is fail-closed (clientKind !== 'mobile'); +689 lines are the v25/v26 daemon-generation E2E safety harness + reliability gate.
…l-teardown # Conflicts: # src/main/runtime/rpc/e2ee-channel.ts # src/main/runtime/rpc/mobile-socket-wiring.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
config/reliability-gates.jsonc (1)
1165-1173: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
result: "passed"records a run that did not exercise the current oracle.The summary states this run captured baseline/current-main destructive behavior and that the capability-gated combined oracle "supersedes that candidate result and is pending GitHub CI". Since this is the only passed evidence run for
tests/e2e/daemon-generation-legacy-close-safety.spec.ts, the gate'spartialprotection is satisfied by evidence that explicitly disclaims the declared invariant/oracle. Either attach a run of the spec at its current head, or record this entry in a way that doesn't read as passing evidence for the new contract.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: aaa353b1-d2a2-42b2-a2a7-06a18cb99ad0
📒 Files selected for processing (2)
config/reliability-gates.jsoncsrc/cli/runtime/websocket-transport.test.ts
|
Addressed the evidence-integrity review in 480e8b0: the earlier baseline-only entry was replaced with the current-head GitHub CI result from run 30250731941 / shard 10 (the compatibility oracle passed in 5.1s), and deterministic red/green evidence is now marked complete. Overall gate protection remains partial for the explicit headed paired-server and headless serve live-topology gaps. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: be007343-938e-4ba8-964c-a043341fc59a
📒 Files selected for processing (17)
config/reliability-gates.jsoncsrc/cli/runtime/websocket-transport.test.tssrc/main/runtime/remote-runtime-request-connection.integration.test.tssrc/main/runtime/rpc/e2ee-channel-v2.test.tssrc/main/runtime/rpc/e2ee-channel.test.tssrc/main/runtime/rpc/e2ee-channel.tssrc/main/runtime/rpc/mobile-e2ee-auth-validation.tssrc/main/runtime/rpc/mobile-socket-wiring.test.tssrc/main/runtime/rpc/mobile-socket-wiring.tssrc/renderer/src/web/web-runtime-client.test.tssrc/renderer/src/web/web-runtime-client.tssrc/shared/remote-runtime-client.test.tssrc/shared/remote-runtime-client.tssrc/shared/remote-runtime-request-connection.test.tssrc/shared/remote-runtime-request-connection.tssrc/shared/remote-runtime-shared-control-connection.test.tssrc/shared/remote-runtime-shared-control-frame-handler.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- config/reliability-gates.jsonc
…l-teardown # Conflicts: # src/main/runtime/rpc/core.ts # src/main/runtime/rpc/dispatcher.ts
…r workers (stablyai#9925)" This reverts commit cd05f2f for the v1.4.160-rc.4 cut. Three files needed manual resolution because later commits touched the same regions. Their post-9925 work is preserved: - src/main/runtime/rpc/core.ts and dispatcher.ts keep clientCapabilities and the RuntimeCapability import from stablyai#10129 (close-intent negotiation for paired runtimes). - src/cli/runtime/client.ts keeps the deferred websocket-transport import from stablyai#10919.
Summary
session-tabs.close-intent.v1inside encrypted authentication for paired runtime clients.session.tabs.closeonly for in-process callers and paired runtime clients that advertised the capability.Cause
A pre-contract paired desktop viewer can turn stale PTY-exit observations into sequential reasonless
session.tabs.closerequests. The host treats those requests as explicit user intent and routes them throughcloseMobileSessionTab,RuntimeNotifier.closeTerminalTab, and immediate daemon shutdown. That RPC path bypasses #10013's orphan-sweep confirmation guard.An old viewer's intentional close and stale cleanup close are byte-identical. A global reasonless-close refusal therefore prevents the incident but makes mixed-version behavior worse than current main by breaking intentional closes from old clients.
Compatibility
reason: "user"remains destructiveThe plaintext hello now carries only the ephemeral ECDH public key. The capability is parsed from encrypted authentication after the device token succeeds, then bound to that authenticated socket. A destructive request cannot assert the capability itself, and an intermediary cannot strip it without invalidating encryption. Capability parsing is bounded to 64 non-empty strings of at most 128 characters.
Reproduction oracle
GitHub CI command:
The harness starts live v25 and v26 daemon generations. Each generation owns one capable-viewer PTY and one legacy-viewer PTY in separate worktrees, plus a fifth unrelated control PTY. An observer lists every target before, between, and after the close sequences while issuing zero closes.
A real-WebSocket integration contract separately proves encrypted auth metadata reaches authenticated socket state, RPC context, and the close-refusal handler.
Screenshots
No visual change.
Testing
pnpm typecheckpnpm build:desktopandpnpm build:webc528b6bb5pnpm lintpasses Oxlint, switch exhaustiveness, quadratic-buffer, reliability-gate, max-lines, skill-manifest, and localization-catalog checks. Its final localization-coverage step fails on six unchanged Ghostty search keywords from current main.No Playwright tests were run locally for this revision.
Review and security
Three internal review/fix rounds completed. The production path adds one bounded capability parse during authentication and one membership check per close. It adds no polling, subprocess, provider scan, retry, timer, filesystem mutation, path construction, dependency, or broad lifecycle change.
The capability only enables stricter self-denial. Local/in-process cleanup, typed lifecycle close, daemon routing, SSH/relay, WSL, reconnect, sleep/wake, shutdown, and explicit single-terminal close paths are otherwise unchanged.
Remaining gaps
orca serve.Supersedes #10013.
Fixes #9949.
Linear: STA-2354.