Skip to content

fix(runtime): keep a slept remote shell terminal stopped - #24416

Open
innocarpe wants to merge 2 commits into
stablyai:mainfrom
innocarpe:fix-24399-remote-sleep-replay
Open

innocarpe wants to merge 2 commits into
stablyai:mainfrom
innocarpe:fix-24399-remote-sleep-replay

Conversation

@innocarpe

Copy link
Copy Markdown
Contributor

Description

Sleeping a paired headless workspace stops its terminal, then automatic recovery creates the same session again about a second later. Refreshing the desktop first, or sleeping a workspace that is not selected, stays stopped. Ordinary shell tabs have no agent resume record, and the park guard only looks at that record. The spawn lock then treats a sleep that became committed while the spawn waited as a user wake and clears it.

Focused fix

In scope: an automatic materialize refuses a host sleep phase of stopping, sleeping, or partial, and the one spawn acquire leaves a committed sleeping or partial phase in place instead of clearing it.

Out of scope: the user opening the tab, which remains the wake gesture. Agent resume records, SSH reattach hydration, and holding a second lock across terminal creation.

Preserves

An explicit user spawn still clears a committed sleep and creates the terminal. A shell tab with no host sleep still materializes. A slept agent pane stays parked. A failed SSH reattach still hydrates the headless snapshot.

Evidence

node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts --cache false src/main/runtime/worktree-terminal-spawn-sleep-disposition.test.ts src/main/ipc/pty/runtime/spawn-execute-sleep-disposition.test.ts — 5 passed.

node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts --cache false src/main/runtime/orca-runtime.test.ts -t "deliberately parked pane activation" — 18 passed, 1329 skipped.

The cases cover a shell tab with no host sleep, automatic recovery for sleeping, partial, and stopping, a user open of a sleeping shell tab, and the spawn acquire passing leave versus wake. A live remote workspace was not rechecked.

User-regression-tradeoffs

Automatic recovery no longer recreates a terminal while the host records that workspace as stopping, sleeping, or partially slept. Opening the tab still wakes it.

Fixes #24399

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds sleep-state tracking to terminal spawn logic.

The PR appears safe to merge, although the sleep-versus-spawn race still lacks a regression test.

Findings

  1. P2 Sleep race remains untested ▶

Summary

The PR prevents automatic recovery from recreating a terminal when its worktree is stopping or asleep, while preserving an explicit tab open as a wake gesture. The only change since the previous review adds a comment explaining the lock-ordering assumption; it does not change behavior.

Reviews (2) · Last reviewed commit: "fix(runtime): declare leaveWorktreeSleep..."

