Skip to content

fix(terminal): make remote stop retirement durable - #11804

Closed
revofusion wants to merge 2 commits into
stablyai:mainfrom
revofusion:revofusion/fix-durable-terminal-retirement
Closed

revofusion wants to merge 2 commits into
stablyai:mainfrom
revofusion:revofusion/fix-durable-terminal-retirement

Conversation

@revofusion

Copy link
Copy Markdown

Summary

  • resolve terminal lifecycle mutations through the unique persisted workspace-session owner when repo metadata points at a stale runtime partition
  • make direct terminal.stop await provider exit so host-side tab retirement and forced persistence finish before the RPC returns
  • cover restored whole-tab close and stop-before-restart durability with runtime regressions

Closes #11803.

This stays separate from #11801: that PR repairs paired-viewer null-PTY reconciliation, while this change owns host runtime session routing and durable PTY stop receipts.

Root cause

A restarted remote runtime could retain repo metadata whose executionHostId named an obsolete runtime partition, while the worktree's tabs remained uniquely owned by the local workspace session. Whole-tab close followed the stale catalog owner and returned tab_not_found.

Bulk stop had a second gap: its direct path called fire-and-forget kill, so the RPC could acknowledge before onPtyExit removed and flushed the persisted terminal surface. Restarting the relay during that gap restored the tabs and respawned PTYs.

Verification

  • pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts — 955 passed, 1 skipped
  • pnpm typecheck
  • pnpm run check:code-quality:changed
  • pnpm run check:max-lines-ratchet

@greptile-apps

greptile-apps Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR fixes two independent gaps in remote terminal lifecycle management. First, getWorkspaceSessionHostIdForWorktree now detects when repo metadata names a stale runtime partition and falls back to the unique persisted session that actually owns the worktree tabs, so whole-tab close no longer returns tab_not_found after a relay restart. Second, stopTerminalsForWorktree switches from fire-and-forget kill to an awaited stopAndWait call so the RPC does not return until onPtyExit has de-persisted and flushed the terminal surface, preventing tab respawn on relay reconnect.

  • Stale-host routing: getWorkspaceSessionHostIdForWorktree checks tabsByWorktree across all persisted sessions; if exactly one non-preferred host owns the worktree, it is returned as the authoritative owner. Ambiguous or missing cases fall back to the catalog-preferred host.
  • Durable stop: stop() is now unconditionally async and always calls stopAndWait when available; a try/catch isolates per-PTY provider errors so a single unavailable relay does not abort the worktree sweep.
  • Type tightening: stopPty's stop callback is narrowed from () => boolean | Promise<boolean> to () => Promise<boolean>, matching the now-always-async implementation.

Confidence Score: 5/5

Safe to merge; both fixes are narrowly scoped, the stale-host fallback is conservative (requires exactly one unambiguous owner), and all three new regression tests pass end-to-end lifecycle scenarios.

The stale-host routing change adds a bounded search over persisted sessions and returns the preferred host whenever the result is ambiguous, keeping existing behavior intact for healthy state. The durable-stop change replaces a fire-and-forget with an awaited call and catches provider errors per-PTY, so neither the happy path nor failure cases can leave the loop in a worse state than before.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/main/runtime/orca-runtime.ts Two targeted fixes: stale-host routing in getWorkspaceSessionHostIdForWorktree (falls back to the unique persisted session owner when repo metadata points at an obsolete partition), and durable stop in stopTerminalsForWorktree (always awaits stopAndWait instead of fire-and-forget kill, with per-PTY error isolation via try/catch).
src/main/runtime/orca-runtime.test.ts Adds three new regression tests pinning the stale-host lookup order, durable PTY retirement before RPC return, and per-PTY error isolation during a worktree sweep; existing stopPty callback mocks updated to match the narrowed Promise<boolean> return type.

Reviews (2): Last reviewed commit: "fix(terminal): isolate worktree stop fai..." | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ab81bd4-3f27-405d-be88-e0678577a4c4

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa75fd and 306eed5.

📒 Files selected for processing (2)
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/orca-runtime.test.ts

📝 Walkthrough

Walkthrough

