Skip to content

fix(terminal): every start in a deleted folder opens at the workspace root and says so - #30747

Merged
brennanb2025 merged 19 commits into
mainfrom
brennanb2025/conv-13a-missing-cwd
Oct 21, 2026
Merged

brennanb2025 merged 19 commits into
mainfrom
brennanb2025/conv-13a-missing-cwd

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Terminals whose saved start folder was deleted could open from the desktop window but fail when started through the host, phone, restore or orchestration. These callers made the folder decision at different stages, so some reached launch preparation with a deleted folder.

The saved tab folder also has two writers: the host’s snapshot and the window’s tab snapshot. The window’s snapshot replaces the host’s. This fix makes a newly revealed window tab write the right value by passing the retained saved folder through the existing reveal folder field.

User-facing change

Starts share the folder decision on the machine running the terminal, before launch preparation. A deleted requested folder opens at a usable workspace root. The existing CLI/RPC warning reports fallback; an explicit phone restore gets one toast and automatic restore stays quiet. A window-created terminal writes its existing sentence once after restored output, and normal restore writes none. The host-created terminal line remains deferred below.

Host-owned saved tabs correct their saved folder only after proven absence. Access errors, I/O errors, timeouts and inconclusive probes never authorize correction. Recovery keeps a present or unverifiable historical folder; a proven-missing historical folder can yield to the resolved request. Attaching to a live terminal preserves its identity and saved folder. A deleted workspace root still fails.

Native folder checks are asynchronous, preserving main’s shared resolver and ownership/commit rules. There is no probe deadline: an unanswered check can hold its start indefinitely while the main event loop remains free. It cannot authorize a fallback-derived correction or complete the spawn binding commit. Existing admission can save a new tab’s original requested folder before probing; that is distinct from healing an existing choice.

Differences and limits

  • Intended: root fallback also applies to unreadable host requests, matching the window policy, while retaining the original saved folder. The existing sentence can say the folder no longer exists even for an access error. Callers that ignore the warning can run at a different folder.
  • TEMPORARY: collapsing the host snapshot and window tab snapshot to one saved-folder writer is owed to rows 14–18 (window starts moving onto the host path). This PR makes a newly revealed window tab write the retained value. Tracked in Main owns the terminal and tab layout in shipped builds #30861, the layout-owner migration that removes the second writer.
  • TEMPORARY: early agent-launch tabs and the window’s phone-copy publisher can still omit the requested folder, as on main. Tracked in STA-11295; removed by rows 14–18. With a desktop window open, phone restoration of such a tab can use the root without a phone notice or saved-folder correction.
  • TEMPORARY: the in-terminal line for host-created tabs, including host splits, is owed to rows 14–18 (window starts moving onto the host path). Existing CLI/RPC warnings and explicit phone toasts remain. Tracked in STA-11517.
  • Intended: window-created tabs keep their existing saved-folder policy. Old paired servers keep their current launch path; new reply metadata is optional and the daemon protocol is unchanged.
  • TEMPORARY: explicitly requested WSL UNC paths retain the limitation tracked in STA-11218. Recovered WSL paths use the execution-host probe. The existing guest directory test cannot distinguish every guest permission denial from absence; inconclusive host execution retains the candidate.
  • Accepted: renderer remount can remove its synthetic notice. A bounded reservation/notice trace established no concrete duplicate or spurious notice; no extra notice state was added. Existing durable ownership/save refusal can still reject a start; this change preserves those rules.

Validation

Current head: 000e41e17a38a26f8953f68de9332acd7d4ff001, composed with main fc8439e05f1eceb33e7178214725de408c5d73ca.

Formatted production additions/deletions: +326/−117 in 51 paths, including fixtures. Tests: +317/−154 in 22 paths. These satisfy the authorized absolute production ceiling of +355 and test additions do not exceed production additions. All 163 tests in nine named suites passed through the machine queue; the normal commit hook passed without changing the approved tree. Both fresh full source reviews found no new concrete defect, including the final whole-mechanism comparison and readiness audit.

The exact-head full PR workflow and its aggregate gate passed. All PR checks were successful or skipped.

Runtime QA

