Skip to content

feat(sidebar): main runs workspace sleep; the window follows (P3b) - #31564

Merged
Jinwoo-H merged 37 commits into
mainfrom
sidebar-p3b-main-lifecycle
Oct 22, 2026
Merged

Jinwoo-H merged 37 commits into
mainfrom
sidebar-p3b-main-lifecycle

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 75 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1579 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1266 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​313
Prod 65 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1536 $\color{#cf222e}{\Huge{\mathbf{−}}}$​590 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​946

ELI5

Putting a workspace to sleep used to be run by the desktop window: it stopped the terminals, chats and VM itself, and a phone's sleep had to ask the window to do it. Now the desktop's main process runs every sleep, whoever asked (sidebar, phone or CLI), and the window follows along by hiding the workspace's panes and browsers.

Waking works the same way. Main records which agents to resume and is the only thing that resumes them, each in its own pane. The window just connects to the terminal main started. The window's own sleep code and its "relaunch in a new tab" wake are deleted.

The menu and toasts are unchanged, including the "kept open" message when the host cannot confirm the terminals stopped. A phone's or the CLI's failed sleep still shows that toast on the desktop. One message is new: when only the VM fails to suspend, the toast now says the terminals stopped but the machine is still running, instead of "kept open".

What Changed

Main runs sleep (workspace-sleep-execution.ts, workspace-sleep-runtime.ts)

  • For each workspace, in order:
    1. tell the window "sleep started"
    2. stop the terminals, here or on the paired server that owns them
    3. stop the workspace's chats
    4. suspend its VM, unless the workspace was reopened in the meantime
    5. tell the window "sleep ended", with the stopped terminals and main's wake records
  • Only lost contact makes a sleep unconfirmed: the host could not prove its terminals stopped, or a paired server never answered. Unconfirmed pauses that workspace's line (feat(sidebar): one lifecycle line per workspace in main, with in-progress hints (switch-gated) #31553, switch on). A definite answer (a refusal, a terminal the server still sees running, a chat stop that reported a failure) is a plain failure with today's toast and no pause.
  • A paired server's chats are asked with the Sleep mark. An older server refuses that field, so it is asked again without it. Before, main relied on a server status it might never have checked.
  • New IPC for the window: worktrees:sleep, plus worktrees:sleepReopened (the user opened the workspace mid-sleep, so its VM keeps running). A phone's activation sends the same signal to main.
  • The desktop passes one adapter where it builds the runtime. The adapter reaches paired servers and suspends VMs. The headless server gets none and refuses those parts with an error code.

Wake has one executor: main (restored-pane-materialize.ts, apply-mobile-session-tab-navigation.ts, main-resumed-sleep-pane.ts)

  • Main records the wake records when it stops the terminals. Only main resumes them, through the same in-pane resume a host with no window already used (fix(remote): opening a slept running agent on a host with no window resumes it, never a fresh launch #29950), with a window open or not.
  • Opening a slept pane in the window asks main to resume it (worktrees:resumeSleptPane). The pane's own start then connects to main's terminal instead of launching anything.
  • A phone opening the workspace queues main's wake. The queued step ends only when the resumes have started, so a sleep or delete issued after it waits for them. With no window open it still tells the sleep the workspace was reopened, so a sleep still running leaves the VM up.
  • The window keeps a read-only copy of the records, to know which panes to ask main about. It never writes or clears them: every window save keeps main's records, including a full save that omits the field and a patch that sets it to nothing (which is what the window sends after it drops its copy on a resume). The layout-owner build no longer echoes them back.
  • A phone opening a workspace on a server with no window is told "Open Orca on the host" only for agents only a window can wake. A running agent's own resume record and a workspace sleep's records (main resumes those) do not count.
  • A phone opening a slept workspace also wakes the window's still-mounted panes of agents that had finished before the sleep, as it did before. The pane asks main, so each agent resumes once; a tab that is not mounted still waits until it is opened.
  • Completed-agent hibernation, which the window plans itself, keeps its window wake.
  • A paired server keeps its own records and resumes its own agents, as before.

Failure messages

  • If only the VM fails to suspend, the sleep still counts as done: the terminals stopped and the row stays hidden. The toast says "The workspace's terminals stopped, but its machine couldn't be suspended and is still running." instead of "kept open", and the window no longer rolls the sleep back. Jinwoo approved this behaviour and copy.
  • A phone's or the CLI's sleep that fails or goes unconfirmed tells the window, which shows the same toast as its own Sleep. A sleep cancelled by a delete shows nothing.

The window follows (workspace-sleep-follower.ts)

  • On "started", the window deselects the workspace (keeping the sidebar position) and hides its browsers. It detaches its terminal panes and waits on main's stop instead of killing them itself.
  • On "ended" with a failure, it restores the selection and clears the sleep mark.

Phone, CLI and web

  • A phone's worktree.sleep is answered as soon as the sleep is accepted. The phone never reads the reply, and stopping chats can take longer than the phone's 30-second request timeout. A phone's activation no longer waits on a queued wake.
  • worktree.sleep takes an optional markChatsAsleep and replies with the chat receipts; older hosts ignore the field. The web client passes it, keeps the receipts, and waits past the two-minute chat stop.

Deleted

  • The window-side sleep code: the paired-server branch in terminal-shutdown.ts and its tests (ported to remote-workspace-terminal-sleep.test.ts in main), and sleep-workspace-chat-stop.ts.
  • The VM suspend IPC (ephemeralVm:suspendWorkspace).
  • The window's relaunch of a workspace sleep's agents in fresh tabs, and the "window present" split in main's resume.
  • The sleep-request relay from main to the window (worktree-sleep-request-relay.ts, shared/worktree-sleep-request.ts).
  • The sleepWorktree notifier and the admission test for the old flow.
  • remote-worktree-sleep.ts moved from the renderer into main.

Other

  • The chat teardown policy now stops chats on sleep from main, so the "pending Sleep stops a workspace's native chat agents #28445" exception is gone.
  • Retention reads row keys from main's rows feed. A workspace whose line is paused counts as active, and a confirmed sleep gets no grace period.

Why

  • One executor. With two executors (the window for the desktop, a relay for the phone), a phone sleep failed when no window was open, and the two paths drifted apart. Main already owns the terminals, chats and VM, so running sleep there removes the relay rather than adding a third path.
  • One writer and one executor of wake. The window and main both recorded the same agents, and both could resume them. Two resumes of one conversation means two agents writing one transcript, and a stale window save could undo main's claim on a record. Main records the agents when it stops the terminals and already resumes panes with no window open, so it takes every wake. The minimal alternative, having the window ask main for a claim before it relaunches, would still leave two executors.
  • The window follows a notice instead of being asked. The window only needs to drop its views, and a notice cannot block a sleep. The alternative, keeping the window as the executor and routing phone sleeps to it, is what we had, and it needs an open window.
  • No new "couldn't confirm" UI. We keep today's kept-open toast; retrying is doing the action again.

Linked Issue

Part of the sidebar one-owner design (P3b). Based on main, now that #31553 (P3) has merged. The sleeping glyph and Hide sleeping move onto main's feed in a follow-up PR stacked on this one.

Visual Proof

N/A for new UI: the menu, toasts and copy are unchanged. The disconnected-SSH e2e checks the kept-open toast on screen.

Testing

  • pnpm tc and pnpm typecheck:e2e pass. The headless server build (build:orcad-template) passes. It caught Electron leaking into the server's sleep path, which is fixed here.
  • Unit: every touched area passes, 204 files and 1,587 tests: workspaces, worktree IPC and RPC, runtime environments, VM, sidebar inputs, shared sidebar list, sidebar and store sleep tests, and the web client. New tests cover the sleep order, lost contact versus a definite failure, the Sleep mark fallback, reopen-skips-VM, main's records reaching the window, and the paired-server sleep and its legacy fallback.
  • E2E with the switch on: the sleep, wake and delete specs (24 tests) pass. worktree-active-delete-scroll-position fails, and it also fails on the base.
  • Disconnected SSH (real): a Docker SSH host running the headless server, reached through a proxy the test freezes. The network is cut and the user clicks Sleep in the sidebar. The kept-open toast shows and the window keeps its terminal. After the network returns, the same shell answers, and sleeping again stops every terminal. Passes. Not covered: a paired server reached through the relay.
  • Timing with the switch on (32 workspaces, 10 live; time to the first visible sidebar change). Both runs were on the same loaded machine:
action base this PR
sleep (row hides, Hide sleeping on) 2.1–3.0 s 0.9–1.7 s
wake 237–442 ms 235–366 ms
close last tab 29–45 ms 39–49 ms

This PR still shows no immediate hint on sleep. The follow-up PR hides the row when the sleep is requested.

  • Real Claude agent, switch off, base and this PR run one after the other on one machine, three runs each (median, action to effect; polling makes sleep times accurate to about 1 s and wake times to about 1 s):
action base this PR
sidebar sleep 2.0 s 1.9 s
sidebar open, Claude resumes 3.3 s 3.3 s
phone sleep 1.4 s 0.8 s
phone open, Claude resumes (no window visit) yes yes
sleep then reopen at once 12.2 s 12.0 s
CLI-style sleep / wake 2.7 s / 3.5 s 0.8 s / 3.3 s

An earlier version was slower: main's stop waited a further second after it had already seen each terminal exit. That wait now runs only when no exit was seen. The phone reply arrives in about 25 ms on both.

  • Fix: a resumed Claude now keeps the launch flags it was started with (for example --dangerously-skip-permissions). The base dropped them on resume.
  • agent-descendant-process-kill alternated base and this PR on one machine, 10 runs each: base 10/10, this PR 9/10. The one failure was a detached child that survived the kill. That kill path has no changes in this PR, and 20 more runs of this PR with tracing all passed.

What runs with the switch off:

  • Sidebar Sleep runs in main, and the window follows.
  • A phone or CLI sleep stops chats and suspends the VM in main, with no window needed. A phone gets its reply on acceptance.
  • Wake records come from main only.
  • Wake runs in main, and the window attaches.
  • The chat policy stops chats on sleep.

Only the queue and the queued phone wake are behind the switch.

  • Wake e2e (new, workspace-sleep-main-resume.spec.ts): a slept workspace with two agent panes and a window open. The visible pane resumes once in place, the hidden one only when opened, no new tab appears, and main still holds both records. The same spec fails on the base: the window resumes from its own copy and its save erases main's record.

  • Unit tests cover the window save that omits or clears the field, the headless wake report by record kind, and a slept pane opened twice and after a window reload (one resume, the second open attaches).

  • Wake-related e2e (12 tests) and disconnected SSH pass again after the wake change. The related unit set passes: 71 files and 720 tests.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Not done

  • A pane restored by a crash-recovery reattach (the daemon kept its screen) still types the resume command itself. That path does not follow a sleep.
  • The sleeping glyph and Hide sleeping move onto main's feed in the follow-up PR.

Notes

SSH: a paired server that cannot be reached leaves the sleep unconfirmed; it never counts as stopped. Older paired servers without the new sleep method fall back to the old one. Folder workspaces sleep the same way.

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)

Jinwoo-H and others added 16 commits October 21, 2026 02:48
…lip)

