Skip to content

Restoring a remote workspace terminal hands it this machine's working directory - #8634

Merged
teamleaderleo merged 18 commits into
manaflow-ai:mainfrom
ejc3:fix/remote-restore-local-cwd
Sep 27, 2026
Merged

teamleaderleo merged 18 commits into
manaflow-ai:mainfrom
ejc3:fix/remote-restore-local-cwd

Conversation

@ejc3

@ejc3 ejc3 commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fail closed when a remote-workspace agent restore has no trusted remote cwd: only authoritative restored remote provenance may select the far-host directory.
  • Preserve an exact nil cwd through built-in, custom-agent, and relaunch command construction instead of silently consulting the agent or launch snapshot again.
  • Keep generic directory reports rejected for trust-required remote panels, while allowing repeated authoritative remote reports to update the restored cwd.
  • Add behavior regressions for the remote restore paths.
  • A remote workspace's local terminal never takes a remote path as its own working directory. The chosen remote directory reaches the remote side as CMUX_REMOTE_INITIAL_CWD, which is how main starts remote terminals; the regression tests check that variable.

Testing

  • Red regression proof: GitHub Actions run 31136545880 failed the focused app-host shard on the test-only commit ff0f3cf.
  • Rebased onto main 4d3385b. Built locally with Xcode 26.2; AgentSessionAutoResumeSwiftTests and RemoteAgentRestoreWorkingDirectoryTests pass (39 tests), and the CMUXAgentLaunch package's suite passes (453 tests).
  • SessionPersistenceTests/testAgentHookSurfaceResumeStartupInputRunsWhenSavedWorkingDirectoryWasDeleted fails on this branch and identically on main in a host where a real codex is on PATH; it is not affected by this change.

Demo Video

  • Not applicable: this is a non-visual session-restore correctness fix.

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally (see Testing).
  • Regression coverage exercises the exact remote restore paths.
  • No user-facing strings were added; localization catalogs require no changes.
  • Docs and changelog changes are not needed for this internal correctness fix.
  • All required CI checks are green.
  • All review comments are resolved.

Note

High Risk
Changes session-restore and remote auto-resume cwd trust boundaries and command construction; incorrect behavior could run agents in the wrong directory on a remote host or drop cwd when it should apply.

Overview
Fixes remote workspace terminal restore leaking the local machine's cwd into remote agent resume commands.

Working directory policy: Introduces RestorableAgentWorkingDirectorySelection with recordedFallback vs exact modes. Exact selection stops silent fallback to agent/launch snapshot cwd and forces stripping all captured cwd argv flags (--work-dir, attached -C/w, etc.) via AgentLaunchSanitizer.removeAllWorkingDirectoryOptions.

Remote restore path: When restoring a remote workspace terminal snapshot, resume cwd is chosen only from trusted remote directory provenance (directoryIsTrustedRemoteReport), then AgentResumeWorkingDirectory resolves per agent kind (runtime vs launch namespace). Remote auto-resume uses .exact(...) so untrusted or missing remote cwd cannot reapply recorded local paths.

CI: Adds a non-tolerant focused app-host run for RemoteAgentRestoreWorkingDirectoryTests so assertion failures on this boundary are not masked by the broad sharded suite's tolerance.

Tests: Broad regressions for exact cwd stripping, remote authoritative directory updates, directory-keyed vs id-keyed agents, and untrusted remote panels; hook binding duplicate Kimi --work-dir drop.

Reviewed by Cursor Bugbot for commit 146a348. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Improved agent session resuming to select the correct working directory, including remote sessions.
    • Prevented stale, untrusted, or duplicate working-directory arguments from affecting resumed sessions.
    • Added support for recognizing and removing working-directory options across supported command formats.
    • Preserved custom agent working-directory settings and template substitutions during relaunches.
  • Tests

    • Added regression coverage for remote restores, Kimi resume commands, and working-directory argument handling.

Changelog

Fixed: Restoring a remote workspace terminal starts it in the remote directory instead of this Mac's working directory, and keeps agent flags like Claude's -w on resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review 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

Walkthrough