Final exact-head QA: PASS, M4Air ticket 1059. Both main fc8439e05f1eceb33e7178214725de408c5d73ca and PR 000e41e17a38a26f8953f68de9332acd7d4ff001 were built in this run, plus main-owned-layout variants of both. Codex drove isolated hidden Electron through CDP/RPC; real Claude Code 2.1.306 and Codex 0.163.0 ran as the agents under test.

Flow Main versus PR result
Window start and cold restore Fresh missing-folder start opens at root with one notice on both. Cold restore: main has a missing-directory error panel; PR opens without it and writes exactly one notice. Existing-folder control has zero notices on both.
Host start Main errors; PR opens at root with the caller warning.
Headless saved-tab restore, folder and linked git worktree Main activation errors. PR heals Store memory and SQLite to root; a second full restart retains root without repeating fallback metadata.
Unreadable host start with a visible window Real EACCES reproduced on both builds. Main errors; PR opens at root with a warning. The original child folder remains in renderer, Store and SQLite after permissions are restored and after cold restart.
Owner reattach with save during reconnect Real Claude resumes and replies on both, no owner banner; the tab survives a second relaunch.
Held handle, both main-owned-layout builds Saved handle initially has no PTY; wait materializes it, send/read succeed with the same handle, and exit satisfies the wait.
Codex restart from main-created state Frozen main profile restored in place into PR resumes the exact provider session and saved choices, completes a real reply and a pwd action, with one agent process. Main-to-main control also passes.
Split and startup command Both builds use root for inherited deleted cwd with one notice; command runs once, one PTY per pane.
Normal restore Order, active third shell, focus, zero notices and real Claude Done match main.

Earlier phone-toast and Windows/WSL evidence is accepted by the launcher, with its original revision c90a82b94cb3e14e08fa60ba92379ced8725e357. Explicit phone tap produced one toast; automatic restore remained silent; Windows mounted-drive denial preserved the saved original through restart. SSH and never-settling probes remain source-only; no unsafe hung mount was manufactured. Other I/O, timeout and inconclusive cases remain source/test evidence. The pending-probe ordering claim excludes inherited new-tab admission of the original requested folder.

All scored QA processes were stopped; source and lockfiles stayed clean. Headless healing and unreadable saved-folder retention are backed by RPC, renderer/Store memory and SQLite evidence, rather than inferred from screenshots.

Visual proof

Deleted-folder cold restore: main fc8439e PR 000e41e
Main cold restore with error panel PR cold restore with one notice
Final-head normal restore, unreadable start and held handle

PR normal restore, active third shell

PR unreadable-folder start

PR held handle after send and read

Final-head real Claude and Codex replies, and command split

PR real Claude reply after owner reattach

PR real Codex reply after in-place upgrade

PR command split runs once

Earlier phone evidence at c90a82b (accepted unchanged mobile code):

Main explicit tap PR explicit tap PR automatic restore
Main phone restore PR phone toast PR automatic restore

Author: @BrennanKB5

…ved folder is gone, and say so

A terminal whose start folder was deleted opened at the workspace root only
when the window started it; the CLI, phone restore, orchestration and other
host starts failed with the raw "Working directory ... does not exist" error,
because the renderer had to opt in with a cwdFallback flag.

The execution host now makes that decision for every start that may create a
process (anything but a known live pane), with no caller flag. The fallback is
reported only when a process was proven started at the requested folder, so a
reattach or a crash restore in its recovered folder never claims "workspace
root", and only then does the saved start folder heal. A recovered folder that
was itself deleted yields to the request's resolved folder.

The notice reaches each caller through surfaces it already has: the window's
terminal line (now also written when a host-created tab attaches, and on a
restored reattach), the agent.launch warning (also for callers that show the
tab themselves), and a toast when a phone tap restores the tab.
@brennanb2025
brennanb2025 marked this pull request as ready for review October 19, 2026 18:12
@coderabbitai

coderabbitai Bot commented Oct 19, 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

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: b2514b92-c0a9-4e02-bee6-dc46b56bad2b
📥 Commits

Reviewing files that changed from the base of the PR and between ae355fa and 62b1bb2.