The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.

- Main stops counting a closed tab's terminal once it was asked to stop
  and no pane shows it; a failed or refused stop takes the request back,
  and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
  so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
  read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
  feed stands in while a fresh read is out; it highlights its own
  last-opened workspace and a fold never scrolls; projects file under
  their project group with the desktop's indent; the collapse spinner
  shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.
…d prune pending unread raises

- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.
…stores; detected, view and live-content domains (inert)
…n metadata writes, no phone wait on detected checkouts
…gress hints (switch-gated)

Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one
first-in, first-out queue per host-qualified workspace: strict order, no merging, delete
cancels what has not started and waits for what has, an unconfirmed outcome pauses until
the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the
creations domain is its projection. A finished create or delete keeps its hint until a scan
started after it applies, in the same revision. Adds the paired-server metadata serializer
(saving domain, no caller yet). Replaces the creation registry.
The desktop registers how sleep reaches paired servers; a headless serve has none.

Co-Authored-By: Claude <noreply@anthropic.com>
…spend stub

Co-Authored-By: Claude <noreply@anthropic.com>
- A command issued while paused waits behind the pause; when the owner answers again main
  re-reads terminal liveness and the listing, then carries on. A forced delete runs past it.
- Commands keep their arrival order across a workspace lookup; deletes by name key by the
  resolved id, so they wait for that workspace's running sleep.