Remote agent resume now selects trusted runtime or launch directories, removes conflicting recorded working-directory arguments, and passes exact directory selections into startup command generation. Regression tests cover remote trust, agent-specific paths, option formats, canonicalization, and focused CI execution.

Changes

Remote resume working-directory handling

Layer / File(s) Summary
Working-directory selection
Sources/RestorableAgentTypes.swift, Sources/SessionRestorableAgentSnapshot+Commands.swift, Sources/RestorableAgentSession.swift
Resume startup input now supports recorded fallback and exact directory selection.
Command sanitization
Packages/macOS/CMUXAgentLaunch/..., Sources/RestorableAgentSession.swift, Sources/AgentRelaunchCommandBuilder.swift, Packages/macOS/CMUXAgentLaunch/Tests/...
Resume and relaunch commands resolve explicit directories and remove supported recorded working-directory options when required.
Trusted remote resolution
Sources/Workspace.swift
Remote resumes use runtime or launch directories according to agent kind.
Regression coverage
cmuxTests/..., Packages/macOS/CMUXAgentLaunch/Tests/..., .github/workflows/ci.yml
Tests cover remote directory trust, option removal, command canonicalization, and focused CI execution.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workspace
  participant RestorableAgentWorkingDirectorySelection
  participant RestorableAgentSession
  participant AgentLaunchSanitizer
  Workspace->>RestorableAgentWorkingDirectorySelection: resolve trusted runtime or launch directory
  RestorableAgentWorkingDirectorySelection->>RestorableAgentSession: provide exact working-directory selection
  RestorableAgentSession->>AgentLaunchSanitizer: remove recorded working-directory options
  AgentLaunchSanitizer-->>RestorableAgentSession: return sanitized resume arguments
  RestorableAgentSession-->>Workspace: produce resume startup input
Loading

Possibly related PRs

  • manaflow-ai/cmux#9855: Extends the same remote working-directory restore behavior across the workspace, session, sanitizer, and resume tests.

Suggested reviewers: lawrencecchen, austinywang

Merge Risk: 🟡 Moderate · up to b16bc