📒 Files selected for processing: 1
  • src/main/runtime/session-lifecycle/projection/perform-mobile-session-pty-records-refresh.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

PTY startup now probes requested and recovered directories and can select the workspace root when a requested directory is proven missing. Spawn results carry startup-directory source and fallback metadata into persistence and runtime replies. Terminal, reattach, agent-launch, and mobile-tab activation paths expose fallback information and display a notice or toast. The spawn request no longer accepts the cwdFallback option.

Priority: ⚪ Pending latest changes

Merge Risk: 🟡 Moderate · up to 62b1b

Unreadable WSL startup folders can be unexpectedly replaced by the workspace root, so this should be fixed before merging.

Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 21.21% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 51 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check Warning The description gives detailed problem, user-facing behavior, implementation limits, testing results, runtime QA, and visual proof. However, it does not follow the required template: it lacks an expli… Add the required template sections and complete them. Include the actual linked issue in the Fixes #... field, or document the maintainer exception if applicable. Preserve the existing validation, QA, and visual-proof details under the ma…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly states the primary change: starts from deleted folders now open at the workspace root and report the fallback.
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.
Full details: Description check

Explanation

The description gives detailed problem, user-facing behavior, implementation limits, testing results, runtime QA, and visual proof. However, it does not follow the required template: it lacks an explicit Linked Issue with a Fixes #... entry, ELI5, What Changed, Why, Testing, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist sections.

Resolution

Add the required template sections and complete them. Include the actual linked issue in the Fixes #... field, or document the maintainer exception if applicable. Preserve the existing validation, QA, and visual-proof details under the matching sections.

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

❤️ 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: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: be3e21f9-330b-4f6b-ae99-048f9b8a8d6b
📥 Commits

Reviewing files that changed from the base of the PR and between 36913fd and 78cbf31.

📒 Files selected for processing: 65
  • mobile/src/session/session-write-reply-schema.ts
  • mobile/src/session/startup-cwd-fallback-mobile-reply.test.ts
  • mobile/src/session/use-mobile-session-tab-switching.ts
  • src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts
  • src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts
  • src/main/daemon/daemon-pty-session-spawn.ts
  • src/main/daemon/daemon-pty-spawn-result.ts
  • src/main/ipc/pty-agent-pane-launch.test.ts
  • src/main/ipc/pty-launch-parity.test-fixture.ts
  • src/main/ipc/pty-launch-parity.test.ts
  • src/main/ipc/pty-pane-materialization-race.test.ts
  • src/main/ipc/pty-spawn-cwd-fallback.test.ts
  • src/main/ipc/pty/host-env/spawn-cwd.ts
  • src/main/ipc/pty/host-env/startup-cwd-directory-exists.test.ts
  • src/main/ipc/pty/host-env/startup-cwd-directory-exists.ts
  • src/main/ipc/pty/ipc/spawn-agent-launch.ts
  • src/main/ipc/pty/ipc/spawn-commit-persist.ts
  • src/main/ipc/pty/ipc/spawn-commit.ts
  • src/main/ipc/pty/ipc/spawn-preflight.ts
  • src/main/ipc/pty/ipc/spawn-state.ts
  • src/main/ipc/pty/ipc/spawn-types.ts
  • src/main/ipc/pty/pane/stable-owner.ts
  • src/main/ipc/pty/register-handlers.ts
  • src/main/ipc/pty/runtime/spawn-commit.ts
  • src/main/ipc/pty/runtime/spawn-options.ts
  • src/main/ipc/pty/runtime/spawn-preflight.ts
  • src/main/ipc/pty/runtime/spawn-state.ts
  • src/main/ipc/pty/runtime/spawn.ts
  • src/main/ipc/pty/spawn-options-agent-workspace-trust.test.ts
  • src/main/ipc/pty/spawn-startup-target.ts
  • src/main/persistence/loading-store/pty-binding-fast-lane.ts
  • src/main/persistence/loading-store/pty-binding-persistence.ts
  • src/main/persistence/loading-store/pty-binding-placement-inertness.test.ts
  • src/main/persistence/loading-store/pty-binding-session-update.ts
  • src/main/persistence/loading-store/pty-spawn-commit-dependencies-fixture.ts
  • src/main/providers/local-pty-session-activation.ts
  • src/main/providers/pty-spawn-result.ts
  • src/main/runtime/orca-runtime-terminal-cwd.test.ts
  • src/main/runtime/rpc/methods/agent-launch-caller-selection.test.ts
  • src/main/runtime/rpc/methods/agent-launch-surfaces.ts
  • src/main/runtime/rpc/methods/agent-launch.test-fixture.ts
  • src/main/runtime/rpc/methods/session-tab-mutation-methods.ts
  • src/main/runtime/rpc/methods/terminal/terminal-create-sort-stamp.test.ts
  • src/main/runtime/rpc/methods/terminal/terminal-lifecycle-methods.ts
  • src/main/runtime/runtime-pty-controller-contract.ts
  • src/main/runtime/session-lifecycle/projection/perform-mobile-session-pty-records-refresh.ts
  • src/main/runtime/terminal/launch/mobile-terminal-owned-create.ts
  • src/main/runtime/terminal/launch/terminal-create.ts
  • src/preload/api/pty-api.ts
  • src/preload/api/pty-bridge-session-control.ts
  • src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts
  • src/renderer/src/components/terminal-pane/ipc-pty-connect-result.ts
  • src/renderer/src/components/terminal-pane/ipc-pty-spawn-request.ts
  • src/renderer/src/components/terminal-pane/pty-connection-reattach-mode-reset.spec.ts
  • src/renderer/src/components/terminal-pane/pty-connection-runtime-owner-spawn-routing.spec.ts
  • src/renderer/src/components/terminal-pane/pty-connection/deferred-session-reattach-connect.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pane-transport-options.ts
  • src/renderer/src/components/terminal-pane/pty-connection/startup-cwd-fallback-notice.ts
  • src/renderer/src/components/terminal-pane/pty-transport-connect-spawn.test.ts
  • src/renderer/src/components/terminal-pane/pty-transport-reattach-admission.test.ts
  • src/renderer/src/components/terminal-pane/pty-transport-types.ts
  • src/shared/launch-parity-window-request.test-fixture.ts
  • src/shared/runtime-session-contracts.ts
  • src/shared/runtime-terminal-contracts.ts
  • src/shared/startup-cwd-fallback.ts