- A second delete with different options is refused as already in progress.
- Close and delete read lost contact as sleep does.
- A create always frees its own queue entry, even after a retry took over its id.
- Drops the unused opId, and the paired-server write serializer and its feed section,
  which land with their first caller.

Co-Authored-By: Claude <noreply@anthropic.com>
… sidebar-p3b-main-lifecycle

# Conflicts:
#	src/main/runtime/workspaces/workspace-creation.ts
#	src/main/runtime/workspaces/workspace-lifecycle-commands.ts
#	src/main/runtime/workspaces/workspace-lifecycle-queue.ts
…p classifies only lost contact as unconfirmed

- The window no longer captures wake records for a sleep main runs; it takes main's records
  from main's answer, which its wake and tab-open resume then read.
- A workspace reopened during its sleep keeps its VM running (the window and a phone's
  activation tell main).
- Only lost contact pauses: a definite chat-stop failure or a server's still-live answer is a
  plain failure with today's toast.
- A paired server's chats are asked with the Sleep mark, and again without it when an older
  server refuses the field, instead of reading an unprobed status.
- A phone's worktree.sleep is answered on acceptance and its activation no longer waits on a
  queued wake, so neither can outlast the phone's request timeout. The web client passes
  markChatsAsleep and keeps the receipts.
- One desktop adapter, passed where the desktop builds the runtime, replaces the two module
  slots; a headless server gets none.

Co-Authored-By: Claude <noreply@anthropic.com>
…int fixes

Co-Authored-By: Claude <noreply@anthropic.com>
…the window's copies

The window no longer sleeps a paired server's terminals, so its convergence and order tests for
that path move to main (remote-workspace-terminal-sleep.test.ts) or follow main's answer.

Co-Authored-By: Claude <noreply@anthropic.com>
Jinwoo-H and others added 5 commits October 21, 2026 13:52
Also: split the workspace-wide terminal stops into their own RPC module (line limit), and let a
pending name lookup hold other workspaces' commands only briefly.

Co-Authored-By: Claude <noreply@anthropic.com>
… sidebar-p3b-main-lifecycle

# Conflicts:
#	src/main/runtime/workspaces/managed-workspace-operations.ts
…attaches

- Main claims and starts each slept pane through its windowless resume path, with or
  without a window: the "window present" check is gone.
- A phone's open queues main's wake, which finishes only once the resumes have started,
  and always notes the reopen so a running sleep leaves the VM up.
- The window no longer relaunches a workspace sleep's records in fresh tabs; opening a
  slept pane asks main to resume it and the pane's spawn attaches to main's terminal.
- A window save cannot overwrite main's workspace-sleep records.
- A VM that would not suspend no longer reads as "kept open": the terminals slept, and
  the toast says the machine is still running.
@odmrs odmrs mentioned this pull request Oct 21, 2026
9 tasks done
… sidebar-p3b-main-lifecycle

# Conflicts:
#	src/main/runtime/workspaces/workspace-activation.ts
…cycle

# Conflicts:
#	src/main/runtime/rpc/methods/worktree-sleep-method.ts
#	src/main/runtime/sidebar-inputs/orca-runtime-sidebar-temporal.ts
#	src/main/runtime/sidebar-inputs/sidebar-retention-owner.ts
#	src/main/runtime/workspaces/workspace-creation.ts
#	src/main/runtime/workspaces/workspace-lifecycle-commands.ts
#	src/main/runtime/workspaces/workspace-lifecycle-queue.test.ts
#	src/main/runtime/workspaces/workspace-lifecycle-queue.ts
@Jinwoo-H
Jinwoo-H changed the base branch from sidebar-p3-lifecycle-queue to main October 21, 2026 23:54
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

The pull request moves workspace sleep execution into the main process. It adds shared sleep contracts, IPC routes, lifecycle notifications, remote-host handling, VM suspension, record preservation, and pane-resume support. Renderer code delegates sleep requests to main and follows lifecycle notices. The change also updates PTY shutdown and workspace retention behavior, removes the former renderer-owned sleep protocol and VM-suspension IPC route, and adds unit and end-to-end coverage.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 7fc3b

Sleep failures can leave chats marked asleep, hide that a VM is still running, block lint, and skip resuming some saved agents. These issues should be fixed before merge.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 46.03% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 63 functions across 58 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the main change: workspace sleep now runs in the main process while the sidebar window follows it.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description is complete and closely follows the repository template. It explains the user impact, implementation changes, rationale, linked issue context, visual-proof decision, testi…
Full details: Description check

Explanation

