You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(wsl): hold a passive WSL restore before the cwd probe, whichever provider spawns - #35895
Flag-off: applies with the flag on or off, to WSL bridge panes restored without a user act (#35813's wslPassiveRestore). User-risk: Low. A restored WSL pane on a stopped distro now waits with Start in every daemon state: healthy, degraded or off. Before, a degraded or off daemon, or the startup cwd probe, could start the distro. A pane the user opens is not held.
ELI5
When Orca restores a terminal you didn't ask for (launch restore, a reload, a remount), it must not start a stopped WSL distro. #35813 added that check, but in the wrong layer. If the terminal daemon was off or unhealthy, Orca spawned the shell itself and skipped the check. Even with the daemon healthy, an earlier step that checks the terminal's folder ran a command inside the distro first. The check now runs before both, so it holds in every case.
What Changed
Before:holdPassiveRestoreOfStoppedWslDistro ran only inside the daemon adapter's spawn (daemon-pty-session-spawn.ts).
With the daemon off or degraded, LocalPtyProvider and the fresh-spawn fallback never read wslPassiveRestore, so a restore booted the distro.
With the daemon healthy, resolvePtySpawnStartupTarget had already run the startup cwd probe (wslUncDirectoryExistsAsync, a wsl.exe -d <distro> --exec test) for a POSIX restore cwd. That too could boot it.
After: the hold runs in the shared resolvePtySpawnStartupTarget (spawn-startup-target.ts), after the runtime options are known and before the cwd probe and any provider spawn.
The IPC preflight passes passiveWslRestore: { sessionId, isNewSession, attachOnly } only when the renderer marked the request wslPassiveRestore.
The distro comes from the same resolveWslSessionContext the adapter uses.
The adapter's own hold stays as a second check.
It reuses holdPassiveRestoreOfStoppedWslDistro unchanged: a settled running read, where a stopped or unanswered distro holds with the existing "WSL distro … is stopped" message.
Why
Hold at the shared step every provider passes through, rather than in each provider. A new or fallback provider can't miss it, and it also covers the cwd probe, which runs before any provider is chosen.
Keep the adapter's check. It's cheap and still guards any direct adapter caller.
Linked Issue
Internal WSL tracker, E9. e9-reviewer found this gap on #35865; it already existed for #35813's wslPassiveRestore. wslBackgroundStart from #35865 will get the same hold once #35865 merges.
Visual Proof
N/A: no new UI. A held pane shows #35813's existing Start card.
Testing
New pty-spawn-wsl-passive-restore-hold.test.ts, through the real pty:spawn IPC with a mocked running read. The first two cases fail without the change.
With the default local provider (daemon off) and a stopped distro: the restore is refused, the cwd probe never runs, and node-pty never spawns.
With the daemon provider: the restore is refused before its cwd probe and before the provider spawns.
Once the distro runs, the restore spawns. A request the user made (no wslPassiveRestore) is never held.
Also run:
the pty and daemon suites: 4,607 pass;
the load rule: pnpm tc, typecheck:ci, check:code-quality:changed, the anti-slop lint and the ast-grep gates.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.
Notes
Platforms: Windows only in effect. Off Windows, no session resolves a WSL distro.
Wire: no wire change. wslPassiveRestore is an existing IPC field.
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)
…provider spawns
The passive-restore hold lived in the daemon adapter, so a degraded or
off daemon (LocalPtyProvider) never applied it, and even with the daemon
the startup cwd probe (wsl.exe --exec test -d) ran first and could boot
the distro. The hold now runs in the shared startup-target resolution,
before that probe and before any provider spawns (E9).
Moves the passive WSL restore hold into shared startup preparation, before the cwd probe and provider dispatch.
Passive WSL restores check the distro before startup continues.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Passive WSL restore] --> B[Shared startup preparation]
B --> C{Attach-only request?}
C -->|No| D[Read distro running state]
D -->|Stopped or unanswered| E[Hold pane for Start]
D -->|Running| F[Check startup cwd]
C -->|Yes: hold skipped| F
F --> G[Provider spawn or attach]
The reason will be displayed to describe this comment to others. Learn more.
Passive attach starts WSL
Passing attachOnly: true makes holdPassiveRestoreOfStoppedWslDistro return without checking the distro. But this preflight still checks the cwd before attempting the attach. When no pane owner is recorded and a passive restore has a POSIX cwd in WSL, that check runs wsl.exe -d <distro> --exec and starts the stopped distro without the user pressing Start.
Either skip the cwd probe for attach-only requests or keep the shared hold active before it. The adapter's attach-only exemption is safe only after these earlier checks.
When wslPassiveRestore is explicitly enabled, preflight passes passive-restore session options to startup-target resolution. For a non-pre-adopted PTY, the resolver awaits the stopped-distro hold before startup-cwd resolution. Tests cover stopped distros with local and daemon providers, a running distro, and a restore without the passive-restore flag.
Priority: Normal
Merge Risk: Moderate · up to 5c041
The change adds a hold so that restoring a session does not boot a stopped WSL distro, but several reachable paths still bypass it or start the distro before it runs. The effect is limited to Windows users with WSL and causes unwanted distro startups rather than data loss. Address these gaps, or explicitly accept them, before merging.
Pre-merge checks: 4 · 1
Failed checks: 1 warning
Check name
Status
Explanation
Resolution
Docstring Coverage
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks: 4 passed
Check name
Status
Explanation
Title check
The title names the main change: a passive WSL restore hold now runs before the cwd probe, regardless of which provider spawns the PTY. It matches the raw summary and the objectives, which both descri…
Description check
The description follows the template's main sections: ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist. It expla…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title names the main change: a passive WSL restore hold now runs before the cwd probe, regardless of which provider spawns the PTY. It matches the raw summary and the objectives, which both describe moving the hold into shared startup-target resolution. The title is long (84 characters), but it stays specific and on topic.
Full details: Description check
Explanation
The description follows the template's main sections: ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist. It explains the before and after behavior, the mechanism, the reason for the change, and the test coverage. The Linked Issue section is weak. It cites an internal tracker ('Internal WSL tracker, E9') and has no 'Fixes #' issue link, which the template says outside contributors must include. The Visual Proof section is N/A with a reason. The Testing section lists automated tests but leaves the manual-test checkbox unchecked. The pnpm checklist item is also unchecked.
Resolution
Consider these edits: replace the internal tracker reference in Linked Issue with a real GitHub issue number (Fixes #...) or confirm that the maintainer exception applies. Fill in the AI Disclosure section if an AI model helped write the change. Clarify whether pnpm lint, typecheck, test, and build passed, since only a subset of the gates is listed.
Full details: Docstring Coverage
Explanation
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 3 files.
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Finishing Touches: 💡 1
📝 Generate docstrings: 💡
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.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
⚠️ Outside diff range comments (1)
Major · Run the hold before the folder workspace path check. · spawn-preflight.ts:34-38
src/main/ipc/pty/ipc/spawn-preflight.ts:34-38 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Run the hold before the folder workspace path check.
For a folder workspace on \\wsl.localhost, this admission check calls stat on the workspace path before the new hold runs. Accessing that path can start the stopped distro, so the later hold can observe it as running. Check the distro before this filesystem access. (raw.githubusercontent.com)
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: 2d59ba25-c672-4ad0-aab3-1987e5192a2a
Commits
Reviewing files that changed from the base of the PR and between 84a4995 and 5c0410b.
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
The reason will be displayed to describe this comment to others. Learn more.
Major · Functional Correctness · Heavy lift
Do not exempt attach-only requests from the cwd probe hold.
The helper skips the hold when this flag is true, but preflight still runs the cwd probe before attach-only dispatch. That probe can start a stopped distro. Also, attachOnlyViewOf ignores the flag when main-owned layout is disabled, so the request can proceed to a normal spawn. Skip cwd probing for an effective attach-only request and retain the hold otherwise. (raw.githubusercontent.com)
Do not skip the passive hold for sessionless or new-session requests.
holdPassiveRestoreOfStoppedWslDistro returns before reading the distro when sessionId is absent or isNewSession is true. The IPC contract permits wslPassiveRestore: true without sessionId, so local requests can reach the cwd probe without the hold. Daemon requests without an ID receive a minted ID but still skip through isNewSession.
For a WSL UNC cwd, the probe invokes wsl.exe for the distro. That command can start a stopped distro.
The current renderer reattach producer always supplies a session ID. The IPC boundary does not enforce that invariant, so the helper should apply the passive hold based on the flag and distro, not session ownership.
The reason will be displayed to describe this comment to others. Learn more.
Major · Functional Correctness · Heavy lift
Resolve the probe’s distro before applying the hold.
For a WSL worktree with a POSIX cwd and a local numeric session ID, this lookup can return no distro. resolveStartupCwdWithFallback instead derives the distro from the workspace path and runs wsl.exe to probe it. A stopped distro can therefore start before the hold. Resolve the workspace-derived distro before the probe and check that distro. (raw.githubusercontent.com)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Flag-off: applies with the flag on or off, to WSL bridge panes restored without a user act (#35813's
wslPassiveRestore).User-risk: Low. A restored WSL pane on a stopped distro now waits with Start in every daemon state: healthy, degraded or off. Before, a degraded or off daemon, or the startup cwd probe, could start the distro. A pane the user opens is not held.
ELI5
When Orca restores a terminal you didn't ask for (launch restore, a reload, a remount), it must not start a stopped WSL distro. #35813 added that check, but in the wrong layer. If the terminal daemon was off or unhealthy, Orca spawned the shell itself and skipped the check. Even with the daemon healthy, an earlier step that checks the terminal's folder ran a command inside the distro first. The check now runs before both, so it holds in every case.
What Changed
holdPassiveRestoreOfStoppedWslDistroran only inside the daemon adapter's spawn (daemon-pty-session-spawn.ts).LocalPtyProviderand the fresh-spawn fallback never readwslPassiveRestore, so a restore booted the distro.resolvePtySpawnStartupTargethad already run the startup cwd probe (wslUncDirectoryExistsAsync, awsl.exe -d <distro> --exectest) for a POSIX restore cwd. That too could boot it.resolvePtySpawnStartupTarget(spawn-startup-target.ts), after the runtime options are known and before the cwd probe and any provider spawn.passiveWslRestore: { sessionId, isNewSession, attachOnly }only when the renderer marked the requestwslPassiveRestore.resolveWslSessionContextthe adapter uses.holdPassiveRestoreOfStoppedWslDistrounchanged: a settled running read, where a stopped or unanswered distro holds with the existing "WSL distro … is stopped" message.Why
Linked Issue
Internal WSL tracker, E9. e9-reviewer found this gap on #35865; it already existed for #35813's
wslPassiveRestore.wslBackgroundStartfrom #35865 will get the same hold once #35865 merges.Visual Proof
N/A: no new UI. A held pane shows #35813's existing Start card.
Testing
New
pty-spawn-wsl-passive-restore-hold.test.ts, through the realpty:spawnIPC with a mocked running read. The first two cases fail without the change.wslPassiveRestore) is never held.Also run:
the pty and daemon suites: 4,607 pass;
the load rule:
pnpm tc,typecheck:ci,check:code-quality:changed, the anti-slop lint and the ast-grep gates.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
wslPassiveRestoreis an existing IPC field.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)