💤 Files with no reviewable changes: 14
  • src/main/ipc/pty-pane-materialization-race.test.ts
  • src/main/ipc/pty/register-handlers.ts
  • src/preload/api/pty-bridge-session-control.ts
  • src/shared/launch-parity-window-request.test-fixture.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pane-transport-options.ts
  • src/main/persistence/loading-store/pty-spawn-commit-dependencies-fixture.ts
  • src/main/ipc/pty/ipc/spawn-types.ts
  • src/preload/api/pty-api.ts
  • src/main/ipc/pty/spawn-options-agent-workspace-trust.test.ts
  • src/main/ipc/pty/host-env/spawn-cwd.ts
  • src/main/ipc/pty/ipc/spawn-agent-launch.ts
  • src/renderer/src/components/terminal-pane/pty-transport-types.ts
  • src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts
  • src/renderer/src/components/terminal-pane/ipc-pty-spawn-request.ts

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

Comment thread src/main/ipc/pty/spawn-startup-target.ts Outdated
@brennanb2025

brennanb2025 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

A terminal whose saved start folder was deleted could open from the desktop window but fail through the host. Host starts now resolve the folder on the execution host before launch preparation and can open at the workspace root with the existing caller warning or explicit phone toast. Window restore writes its existing sentence once after replay; normal restore writes none.

The saved tab folder has two writers: the window tab snapshot replaces the host snapshot. The retained saved folder now travels through the existing reveal folder field so a newly revealed tab writes the right value. TEMPORARY: consolidation to one writer is owed to rows 14–18 (window starts moving onto the host path), tracked in #30861, the layout-owner migration that removes the second writer.

Reviewed head: 000e41e17a38a26f8953f68de9332acd7d4ff001, with main fc8439e05f1eceb33e7178214725de408c5d73ca. Both fresh full source reviews found no new concrete defect and completed the whole-mechanism comparison. Formatted production is +326/−117 in 51 paths, tests +317/−154 in 22, within the authorized +355 ceiling. All 163 tests in nine named suites and the normal commit hook passed. The exact-head full PR workflow and aggregate gate passed.