The pull request description is complete and closely follows the repository template. It explains the user impact, implementation changes, rationale, linked issue context, visual-proof decision, testing results, known limitations, compatibility considerations, and checklist status. The description does not use an explicit Fixes #... line, but it provides relevant issue references and is sufficiently complete.

✨ Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

🧹 Nitpick comments: 1
tests/e2e/workspace-sleep-in-main.spec.ts: 1

95-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Check the visible result in the page, not only the store and runtime counts.

readSleepState reads window.__store and terminal.list and compares their counts. These checks prove the host stopped its terminals. They do not prove the window shows the slept workspace correctly. A broken follower that hides nothing, or that leaves a dead pane mounted, still passes. Keep the host liveness check. Also check the page itself, for example the slept row's state in the sidebar or that no terminal pane for second is mounted, using getByRole, toBeVisible, or toHaveCount.

As per coding guidelines: "Use window.__store for setup; assert user-visible behavior through the DOM (getByRole, toBeVisible, toHaveText). Writing state and reading it back cannot catch broken rendering."

Also applies to: 119-119

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6f6f8af9-1e65-480f-a430-2c6dc4c6f686
📥 Commits

Reviewing files that changed from the base of the PR and between d15677e and e2f9a70.

⛔ Files ignored due to path filters: 1
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing: 129
  • src/main/ephemeral-vm-workspace-suspend.ts
  • src/main/ipc/ephemeral-vm-managed-ssh.test.ts
  • src/main/ipc/ephemeral-vm-runtime-handlers.ts
  • src/main/ipc/ephemeral-vm.test.ts
  • src/main/ipc/pty-launch-parity.test-fixture.ts
  • src/main/ipc/session.ts
  • src/main/ipc/worktrees-create-metadata-persistence.test.ts
  • src/main/ipc/worktrees.ts
  • src/main/ipc/worktrees/sleep/register-worktree-sleep-handlers.ts
  • src/main/persistence/window-save-sleep-records.test.ts
  • src/main/persistence/window-save-sleep-records.ts
  • src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
  • src/main/runtime/construction/runtime-construction-dependencies.ts
  • src/main/runtime/construction/runtime-construction.ts
  • src/main/runtime/fit-override-integration.test.ts
  • src/main/runtime/mobile-presence-lock.test.ts
  • src/main/runtime/mobile-subscribe-integration.spec.ts
  • src/main/runtime/multi-client-navigation-isolation.integration.test.ts
  • src/main/runtime/orca-runtime-default-tab-agent-status.test.ts
  • src/main/runtime/orca-runtime-layout-owner-start-fixture.ts
  • src/main/runtime/orca-runtime-terminal-cwd.test.ts
  • src/main/runtime/orca-runtime-terminal-rename-retention.test.ts
  • src/main/runtime/orca-runtime-test-scenario-builders.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-headless-wake-report.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-02.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-03.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-07.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-09.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-12.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-13.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-04.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-05.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation-part-04.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-02.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-03.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-04.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-single-creator.spec.ts
  • src/main/runtime/owned-session-teardown-policy.test.ts
  • src/main/runtime/owned-session-teardown-policy.ts
  • src/main/runtime/paired-host-terminal-close.test.ts
  • src/main/runtime/qoder-mobile-resume-adoption.test.ts
  • src/main/runtime/remote-desktop-driver.test.ts
  • src/main/runtime/rpc/methods/worktree-sleep-method.ts
  • src/main/runtime/runtime-created-worktree-terminal-target.test.ts
  • src/main/runtime/runtime-notifier-contract.ts
  • src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts
  • src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts
  • src/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.ts
  • src/main/runtime/sidebar-inputs/orca-runtime-sidebar-temporal.ts
  • src/main/runtime/sidebar-inputs/sidebar-retention-owner.test.ts
  • src/main/runtime/sidebar-inputs/sidebar-retention-owner.ts
  • src/main/runtime/workspaces/managed-workspace-operations.ts
  • src/main/runtime/workspaces/managed-worktree-sleep-layout-owner.test.ts
  • src/main/runtime/workspaces/remote-workspace-terminal-sleep.test.ts
  • src/main/runtime/workspaces/remote-workspace-terminal-sleep.ts
  • src/main/runtime/workspaces/workspace-activation.ts
  • src/main/runtime/workspaces/workspace-creation.ts
  • src/main/runtime/workspaces/workspace-lifecycle-commands.ts
  • src/main/runtime/workspaces/workspace-lifecycle-queue.test.ts
  • src/main/runtime/workspaces/workspace-lifecycle-queue.ts
  • src/main/runtime/workspaces/workspace-sleep-execution.test.ts
  • src/main/runtime/workspaces/workspace-sleep-execution.ts
  • src/main/runtime/workspaces/workspace-sleep-own-worktree.test.ts
  • src/main/runtime/workspaces/workspace-sleep-platform.ts
  • src/main/runtime/workspaces/workspace-sleep-runtime.ts
  • src/main/startup/main-process-runtime-service.ts
  • src/main/window/runtime-window-lifecycle.ts
  • src/preload/api/ephemeral-vm-api.ts
  • src/preload/api/ephemeral-vm-bridge.ts
  • src/preload/api/ui-bridge-clipboard-and-window-controls.ts
  • src/preload/api/ui-command-event-api.ts
  • src/preload/api/worktree-api.ts
  • src/preload/api/worktrees-bridge.ts
  • src/renderer/src/components/sidebar/sleep-workspace-chat-stop.ts
  • src/renderer/src/components/sidebar/sleep-worktree-activation-race.test.ts
  • src/renderer/src/components/sidebar/sleep-worktree-admission.test.ts
  • src/renderer/src/components/sidebar/sleep-worktree-flow.test.ts
  • src/renderer/src/components/sidebar/sleep-worktree-flow.ts
  • src/renderer/src/components/sidebar/sleep-worktree-test-main.ts
  • src/renderer/src/components/sidebar/workspace-sleep-follower.ts
  • src/renderer/src/components/sidebar/workspace-sleep-target.ts
  • src/renderer/src/components/sidebar/worktree-list/listing/use-sleeping-sweep-retention.main-parity.test.tsx
  • src/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.ts
  • src/renderer/src/components/terminal-pane/pty-connection/main-resumed-sleep-pane.ts
  • src/renderer/src/hooks/ipc-events-agent-status-window-test-fixtures.ts
  • src/renderer/src/hooks/ipc-events-close-routing-test-harness.ts
  • src/renderer/src/hooks/ipc-events-terminal-create-window-test-fixtures.ts
  • src/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge-reveal.test.ts
  • src/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge.test.ts
  • src/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge.ts
  • src/renderer/src/hooks/useIpcEvents-browser-tab-create.test.ts
  • src/renderer/src/hooks/useIpcEvents-cli-worktree-activation.test.ts
  • src/renderer/src/hooks/useIpcEvents-close-routing-browser-pages.test.ts
  • src/renderer/src/hooks/useIpcEvents-ssh-disconnect-cleanup.test.ts
  • src/renderer/src/hooks/useIpcEvents-updater-status.test.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/lib/pi-session-resume-wake.test.ts
  • src/renderer/src/lib/resume-sleeping-agent-session-live-ssh-dedupe.test.ts
  • src/renderer/src/lib/resume-sleeping-agent-session-provider-claim.test.ts
  • src/renderer/src/lib/resume-sleeping-agent-session.test.ts
  • src/renderer/src/lib/resume-sleeping-agent-session.ts
  • src/renderer/src/lib/settled-worker-wake-policy.test.ts
  • src/renderer/src/lib/wake-sleeping-agents-in-background.ts
  • src/renderer/src/store/host-twin-isolation.test.ts
  • src/renderer/src/store/slices/agent-status-manual-sleep-capture.test.ts
  • src/renderer/src/store/slices/store-sleep-runtime-convergence.test.ts
  • src/renderer/src/store/slices/store-sleep-shutdown-order.test.ts
  • src/renderer/src/store/slices/store-sleep-twin-host-owner.test.ts
  • src/renderer/src/store/terminals/terminal-actions.ts
  • src/renderer/src/store/terminals/terminal-shutdown.ts
  • src/renderer/src/store/terminals/terminal-sleep-layout-owner.ts
  • src/renderer/src/web/preload-api/web-ui-api.ts
  • src/renderer/src/web/preload-api/web-workspace-sleep-notices.ts
  • src/renderer/src/web/preload-api/web-worktrees-api.ts
  • src/shared/rpc-contract/worktree-params.ts
  • src/shared/workspace-sleep.ts
  • src/shared/worktree-sleep-request.ts
  • tests/e2e/ssh-orcad-sleep-disconnected.spec.ts
  • tests/e2e/workspace-sleep-in-main.spec.ts
  • tests/e2e/workspace-sleep-main-resume.spec.ts
  • tests/e2e/worktree-lineage.spec.ts