The runtime now resolves a worktree to a unique persisted workspace-session host when the preferred host does not own the worktree. PTY shutdown now awaits provider retirement through stopAndWait when available, applies the teardown deadline, handles stop failures, and awaits the default stop result. Tests cover stale-host terminal closure and durable PTY cleanup without fallback killing.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change and verification, but it omits the required Screenshots, AI Review Report, Security Audit, and Notes sections. Add all required template sections and include the requested platform compatibility and security audit details.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: making remote terminal stop retirement durable.
Linked Issues check ✅ Passed The changes address issue #11803 by routing stale ownership correctly and awaiting durable terminal retirement before stop succeeds.
Out of Scope Changes check ✅ Passed The runtime changes and regression tests remain within the linked issue scope of stale session routing and durable terminal stopping.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/runtime/orca-runtime.ts (1)

25786-25809: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Isolate per-terminal stop failures so one rejection does not abort the whole worktree sweep.

PTY stopping is now asynchronous. It waits for provider exit through stopAndWait when available, passes the teardown RPC deadline when applicable, falls back to kill, and awaits the default stop result. This replaces a synchronous, non-throwing kill() call with an await this.ptyController.stopAndWait(...) call that can reject (for example, on a transient SSH or relay error).

In the branch used when options.stopPty is not supplied, await stop() at Line 25803 has no try/catch. If stopAndWait rejects for one ptyId, the exception propagates out of the for loop and stopTerminalsForWorktree stops processing the remaining ptyIds in ptyIds. Other terminals in the same worktree stay unstopped.

Elsewhere in this class, sleepResolvedWorktreeTerminals uses Promise.allSettled for the equivalent stopAndWait fan-out specifically to isolate per-PTY failures. Apply the same isolation here, since this path backs worktree-wide terminal teardown.

🔧 Proposed fix: catch stop failures per PTY
       const stop = async (): Promise<boolean> => {
         if (options.deadline !== undefined && Date.now() >= options.deadline) {
           return false
         }
-        // Why: terminal.stop is a durable lifecycle receipt; wait for provider exit so onPtyExit de-persists the tab before returning.
-        if (this.ptyController?.stopAndWait) {
-          if (options.deadline !== undefined) {
-            return await this.ptyController.stopAndWait(ptyId, {
-              deadlineMs: teardownRpcDeadline(options.deadline)
-            })
+        try {
+          // Why: terminal.stop is a durable lifecycle receipt; wait for provider exit so onPtyExit de-persists the tab before returning.
+          if (this.ptyController?.stopAndWait) {
+            if (options.deadline !== undefined) {
+              return await this.ptyController.stopAndWait(ptyId, {
+                deadlineMs: teardownRpcDeadline(options.deadline)
+              })
+            }
+            return await this.ptyController.stopAndWait(ptyId)
           }
-          return await this.ptyController.stopAndWait(ptyId)
+          return Boolean(this.ptyController?.kill(ptyId))
+        } catch (error) {
+          console.error(`[runtime] failed to stop terminal ${ptyId}`, error)
+          return false
         }
-        return Boolean(this.ptyController?.kill(ptyId))
       }

Wrapping inside stop() protects both call paths: the direct { stopped: await stop(), owner: true } branch, and the options.stopPty(ptyId, stop) branch, in case that external callback also lets stop() reject unguarded.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f0901042-f12a-4ec3-9be1-f2aaacea9770

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8908b and 8aa75fd.

📒 Files selected for processing (2)
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts

Comment thread src/main/runtime/orca-runtime.test.ts Resolved
Comment thread src/main/runtime/orca-runtime.test.ts Resolved
@revofusion

Copy link
Copy Markdown
Author

@greptile-apps @coderabbitai Addressed the remaining automated-review findings in 306eed5. The stopPty callback contract now requires Promise<boolean>, a rejected provider stop is isolated per PTY so later worktree terminals are still processed, and both lifecycle regressions now pin the lookup order and genuinely asynchronous stop receipt. Verification: runtime suite 956 passed / 1 skipped, pnpm typecheck, changed-code quality, and max-lines ratchet.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@revofusion Confirmed. The changes in 306eed50 address the reported lifecycle findings. The stated verification also covers the affected runtime behavior and static checks.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@nwparker

nwparker commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Closing as superseded — the linked issue (#11803) has been resolved on main by #17159 (fbe94ceff6), #18073 (573537ecd4). Thanks for the contribution!

@nwparker nwparker closed this Nov 5, 2026
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]: Remote terminal stop can respawn tabs from a stale session owner

4 participants