Comment on lines +385 to +390
it('keeps a committed host sleep when an automatic spawn reaches the lock', async () => {
const { runtime } = shellSessionRuntime()
rememberHostSleep(runtime, 'sleeping')

await expect(
runtime.acquireWorktreeTerminalSpawn(TEST_WORKTREE_ID, 'leave')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Sleep race remains untested The test inserts an already-sleeping record before the spawn tries to acquire the lock. It therefore does not cover the regression's timing: a sleep committing while the spawn waits. A future change to that ordering could pass these tests while recreating the terminal. Please add a test that holds the sleep lock, queues the spawn, then commits and releases the sleep.

Knowledge Base Used: PTY streaming and recovery

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@coderabbitai

coderabbitai Bot commented Oct 1, 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: 1130d95c-0246-4ec5-ae59-4da19c92ad7a

📥 Commits

Reviewing files that changed from the base of the PR and between 50f0b41d2c2bce27dfda932a6a88a65bbd03e91f and 9447a72.

📒 Files selected for processing (2)
  • src/main/runtime/runtime-pty-controller-contract.ts
  • src/main/runtime/worktree-terminal-spawn-sleep-disposition.ts

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


📝 Walkthrough

Walkthrough

The change adds host worktree sleep phases and leave or wake spawn dispositions. Automatic mobile-session activation checks host sleep state and requests that terminal creation preserve it. If spawn acquisition leaves the worktree asleep, activation handles that result without treating it as an error. User activation can still wake a sleeping worktree. Tests cover disposition handling, PTY spawn arguments, and mobile activation behavior.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 9447a

Automatic recovery now leaves a slept remote terminal stopped, while explicit opening still wakes it. No actionable merge-blocking risk remains from the supplied review.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 9447a

The change strengthens the distinction between automatic recovery and intentional reopening. The reviewed paths preserve stopped state, but external caller behavior and live remote operation remain partly unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The reviewed enforcement scope is a normalized worktree and its recorded PTYs and terminal handles. It controls whether recovery may recreate a terminal for that worktree; broader external caller authority was not established by this probe.

Trust Boundaries and Controls

  • observed — Only intent equal to automatic selects the restrictive recovery behavior; absent intent retains permissive wake behavior for compatibility. This classifier is policy input, not evidence that an external request represents a genuine user gesture.
  • observed — The reviewed recovery path still resolves the worktree and selects an existing tab before materialization. It forwards tab, leaf, session, workspace connection, command, and environment inputs alongside the new restriction rather than replacing those identity inputs.

Resilience and Maintainability Implications

  • inferred — Retaining partial sleep prevents automatic recovery from undoing already committed stops after an incomplete sleep operation. Explicit reopening remains the recovery path that clears this state.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the issue, scope, behavior, testing, trade-offs, and linked issue. However, it does not follow the repository template and omits several required sections, including ELI5, Wha… Restructure the description using the repository template. Add the missing required sections, provide visual proof or state N/A with a reason, complete the testing checkboxes, disclose AI use when applicable, and complete the review, notes,…
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preventing automatic recovery from restarting a slept remote shell terminal.
Linked Issues check ✅ Passed The PR meets the coding requirements in [#24399]. Automatic activation checks host sleep phases stopping, sleeping, and partial, and passes leaveWorktreeSleeping through the existing spawn pat…
Out of Scope Changes check ✅ Passed The changes stay within [#24399]. The new disposition types, sleep-state helper, argument propagation, error handling, and regression tests implement host-sleep protection and explicit wake behavior. …
Full details: Description check

Explanation

The description explains the issue, scope, behavior, testing, trade-offs, and linked issue. However, it does not follow the repository template and omits several required sections, including ELI5, What Changed, Why, Visual Proof or an explicit N/A, AI Disclosure, Review, Agent skill upstream boundary, Notes, and the Checklist.

Resolution

Restructure the description using the repository template. Add the missing required sections, provide visual proof or state N/A with a reason, complete the testing checkboxes, disclose AI use when applicable, and complete the review, notes, boundary, and checklist items.

  • Fix all pre-merge checks with AI
  • 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.

@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 critical issues — two minor suggestions inline.

Reviewed changes

  • Host sleep predicate + settle helper — new worktree-terminal-spawn-sleep-disposition.ts centralizes which phases block automatic recovery (stopping/sleeping/partial) and how a spawn may settle a committed sleep.
  • Spawn lock honors disposition — acquireWorktreeTerminalSpawn takes 'wake' | 'leave'; 'leave' keeps a committed sleep and throws WorktreeTerminalLeftAsleepError.
  • Automatic materialize refuses a host sleep — activateMobileSessionTab adds the host sleep phase to the park guard, forwards leaveWorktreeSleeping only for automatic activations, and swallows WorktreeTerminalLeftAsleepError without hydrating or rethrowing.
  • Plumbing + tests — leaveWorktreeSleeping travels through TerminalCreateOptions, the runtime-owned create opts, and RuntimePtySpawnArgs; three new unit/integration assertions plus the retained park tests.

ℹ️ Automatic recovery now relies on one call site swallowing a thrown lock error

The lock-level 'leave' fix only works because activateMobileSessionTab's catch recognizes WorktreeTerminalLeftAsleepError via instanceof and returns the pending snapshot. Any other future automatic materialize path that sets leaveWorktreeSleeping must repeat that swallow, or the error surfaces to the user. A comment tying the flag to the required catch (or a helper that both sets the flag and owns the handling) would make the coupling explicit.

Technical details
# leaveWorktreeSleeping and its error are a two-part contract

## Affected sites
- src/main/runtime/orca-runtime-perform-mobile-session-pty-records-refresh.ts:190-215 — sets `leaveWorktreeSleeping` and swallows `WorktreeTerminalLeftAsleepError`.
- src/main/runtime/orca-runtime-stop-terminals-for-worktree.ts:278-287 — throws only for disposition `'leave'`.

## Required outcome
- A caller that opts into `'leave'` cannot accidentally let the error escape.
- Coverage should exercise the interaction, not just each half.

## Open questions for the human
- Is `WorktreeTerminalLeftAsleepError` intended to be caught by exactly one call site, or should the runtime expose a materialize helper that owns both the flag and the catch?

</details>

<!--
Pullfrog review metadata. These findings were written against 50f0b41;
if commits have landed on fix-24399-remote-sleep-replay since, treat every specific bug, file, or
line callout as POTENTIALLY STALE and re-diff before acting on it.

- Mode: Review (initial)
- Files reviewed: 11
- Commits reviewed: 1
- Base: main (c991893)
- Head: fix-24399-remote-sleep-replay (50f0b41)
- Reviewed commits:
  - 50f0b41 — fix(runtime): keep a slept remote shell terminal stopped
- Prior pullfrog review: none
-->


<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->
<sup><a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-18px.png"><img src="https://pullfrog.com/logos/frog-green-full-18px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>&nbsp;&nbsp;| [Fix all ➔](https://pullfrog.com/trigger/stablyai/orca/24416?action=fix&review_id=5382363960) | [Fix 👍s ➔](https://pullfrog.com/trigger/stablyai/orca/24416?action=fix-approved&review_id=5382363960) | [View workflow run](https://github.com/stablyai/orca/actions/runs/36891454710/job/110468520944) | Using `deepseek-v4.1-flash` (free via [Pullfrog for OSS](https://pullfrog.com/for-oss)) | [𝕏](https://x.com/pullfrogai)</sup>

Comment thread src/main/runtime/orca-runtime-create-terminal.ts
Comment thread src/main/runtime/worktree-terminal-spawn-sleep-disposition.ts
innocarpe and others added 2 commits October 2, 2026 07:25
Automatic recovery recreated the terminal because the park guard only
consults an agent resume record, and the spawn lock treated a sleep
that committed while it waited as a user wake. A background materialize
now leaves a stopping, sleeping, or partial host sleep in place, and
the single spawn acquire uses that leave disposition.

Fixes stablyai#24399
Recovery already passes the flag and the spawn path reads it, but the controller options type omitted it. stopping stays out of the leave check because that phase never outlives the exclusive mutation lock.

Co-authored-by: Cursor <cursoragent@cursor.com>
@innocarpe
innocarpe force-pushed the fix-24399-remote-sleep-replay branch from 50f0b41 to 9447a72 Compare October 1, 2026 22:41
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (9447a72ee1)

RuntimePtyController.spawn now includes leaveWorktreeSleeping, which recovery already passed and spawn-execute already read. stopping is still not a leave phase: that value is written under the exclusive mutation lock and replaced with sleeping, partial, or deletion before the lock is released.

@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

The deltas since the prior pullfrog review at 50f0b41 close both prior inline findings; the substantive fix was already reviewed.

  • Spawn contract declares leaveWorktreeSleeping — RuntimePtyController.spawn's opts gained the optional field, so the plumbing no longer type-checks only because orca-runtime-create-terminal.ts is @ts-nocheck.
  • stopping asymmetry documented — settleWorktreeSpawnSleep gained a comment recording that stopping is always replaced before its exclusive mutation lock is released, so a shared-lock spawn never observes it.

I confirmed the stopping invariant in sleepResolvedWorktreeTerminals's finally block: the phase is rewritten to sleeping or partial (or the record deleted) before releaseMutation(), so the comment is accurate. pnpm tc:node and the two new sleep-disposition unit suites pass locally.

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

This branch has not been deployed

No deployments
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]: Sleeping a remote workspace immediately restarts its terminal until the desktop is refreshed

1 participant