💤 Files with no reviewable changes: 41
  • src/renderer/src/components/sidebar/sleep-worktree-admission.test.ts
  • src/shared/worktree-sleep-request.ts
  • src/main/ipc/pty-launch-parity.test-fixture.ts
  • src/preload/api/ephemeral-vm-bridge.ts
  • tests/e2e/worktree-lineage.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts
  • src/main/runtime/mobile-subscribe-integration.spec.ts
  • src/main/runtime/mobile-presence-lock.test.ts
  • src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-single-creator.spec.ts
  • src/preload/api/ephemeral-vm-api.ts
  • src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation.spec.ts
  • src/main/runtime/multi-client-navigation-isolation.integration.test.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-03.spec.ts
  • src/main/runtime/qoder-mobile-resume-adoption.test.ts
  • src/main/runtime/paired-host-terminal-close.test.ts
  • src/renderer/src/components/sidebar/sleep-workspace-chat-stop.ts
  • src/main/runtime/fit-override-integration.test.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-05.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-07.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts
  • src/main/runtime/remote-desktop-driver.test.ts
  • src/main/runtime/orca-runtime-terminal-rename-retention.test.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-13.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-12.spec.ts
  • src/main/runtime/orca-runtime-default-tab-agent-status.test.ts
  • src/main/runtime/orca-runtime-layout-owner-start-fixture.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-09.spec.ts
  • src/main/runtime/orca-runtime-terminal-cwd.test.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-04.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation-part-04.spec.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts
  • src/renderer/src/store/slices/store-sleep-twin-host-owner.test.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-04.spec.ts
  • src/main/runtime/orca-runtime-test-scenario-builders.spec.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-02.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-02.spec.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-03.spec.ts
  • src/main/runtime/workspaces/workspace-creation.ts

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