Only proven absence permits a host saved-folder correction. Unreadable or unverifiable folders retain their saved choice; recovered unknown is retained. Async probes preserve the shared resolver with no deadline. A pending probe cannot authorize healing or complete the spawn binding commit; inherited admission can save a NEW tab’s original request first. Existing ownership and durable-save refusal behavior remains.

Final exact-head QA: PASS, M4Air ticket 1059. Both main fc8439e05f1eceb33e7178214725de408c5d73ca and PR 000e41e17a38a26f8953f68de9332acd7d4ff001 were built in this run, plus main-owned-layout variants of both. Codex drove isolated hidden Electron through CDP/RPC; real Claude Code 2.1.306 and Codex 0.163.0 ran as the agents under test.

Flow Main versus PR result
Window start and cold restore Fresh missing-folder start opens at root with one notice on both. Cold restore: main has a missing-directory error panel; PR opens without it and writes exactly one notice. Existing-folder control has zero notices on both.
Host start Main errors; PR opens at root with the caller warning.
Headless saved-tab restore, folder and linked git worktree Main activation errors. PR heals Store memory and SQLite to root; a second full restart retains root without repeating fallback metadata.
Unreadable host start with a visible window Real EACCES reproduced on both builds. Main errors; PR opens at root with a warning. The original child folder remains in renderer, Store and SQLite after permissions are restored and after cold restart.
Owner reattach with save during reconnect Real Claude resumes and replies on both, no owner banner; the tab survives a second relaunch.
Held handle, both main-owned-layout builds Saved handle initially has no PTY; wait materializes it, send/read succeed with the same handle, and exit satisfies the wait.
Codex restart from main-created state Frozen main profile restored in place into PR resumes the exact provider session and saved choices, completes a real reply and a pwd action, with one agent process. Main-to-main control also passes.
Split and startup command Both builds use root for inherited deleted cwd with one notice; command runs once, one PTY per pane.
Normal restore Order, active third shell, focus, zero notices and real Claude Done match main.

Earlier phone-toast and Windows/WSL evidence is accepted by the launcher, with its original revision c90a82b94cb3e14e08fa60ba92379ced8725e357. Explicit phone tap produced one toast; automatic restore remained silent; Windows mounted-drive denial preserved the saved original through restart. SSH and never-settling probes remain source-only; no unsafe hung mount was manufactured. Other I/O, timeout and inconclusive cases remain source/test evidence. The pending-probe ordering claim excludes inherited new-tab admission of the original requested folder.

All scored QA processes were stopped; source and lockfiles stayed clean. Headless healing and unreadable saved-folder retention are backed by RPC, renderer/Store memory and SQLite evidence, rather than inferred from screenshots.

Deferred and accepted: early agent-launch/window phone-copy omissions are TEMPORARY STA-11295 and rows 14–18; the host-tab in-terminal line is owed to rows 14–18 and tracked in STA-11517; explicit UNC requests remain STA-11218; renderer remount can remove its notice. Existing guest permission ambiguity remains. Bounded reservation/notice and indirect-activation concerns remain unconfirmed, with no added guard or state.

For Brennan’s review

Deleted-folder cold restore: main fc8439e PR 000e41e
Main cold restore with error panel PR cold restore with one notice
Final-head normal restore, unreadable start and held handle

PR normal restore, active third shell

PR unreadable-folder start

PR held handle after send and read

Final-head real Claude and Codex replies, and command split

PR real Claude reply after owner reattach

PR real Codex reply after in-place upgrade

PR command split runs once

Earlier phone evidence at c90a82b (accepted unchanged mobile code):

Main explicit tap PR explicit tap PR automatic restore
Main phone restore PR phone toast PR automatic restore

Three judgment points: only proven absence permits correction, both restore branches preserve the outcome, and retained folder uses the existing reveal field.

@brennanb2025
brennanb2025 merged commit 8174208 into main Oct 21, 2026
66 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/conv-13a-missing-cwd branch October 21, 2026 17:25
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