The PR hardens remote terminal restoration by requiring trusted remote directories and removing captured cwd arguments, but deferred restores can still fall back to persisted snapshot or launch directories when no trusted remote cwd exists. This could start a remote agent in a local or incorrect directory, so the deferred path needs correction or explicit owner acceptance before merge.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (3 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Package Boundaries ❌ Error The diff adds RestorableAgentWorkingDirectorySelection to Sources/RestorableAgentTypes.swift. The type contains standalone working-directory policy over String values, has no AppKit, SwiftUI, Gh… Move RestorableAgentWorkingDirectorySelection and its resolution logic into the existing Packages/macOS/CMUXAgentLaunch target, for example in AgentResumeWorkingDirectorySelection.swift. Expose `RestorableAgentWorkingDirectorySelectio…
Cmux Architecture Rethink ❌ Error The PR introduces two remote-agent resume paths with different working-directory policies. The immediate path in Workspace.swift uses .exact(resumeSessionWorkingDirectory), but the deferred candid… Use one shared remote-agent startup-input path for both immediate and deferred restore. Preserve the selection mode, including exact nil, in DeferredAgentResumeRestore or pass RestorableAgentWorkingDirectorySelection directly. Replace…
Cmux No Ambient Global State ❌ Error The diff adds a new static helper to an existing caseless namespace. Sources/RestorableAgentSession.swift:407 adds AgentResumeCommandBuilder.resumeShellCommand(...) as a static func. `AgentResum… Move the resume-command behavior from the static AgentResumeCommandBuilder namespace to a constructable owning type. Prefer an instance-based AgentResumeCommandBuilder with instance methods and injected dependencies, constructed at the …
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 9 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
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.
Cmux Swift Actor Isolation ✅ Passed PASS. The production diff adds only the immutable RestorableAgentWorkingDirectorySelection: Sendable value enum. It adds no service protocol, mutable Sendable reference type, logger, or UI-bound s…
Cmux Swift Blocking Runtime ✅ Passed The cumulative PR diff from eb0e35107c to HEAD adds or changes only working-directory selection, argument sanitization, restore-command construction, and related tests/CI wiring. Added production …
Cmux Browser Automation Off-Main ✅ Passed PASS: The pull request does not change browser socket automation. The cumulative diff from merge-base changes 10 session-restore, sanitizer, test, and CI files. Sources/TerminalController.swift and …
Cmux Expensive Synchronous Load ✅ Passed PASS: The aggregate PR diff from merge-base eb0e351 does not add or move RestorableAgentSessionIndex.load(), SharedLiveAgentIndex loading, Task.detached, file reads, JSON parsing, transcript/…
Cmux Cache Substitution Correctness ✅ Passed PASS: The merge-base diff introduces no cache lookup or replacement of a fresh authoritative read. The changed Swift code only resolves and sanitizes working-directory values during agent restore. Rem…
Cmux No Hacky Sleeps ✅ Passed PASS. The diff contains nine Swift files and one GitHub Actions YAML file. The only changed non-Swift file adds a focused xcodebuild test step and introduces no sleep, timer, polling, or wall-clock wa…
Cmux Algorithmic Complexity ✅ Passed PASS: The production diff does not introduce a prohibited scalable nested scan or repeated unbounded sort/filter. New scans are linear over command arguments and use only fixed-size option sets (6 and…
Cmux Swift Concurrency ✅ Passed PASS. The pull-request diff adds synchronous cwd selection and argument sanitization only. It adds no DispatchQueue, DispatchGroup, Combine state, publisher/sink, completion-handler, or fire-and-forge…
Cmux Swift @Concurrent ✅ Passed PASS. The PR adds or changes only synchronous Swift helpers and synchronous restore call sites. The diff contains no added async, nonisolated, await, Task, or @concurrent usage; the only add…
Cmux Swiftpm Lockfiles ✅ Passed PASS: The diff changes only Swift source/tests and adds one CI workflow step. It contains no Package.swift or Package.resolved changes, no .gitignore changes, and no cmux.xcodeproj/project.pbxproj pac…
Cmux Swift Logging ✅ Passed PASS: The complete PR diff adds no print, debugPrint, dump, NSLog, ad hoc file logging, or Logger declarations in production Swift. The existing Workspace.swift NSLog statements are unchan…
Cmux User-Facing Error Privacy ✅ Passed PASS — The production diff changes restore-directory selection and argument sanitization. It adds no user-facing errors, alerts, API error bodies, recovery copy, or command-output messages. The added …
Cmux Full Internationalization ✅ Passed PASS — The PR adds no user-facing text. The production Swift diff changes working-directory and resume logic, adds developer comments, and handles literal command-line tokens such as --work-dir, `-C…
Cmux Swiftui State Layout ✅ Passed PASS. The diff adds no SwiftUI state, layout, lazy-row store reference, or render-time mutation pattern. The only touched file with SwiftUI-related declarations is Sources/Workspace.swift; its exist…
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS. The pull request changes agent restore working-directory handling, tests, and CI wiring. The diff does not add or materially change an NSWindow, NSPanel, NSWindowController, SwiftUI Window, Wind…
Cmux Source Artifacts ✅ Passed PASS. The PR changes only regular tracked Swift source files, Swift test files, and .github/workflows/ci.yml. No log, media, build output, checkout, cache directory, or scratch directory is added. T…
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS. The merge-base diff changes production Swift files only for working-directory selection, command construction, and argument sanitization. It adds no #if DEBUG or test-build guard, no test-seam…
Title check ✅ Passed The title clearly identifies the primary remote workspace restore problem: the local machine's working directory is passed to the restored terminal.
Description check ✅ Passed The description includes the required Summary, Testing, Changelog, Demo Video, and Checklist sections. It explains the behavior change, test coverage, known verification limits, and applicable non-vis…
Full details: Docstring Coverage

Explanation

Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 9 files. (2 skipped: 1 unsupported, 1 too large.)

Full details: Cmux Swift Package Boundaries

Explanation

The diff adds RestorableAgentWorkingDirectorySelection to Sources/RestorableAgentTypes.swift. The type contains standalone working-directory policy over String values, has no AppKit, SwiftUI, Ghostty, lifecycle, or singleton dependency, and is directly unit-testable outside the app target. Its new tests remain in the app-host suite. The repository already provides the CMUXAgentLaunch SwiftPM target with the related reusable AgentResumeWorkingDirectory type and package tests. This is independently testable domain logic kept in the app root, which matches the explicit boundary failure condition. The Workspace provenance orchestration and app-specific snapshot integration are app glue and are not the boundary failure.

Resolution

Move RestorableAgentWorkingDirectorySelection and its resolution logic into the existing Packages/macOS/CMUXAgentLaunch target, for example in AgentResumeWorkingDirectorySelection.swift. Expose RestorableAgentWorkingDirectorySelection as the first public type, including the required public initializer/cases and resolution API. Add focused tests to CMUXAgentLaunchTests. Import and use the package type from the app while keeping remote provenance selection and Workspace lifecycle composition in Sources/.

Full details: Cmux Architecture Rethink

Explanation

The PR introduces two remote-agent resume paths with different working-directory policies. The immediate path in Workspace.swift uses .exact(resumeSessionWorkingDirectory), but the deferred candidate and Workspace+AgentLifecycle.resolveDeferredAgentResumeRestores still call the legacy resumeStartupInput(...restoringWorkingDirectory:). That overload selects .recordedFallback, so an exact nil can resolve snapshot.workingDirectory or launchCommand.workingDirectory again. This path is enabled when restoreIndexUnavailable is true, and the new DeferredAgentResumeRestore carries only the optional directory, not the exact-selection invariant. The diff therefore materially expands split entrypoint wiring and leaves the local-cwd leak representable. The PR does provide a clear policy owner (RestorableAgentWorkingDirectorySelection) and a shared resolver, but it does not route all remote restore entrypoints through that owner.

Resolution

Use one shared remote-agent startup-input path for both immediate and deferred restore. Preserve the selection mode, including exact nil, in DeferredAgentResumeRestore or pass RestorableAgentWorkingDirectorySelection directly. Replace the deferred candidate and deferred lifecycle calls with the exact selection for remote restores, and add a regression test with an unavailable agent index and no trusted remote directory that proves no snapshot or launch cwd is emitted.

Full details: Cmux No Ambient Global State

Explanation

The diff adds a new static helper to an existing caseless namespace. Sources/RestorableAgentSession.swift:407 adds AgentResumeCommandBuilder.resumeShellCommand(...) as a static func. AgentResumeCommandBuilder is an enum with an API composed almost entirely of static functions, so the pull request adds new ambient behavior to a static helper namespace. No new top-level free function, mutable global, or singleton was found. The existing AgentLaunchSanitizer namespace was modified, but its sanitizer method already existed.

Resolution

Move the resume-command behavior from the static AgentResumeCommandBuilder namespace to a constructable owning type. Prefer an instance-based AgentResumeCommandBuilder with instance methods and injected dependencies, constructed at the session-restore seam, or place the policy on SessionRestorableAgentSnapshot, which owns the restore data. Do not add further static API to the caseless namespace.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@ejc3
ejc3 force-pushed the fix/remote-restore-local-cwd branch from d935d20 to 7cfee7d Compare July 22, 2026 10:52
ejc3 added a commit to ejc3/cmux that referenced this pull request Jul 22, 2026
testTypingRepairForwardsKeyDown asserted that the repaired keyDown reached
libghostty, but forwarding only happens once the runtime surface is live, and the
headless xctest host does not always spin one up. The assertion then fails for a
reason that has nothing to do with typing repair.

Gate the forward observation on a live surface, matching the constraint the
neighbouring tests in this file already respect.

This commit previously also carried a fix for remote-workspace restore handing
the terminal a local working directory. That fix is manaflow-ai#8634, which restructures the
same logic behind a named predicate and gates both paths rather than one, so it
is dropped here to avoid two competing changes to the same function.
ejc3 added a commit to ejc3/cmux that referenced this pull request Jul 25, 2026
testTypingRepairForwardsKeyDown asserted that the repaired keyDown reached
libghostty, but forwarding only happens once the runtime surface is live, and the
headless xctest host does not always spin one up. The assertion then fails for a
reason that has nothing to do with typing repair.

Gate the forward observation on a live surface, matching the constraint the
neighbouring tests in this file already respect.

This commit previously also carried a fix for remote-workspace restore handing
the terminal a local working directory. That fix is manaflow-ai#8634, which restructures the
same logic behind a named predicate and gates both paths rather than one, so it
is dropped here to avoid two competing changes to the same function.
@ejc3
ejc3 marked this pull request as ready for review July 25, 2026 05:39
@greptile-apps

greptile-apps Bot commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug in session-restore logic where a remote-workspace terminal would be handed the local machine's working directory (typically ~) instead of no directory. The root cause was that the startupHandlesWorkingDirectory fallback in requestedWorkingDirectory did not check whether the surface was local before applying, so the ssh invocation used as the "startup command" on a remote terminal caused the local path to leak to the far host.

  • Extracts a restoresLocalTerminalSurface predicate (no effective remote startup command, no remote PTY attach, not a remote-workspace snapshot) that accurately identifies local terminal surfaces.
  • Gates both localWorkingDirectory and the startupHandlesWorkingDirectory fallback on restoresLocalTerminalSurface, so remote-workspace restores always receive a nil working directory.
  • Local restore paths are fully unchanged; the existing test AgentSessionAutoResumeSettingsTests.testRemoteWorkspaceAutoResumeKeepsRemoteStartupCommand now passes without modification.

Confidence Score: 5/5

Safe to merge — the change is a minimal, targeted fix to a single restore-path condition with no new state, no concurrency changes, and a pre-existing test confirming the correct outcome.

The diff is a six-line refactor of scalar boolean logic. restoresLocalTerminalSurface is derived from conditions already present in the code; the only behavioral change is gating the startupHandlesWorkingDirectory fallback on that same boolean, which is exactly the fix required. Local restore paths are structurally untouched. The existing test exercises the remote-workspace arm and was already failing before this PR, confirming the diagnosis, and now passes.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
Sources/Workspace.swift Extracts restoresLocalTerminalSurface boolean and uses it to gate both the localWorkingDirectory and the startupHandlesWorkingDirectory fallback in requestedWorkingDirectory, preventing remote-workspace terminals from receiving the local machine's working directory.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Restore terminal snapshot] --> B{restoresLocalTerminalSurface?}
    B -- No / remote surface --> C[localWorkingDirectory = nil]
    C --> D[requestedWorkingDirectory = nil]
    B -- Yes / local surface --> E{startupHandlesWorkingDirectory?}
    E -- No --> F[localWorkingDirectory = savedWorkingDirectory or workingDirectory]
    F --> G[requestedWorkingDirectory = localWorkingDirectory]
    E -- Yes --> H[localWorkingDirectory = nil]
    H --> I[requestedWorkingDirectory = workingDirectory via fallback]
Loading

Reviews (2): Last reviewed commit: "Restoring a remote workspace terminal ha..." | Re-trigger Greptile

@ejc3
ejc3 force-pushed the fix/remote-restore-local-cwd branch from 7cfee7d to 16d5e0e Compare July 25, 2026 06:45
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Workspace.swift`:
- Around line 1460-1471: Update the working-directory selection around
restoresLocalTerminalSurface and requestedWorkingDirectory to use
snapshot.terminal?.isRemoteTerminal as the authoritative remote-surface
indicator, rather than inferring local ownership from missing
effectiveRemoteStartupCommand or restoredRemotePTYAttachCommand. Ensure
explicitly remote restored terminals never receive the workspace-local fallback
cwd, and fail closed with nil when no safe local working-directory source
exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 43912b14-903f-4df4-81af-b25c0f611789

📥 Commits

Reviewing files that changed from the base of the PR and between da8a58a and 16d5e0e.

📒 Files selected for processing (1)
  • Sources/Workspace.swift

Comment thread Sources/Workspace.swift Outdated
@ejc3 ejc3 closed this Jul 31, 2026
@ejc3
ejc3 force-pushed the fix/remote-restore-local-cwd branch from 16d5e0e to b5294c4 Compare July 31, 2026 06:43
@ejc3 ejc3 reopened this Jul 31, 2026
austinywang pushed a commit that referenced this pull request Aug 4, 2026
* cmuxTests: gate the keyDown-forward assertion on a live surface

testTypingRepairForwardsKeyDown asserted that the repaired keyDown reached
libghostty, but forwarding only happens once the runtime surface is live, and the
headless xctest host does not always spin one up. The assertion then fails for a
reason that has nothing to do with typing repair.

Gate the forward observation on a live surface, matching the constraint the
neighbouring tests in this file already respect.

This commit previously also carried a fix for remote-workspace restore handing
the terminal a local working directory. That fix is #8634, which restructures the
same logic behind a named predicate and gates both paths rather than one, so it
is dropped here to avoid two competing changes to the same function.

* cmuxTests: skip the keyDown-forward check visibly instead of silently dropping it

The forward assertion was wrapped in `if terminalPanel.surface.hasLiveSurface`, which means on a host
without a live libghostty surface the oracle simply disappears and the test still reports green. The
comment even said the headless host does not always provide one, so the coverage was not just
conditional, it was silently conditional, and nothing distinguished a run that checked the forwarding
from a run that did not.

XCTSkipUnless says it out loud. The repair-routing assertions above run first and are checked on every
host either way, so nothing that was verified before is verified less now.

---------

Co-authored-by: ejc3 <ejc3@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@austinywang

Copy link
Copy Markdown
Contributor

@greptile-apps review

@austinywang
austinywang changed the base branch from main to fix/remote-restore-local-cwd August 7, 2026 00:42
@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@austinywang
austinywang changed the base branch from fix/remote-restore-local-cwd to main August 7, 2026 00:42
@austinywang
austinywang changed the base branch from main to fix/remote-restore-local-cwd August 7, 2026 00:59
@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@austinywang
austinywang changed the base branch from fix/remote-restore-local-cwd to main August 7, 2026 00:59
@austinywang
austinywang changed the base branch from main to fix/remote-restore-local-cwd August 7, 2026 01:30
ejc3 added a commit to ejc3/cmux that referenced this pull request Sep 26, 2026
# Conflicts:
#	.github/workflows/ci.yml
#	Sources/RestorableAgentSession.swift
ejc3 and others added 15 commits September 26, 2026 13:25
… directory

Restoring a remote-workspace terminal builds a resume command that cds into
this machine's cwd, so the far host is asked to enter a path that only exists
locally. Two gaps line up to cause it:

- updatePanelDirectory dropped every generic live report on a trust-required
  remote panel, so a remote-workspace terminal that never got a report through
  the remote control channel had no recorded directory at all in the session
  snapshot. Accept the first report into an empty slot; overwrites stay refused
  and the value gains no remote-report provenance.

- On restore, the remote branch of resumeSessionWorkingDirectory fell back to
  workingDirectory, which degrades to the local cwd whenever remote trust rules
  blank the saved directory. Use the panel's recorded directory instead: the
  resume command runs on the remote host, where that path is the right target.

The pre-existing regression test
AgentSessionAutoResumeSettingsTests.testRemoteWorkspaceAutoResumeKeepsRemoteStartupCommand
was failing on main (the resume cd prefix and requestedWorkingDirectory both
landed on the local home directory) and now passes, along with the restored
panelDirectories assert in
TabManagerSessionSnapshotTests.testSessionSnapshotRestoresSSHWorkspaceDescriptor.
…tions

removingSavedWorkingDirectoryOptions documents that it preserves content after
`--`, but with removeAllWorkingDirectoryOptions the value branch accepted any
next token -- including `--` itself. `--work-dir -- --cwd /x prompt` consumed
the delimiter as the option's value, and everything after it was then sanitized
as options rather than passed through.

A cwd option immediately before `--`, or at the very end, has no value to take:
drop the option alone. Two cases added, both red without the change.
@ejc3
ejc3 force-pushed the fix/remote-restore-local-cwd branch from 0249baa to bdcaa45 Compare September 26, 2026 21:51
ejc3 added a commit to ejc3/cmux that referenced this pull request Sep 26, 2026
ejc3 added a commit to ejc3/cmux that referenced this pull request Sep 26, 2026
teamleaderleo and others added 2 commits September 27, 2026 08:49
Remote (exact) restores removed -w, -C and --workspace for every agent, which
dropped Claude's -w/--worktree, Qoder's --workspace profile selector, and
attached tokens such as -Continue that main keeps on remote resume.

Remove-all now drops an option regardless of value only when its cwd meaning
is known for the kind (AgentWorkingDirectoryOptionPolicy, ported from manaflow-ai#9855):
--cd/--cwd/--work-dir always; -C for codex; -w for kimi and qoder; --workspace
for cursor. Attached short forms follow the same scoping. Custom registrations
pass no kind. Value-matched removal is unchanged.

Also moves RestorableAgentWorkingDirectorySelection into CMUXAgentLaunch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

CI failure attribution

CI passes on d190ec547a (run 36325748356 attempt 2).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's.

Exact remote restores passed no agent kind whenever a registration was
present, so Kimi (restored through its exact built-in registration) kept an
attached -w/dir cwd option. Use managedProviderKind, which maps kimi and
other exact built-in registrations to their kind; user-authored templates
never reach this sanitizing branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo merged commit 3be8bdd into manaflow-ai:main Sep 27, 2026
64 checks passed
@teamleaderleo

Copy link
Copy Markdown
Collaborator

Merged, thanks @ejc3! I added one change on top: exact restores now remove only the cwd flags each agent actually owns (codex -C, kimi/qoder -w, cursor --workspace), so Claude's -w and qoder's --workspace survive a remote resume.

@github-actions

Copy link
Copy Markdown
Contributor

Merge receipt for d190ec547a: every check was green at merge (21 verified; 16 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 27, 2026
c490fb4 Add an opt-in subtle sidebar selection highlight (manaflow-ai#14890)
b78dae5 ci: evict parked pull request builds oldest first instead of a free-disk floor (manaflow-ai#15039)
31b6fc9 test: close the TabManagers the session snapshot tests create (manaflow-ai#15038)
e64eb93 ci: send every retry of an owned-mini job to Blacksmith, once (manaflow-ai#15035)
17f0f90 Test: pin persisted Kimi custom kind on exact remote restore (manaflow-ai#15034)
f9d5bb5 Switch between cmux and cmux NIGHTLY from inside the app (manaflow-ai#14995)
756847a sidebar: keep the selected row clear of the footer (manaflow-ai#15013)
1431a1e One dialog per close, and Feed banner clicks open the agent (manaflow-ai#14960)
74d4719 ci: route cli-product-tests to the gui runners like the shards (manaflow-ai#15011)
b78e8aa ci: park each pull request's build on its root and route its re-push back to it (manaflow-ai#15020)
60e45e7 web tests: run every child process asynchronously (manaflow-ai#15001)
55724f0 ci: side lanes take the light minis' side runners first (manaflow-ai#15019)
3be8bdd Restoring a remote workspace terminal hands it this machine's working directory (manaflow-ai#8634)
a9e555c Reattach 0.64.25 tmux-profile SSH workspaces after upgrading (manaflow-ai#14938)
fe20e20 fix: mark workspace-level notifications read on workspace focus (manaflow-ai#12427)

# Conflicts:
#	.github/workflows/app-host-test-rerun.yml
#	.github/workflows/auth-refresh-tests.yml
#	.github/workflows/ci-macos.yml
#	.github/workflows/ci.yml
#	.github/workflows/cloud-command-deadlines.yml
#	.github/workflows/cloud-machine-tests.yml
#	.github/workflows/cloud-task-local-tests.yml
#	.github/workflows/cmux-tui.yml
#	.github/workflows/iroh-v2.yml
#	.github/workflows/relay-tls.yml
#	.github/workflows/reload-build.yml
#	.github/workflows/remote-daemon.yml
#	.github/workflows/terminal-hang-diagnostics.yml
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.

3 participants