Comment on lines +207 to +209
if (teardownStops('sleep', 'structured-chat')) {
chats = await stopChats(deps, target, markAsleep)
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

set -e
printf '%s\n' '--- target function and surrounding source ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-execution.ts | sed -n '1,280p'
printf '%s\n' '--- failure and outcome references ---'
rg -n -F --glob '*.{ts,tsx}' -e 'WorkspaceSleepOutcome' -e 'outcome.failure' -e 'sleepWorkspaces(' -e 'stopChats(' src/main src | head -240
printf '%s\n' '--- relevant workspace sleep files ---'
rg --files src/main src | rg 'workspace-(sleep|wake)|sleep.*workspace|workspace.*sleep'

Repository: stablyai/orca

Length of output: 27904


🏁 Script executed:

set -e
printf '%s\n' '--- runtime and shared outcome ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-runtime.ts | sed -n '1,135p'
nl -ba src/shared/workspace-sleep.ts | sed -n '1,90p'
printf '%s\n' '--- renderer outcome handling ---'
nl -ba src/renderer/src/components/sidebar/sleep-worktree-flow.ts | sed -n '70,205p'
printf '%s\n' '--- execution tests around terminal failure and chat stopping ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-execution.test.ts | sed -n '1,235p'

Repository: stablyai/orca

Length of output: 27510


Skip chat shutdown when terminal sleep fails.

failure marks the workspace as kept open, but stopChats still runs and can mark chats asleep. Guard the chat stop with !failure so an unsuccessful workspace sleep does not stop its chats.

🐛 Suggested fix
--- "a/src/main/runtime/workspaces/workspace-sleep-execution.ts"
+++ "b/src/main/runtime/workspaces/workspace-sleep-execution.ts"
@@ -204,9 +204,9 @@
           ? error.stoppedPtyIds.filter((id): id is string => typeof id === 'string')
           : null
     }
-    if (teardownStops('sleep', 'structured-chat')) {
+    if (!failure && teardownStops('sleep', 'structured-chat')) {
       chats = await stopChats(deps, target, markAsleep)
     }
   } finally {
     batch.chatsSettled()
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (teardownStops('sleep', 'structured-chat')) {
chats = await stopChats(deps, target, markAsleep)
}
if (!failure && teardownStops('sleep', 'structured-chat')) {
chats = await stopChats(deps, target, markAsleep)
}

Comment on lines +178 to +183
ended(true)
return {
worktreeId,
chatFailed: reply?.chatFailed === true,
receipts: Array.isArray(reply?.receipts) ? reply.receipts : []
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass vmSuspendFailure through from the host's reply.

A web client is not mobile, so worktree.sleep waits for the whole sleep. It returns sleepOwnWorktree's full outcome, and that outcome includes vmSuspendFailure. This mapping keeps only chatFailed and receipts. sleepWorktreesReportingFailures therefore never adds the workspace to vmStillRunningIds. A web user who sleeps a VM workspace whose suspend failed gets no "machine is still running" warning. The VM keeps running without the user knowing.

🐛 Proposed fix
--- "a/src/renderer/src/web/preload-api/web-worktrees-api.ts"
+++ "b/src/renderer/src/web/preload-api/web-worktrees-api.ts"
@@ -175,12 +175,15 @@
               // Why: the host stops the workspace's chats too, which can take up to two minutes.
               WEB_WORKSPACE_SLEEP_TIMEOUT_MS
             )
             ended(true)
             return {
               worktreeId,
               chatFailed: reply?.chatFailed === true,
-              receipts: Array.isArray(reply?.receipts) ? reply.receipts : []
+              receipts: Array.isArray(reply?.receipts) ? reply.receipts : [],
+              ...(typeof reply?.vmSuspendFailure === 'string'
+                ? { vmSuspendFailure: reply.vmSuspendFailure }
+                : {})
             }
           } catch (error) {
             ended(false)
             const failure = error instanceof Error ? error.message : String(error)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ended(true)
return {
worktreeId,
chatFailed: reply?.chatFailed === true,
receipts: Array.isArray(reply?.receipts) ? reply.receipts : []
}
ended(true)
return {
worktreeId,
chatFailed: reply?.chatFailed === true,
receipts: Array.isArray(reply?.receipts) ? reply.receipts : [],
...(typeof reply?.vmSuspendFailure === 'string'
? { vmSuspendFailure: reply.vmSuspendFailure }
: {})
}

… a slept workspace's mounted agents on phone open

A workspace sleep stopped each terminal through main's stop-and-verify, which polled for a
further second after the exit it had already seen. And a phone opening a slept workspace no
longer reached the window's mounted panes of agents that had finished before the sleep, so
they stayed asleep until the window opened their tab.

@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: 19eabcdd-6c0f-49be-bec1-6d0cdeb1b575
📥 Commits

Reviewing files that changed from the base of the PR and between e2f9a70 and 7dd8972.

📒 Files selected for processing: 5
  • src/main/ipc/pty-daemon-controller-teardown.test.ts
  • src/main/ipc/pty/provider/liveness.ts
  • src/main/ipc/pty/runtime/kill.ts
  • src/renderer/src/lib/wake-sleeping-agents-in-background.test.ts
  • src/renderer/src/lib/wake-sleeping-agents-in-background.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.

Comment on lines 300 to 306
}
}
try {
if (!(await verifyPtyStopped(provider, ptyId, opts))) {
if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) {
runtime?.markPtyLivenessLive?.(ptyId)
return false
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '230,325p' src/main/ipc/pty/runtime/kill.ts
sed -n '45,115p' src/main/ipc/pty/provider/liveness.ts

Repository: stablyai/orca

Length of output: 7000


🏁 Script executed:

set +e
printf '%s\n' '--- definitions and references ---'
rg -n -F --glob 'src/**/*.ts' -e 'shutdownProviderAndDetectExit' -e 'PtyExitProof' -e 'providerExitObserved' -e 'verifyPtyStopped' -e 'KEEP_HISTORY_STOP' src
printf '%s\n' '--- changed hunk and helper context ---'
git diff 154307498b7757f718ce7b914aea8eab365597f8 7dd89729e0a5801ec4f7ceddb25ebda3f0eec9c1 -- src/main/ipc/pty/runtime/kill.ts
printf '%s\n' '--- kill.ts top-level context ---'
sed -n '1,240p' src/main/ipc/pty/runtime/kill.ts
printf '%s\n' '--- tests mentioning stop/exit/keepHistory ---'
rg -n -F --glob 'src/**/*.test.ts' --glob 'src/**/*.spec.ts' --glob 'test/**/*.ts' --glob 'tests/**/*.ts' -e 'stopAndWaitPty' -e 'verifyPtyStopped' -e 'keepHistory' -e 'exit-observed' -e 'incarnationId' .
exit 0

Repository: stablyai/orca

Length of output: 41386


🏁 Script executed:

printf '%s\n' '--- shutdown producer ---'
nl -ba src/main/ipc/pty/provider/shutdown-detect.ts
printf '%s\n' '--- provider contract and exit listener bindings ---'
rg -n -F --glob 'src/main/ipc/pty/**/*.ts' -e 'interface IPtyProvider' -e 'type IPtyProvider' -e 'onExit' -e 'listProcesses' -e 'shutdown(' -e 'kill(' -e 'waitForPtyExitRecord'
printf '%s\n' '--- provider type/implementation files ---'
rg --files src/main/ipc/pty/provider src/main/ipc/pty | rg '(provider|pty-provider|bind-listeners|local-configure|session|controller)'
printf '%s\n' '--- focused respawn and teardown tests ---'
nl -ba src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts
nl -ba src/main/ipc/pty-daemon-controller-teardown.test.ts | sed -n '1,230p'
nl -ba src/main/ipc/pty-daemon-controller-teardown.test.ts | sed -n '240,350p'
printf '%s\n' '--- exact PR diff for relevant files ---'
git diff --unified=80 154307498b7757f718ce7b914aea8eab365597f8 7dd89729e0a5801ec4f7ceddb25ebda3f0eec9c1 -- src/main/ipc/pty/provider/shutdown-detect.ts src/main/ipc/pty/provider/liveness.ts src/main/ipc/pty/runtime/kill.ts src/main/ipc/pty-daemon-controller-teardown.test.ts src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts

Repository: stablyai/orca

Length of output: 41933


🏁 Script executed:

printf '%s\n' '--- ownership state and incarnation lifecycle ---'
nl -ba src/main/ipc/pty/provider/ownership-state.ts
rg -n -F --glob 'src/**/*.ts' -e 'ptyIncarnationById.set' -e 'ptyIncarnationById.delete' -e 'ptyIncarnationById.get' -e 'registerPty(' -e 'onPtySpawned' src/main/ipc/pty src/main/runtime | head -240
printf '%s\n' '--- provider contract and event payload ---'
nl -ba src/providers/pty-provider-contract.ts
nl -ba src/main/ipc/pty/provider/bind-listeners.ts | sed -n '70,135p'
nl -ba src/main/ipc/pty/provider/local-configure.ts | sed -n '115,150p'
printf '%s\n' '--- stop entrypoints and no-incarnation tests ---'
rg -n -F --glob 'src/**/*.ts' -e 'stopAndWaitPtyFromRuntimeController' -e 'stopAndWait(' -e 'stopAndWaitPty(' -e 'incarnationId: undefined' -e 'incarnationId: null' src/main/ipc/pty src/main/runtime | head -300
printf '%s\n' '--- exact relevant test blocks ---'
rg -n -F --glob 'src/main/**/*.test.ts' -e 'uses inventory, not an incarnation-less exit' -e 'skips the keep-history settle poll' -e 'incarnation-less exit' -e 'stopAndWait' src/main/ipc/pty-daemon-controller-teardown.test.ts src/main/ipc/pty/runtime src/main/ipc/pty | head -120

Repository: stablyai/orca

Length of output: 41857


🏁 Script executed:

printf '%s\n' '--- locate provider contract and cleanup ---'
rg --files . | rg 'pty-provider-contract|state-cleanup|pty-provider'
nl -ba src/main/ipc/pty/provider/state-cleanup.ts
printf '%s\n' '--- registerPty implementation and spawn event ordering ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(' -e 'onPtySpawned(' -e 'ptyController.stopAndWait' -e 'stopAndWait(' | head -180
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty' -e 'onPtySpawned' | rg 'function|registerPty|onPtySpawned' | head -120
printf '%s\n' '--- stop workflows around sleep and terminal stop ---'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '130,190p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-stop.ts | sed -n '190,235p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-stop-targets.ts | sed -n '40,80p'
printf '%s\n' '--- runtime registration and spawn lifecycle definitions ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(id' -e 'registerPty(' -e 'onPtySpawned(id' -e 'onPtySpawned(' | head -160

Repository: stablyai/orca

Length of output: 41749


🏁 Script executed:

printf '%s\n' '--- runtime registration owner ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'class OrcaRuntimeService' -e 'registerPty(' -e 'onPtySpawned(' -e 'ptyIncarnation' | head -220
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(' | rg 'pty-registration|pty-lifecycle|runtime-service|orca-runtime' | head -80
printf '%s\n' '--- launch flows around registration and spawn ---'
nl -ba src/main/runtime/terminal/launch/terminal-create.ts | sed -n '180,255p'
nl -ba src/main/runtime/terminal/launch/terminal-split-surface.ts | sed -n '80,130p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '140,182p'
printf '%s\n' '--- lifecycle implementation files ---'
rg --files src/main/runtime | rg 'pty-registration|pty-lifecycle|runtime-service|orca-runtime'

Repository: stablyai/orca

Length of output: 41826


🏁 Script executed:

printf '%s\n' '--- pty registration implementation ---'
nl -ba src/main/runtime/terminal/lifecycle/pty-registration.ts
printf '%s\n' '--- sleep live-record construction ---'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '95,175p'
printf '%s\n' '--- normal launch registration exact context ---'
nl -ba src/main/runtime/terminal/launch/terminal-create.ts | sed -n '210,245p'
nl -ba src/main/runtime/terminal/launch/terminal-split-in-main.ts | sed -n '105,140p'
printf '%s\n' '--- restore/reattach registration exact context ---'
nl -ba src/main/runtime/terminal/identity/pty-worktree-record.ts | sed -n '105,145p'
nl -ba src/main/runtime/terminal/lifecycle/pty-handle-invalidation.ts | sed -n '110,175p'

Repository: stablyai/orca

Length of output: 24466


Require an incarnation match before skipping keep-history settlement.

When ptyIncarnationById has no entry, shutdownProviderAndDetectExit accepts any exit with the target ID. stopAndWaitPty then skips the keepHistory settle poll after one absent inventory result. A late exit from an older same-ID PTY can therefore confirm the stop before the current incarnation settles. A different PTY ID cannot trigger this path because the producer checks payload.id === id.

Keep the immediate path for exits that match a captured incarnation. Use the settle poll when no incarnation was captured.

Suggested fix
   }
   try {
-    if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) {
+    const exitObservedForCurrentIncarnation =
+      stoppingIncarnationId !== undefined && providerExitObserved
+    if (!(await verifyPtyStopped(provider, ptyId, opts, exitObservedForCurrentIncarnation))) {
       runtime?.markPtyLivenessLive?.(ptyId)
       return false
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}
}
try {
if (!(await verifyPtyStopped(provider, ptyId, opts))) {
if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) {
runtime?.markPtyLivenessLive?.(ptyId)
return false
}
}
}
try {
const exitObservedForCurrentIncarnation =
stoppingIncarnationId !== undefined && providerExitObserved
if (!(await verifyPtyStopped(provider, ptyId, opts, exitObservedForCurrentIncarnation))) {
runtime?.markPtyLivenessLive?.(ptyId)
return false
}

…cycle

# Conflicts:
#	src/main/ipc/ephemeral-vm-runtime-handlers.ts
#	src/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.ts
#	src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts
#	src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts
#	src/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.ts

@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: 26c8355a-731c-45cb-9802-0836bc74736d
📥 Commits

Reviewing files that changed from the base of the PR and between be2c58a and 41d8c0b.

⛔ Files ignored due to path filters: 1
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing: 13
  • src/main/ephemeral-vm/workspace-suspend.ts
  • src/main/ipc/ephemeral-vm-managed-ssh.test.ts
  • src/main/ipc/ephemeral-vm-runtime-handlers.ts
  • src/main/ipc/ephemeral-vm.test.ts
  • src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.ts
  • src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts
  • src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts
  • src/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.test.ts
  • src/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.ts
  • src/main/startup/main-process-runtime-service.ts
  • src/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.ts
  • src/renderer/src/i18n/locales/en.json
💤 Files with no reviewable changes: 1
  • src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
🚧 Files skipped from review as they are similar to previous changes: 1
  • src/renderer/src/i18n/locales/en.json

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

Comment thread src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts Outdated

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

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Resolve numeric pane keys during workspace wake. · restored-pane-materialize.ts:112-128

src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts:112-128
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Resolve numeric pane keys during workspace wake.

When a legacy numeric paneKey is present, resumeSleptWorkspaceAgents selects the record but parsePaneKey rejects it. The flatMap then drops the record, so its agent does not resume.

Use findPaneSleepingRecord for the runtime panes. That existing path resolves numeric aliases and prevents one record from being assigned to multiple split panes.

Suggested fix
-import { parsePaneKey } from '../../../../shared/stable-pane-id'
+import { findPaneSleepingRecord } from './pane-sleeping-record'
...
   async resumeSleptWorkspaceAgents(worktreeId: string): Promise<void> {
-    const records = Object.values(
-      this.getWorkspaceSessionForWorktree(worktreeId)?.sleepingAgentSessionsByPaneKey ?? {}
-    ).filter(
+    const session = this.getWorkspaceSessionForWorktree(worktreeId)
+    if (!session) return
+    const records = new Set(
+      Object.values(session.sleepingAgentSessionsByPaneKey ?? {}).filter(
       (record) =>
         worktreeIdsEqual(record.worktreeId, worktreeId) && isUnclaimedRunningSleepRecord(record)
+      )
     )
-    const panes = records.flatMap((record) => {
-      const pane = parsePaneKey(record.paneKey)
-      return pane ? [{ worktreeId, tabId: pane.tabId, leafId: pane.leafId }] : []
-    })
+    const panes = buildHeadlessMobileSessionTerminalTabs(
+      worktreeId,
+      session.tabsByWorktree?.[worktreeId] ?? [],
+      session
+    ).flatMap(({ parentTabId, leafId }) => {
+      const found = findPaneSleepingRecord(session, worktreeId, { parentTabId, leafId })
+      return found && records.has(found.record)
+        ? [{ worktreeId, tabId: parentTabId, leafId }]
+        : []
+    })

ℹ️ Review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a1de15c8-0d95-4415-84c3-e876ca6356a3
📥 Commits

Reviewing files that changed from the base of the PR and between 41d8c0b and 7fc3b2d.

📒 Files selected for processing: 1
  • src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts

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

…op sleep adapter, and open the workspace after its sleep starts
…cycle

# Conflicts:
#	src/main/runtime/owned-session-teardown-policy.test.ts
#	src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts
@Jinwoo-H
Jinwoo-H merged commit 41661e3 into main Oct 22, 2026
61 checks passed
Jinwoo-H added a commit that referenced this pull request Oct 23, 2026
…and main typechecks again (#32168)

#32018 pushed headless-terminal-tab-close.ts over the 300-line lint limit,
and its new test passed sleepWorktree to RuntimeNotifier after #31564 removed
it. setMobileSessionTabProps moves unchanged into RuntimeHeadlessTabProperties,
which already owns the helpers it calls and directly extends the close class.
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.

1 participant