Skip to content

fix(agent-resume): resume from launch directory, not drifted cwd - #5029

Closed
TravelThePath wants to merge 3 commits into
manaflow-ai:mainfrom
TravelThePath:fix/agent-resume-launch-dir
Closed

TravelThePath wants to merge 3 commits into
manaflow-ai:mainfrom
TravelThePath:fix/agent-resume-launch-dir

Conversation

@TravelThePath

@TravelThePath TravelThePath commented May 30, 2026 •

Copy link
Copy Markdown

Summary

When the cmux app is closed and reopened, it restores agent sessions by generating a shell command like:

cd '/Users/you/project/subdir' && '/Users/you/.local/bin/claude' '--resume' '<session-id>'

The cd target was the agent's drifted current working directory, not the directory the agent was launched in. Claude Code keys its session transcript to the launch directory, so resuming from a drifted cwd fails hard:

No conversation found with session ID: <session-id>

This PR makes resume (and fork) cd back to the launch directory.

Root cause

Both resume-command builders resolved the working directory as workingDirectory ?? launchCommand?.workingDirectory — i.e. they preferred the drifted cwd and only fell back to the launch dir:

  • workingDirectory = the agent's hook-reported current cwd (can drift mid-session).
  • launchCommand?.workingDirectory = CMUX_AGENT_LAUNCH_CWD = the dir the agent was launched in (set by the claude wrapper to $PWD).

For Claude this is fatal because claude --resume <id> only finds the session in the project directory it was created in (the launch dir). The fork-probe path (AgentForkSupport.openCodeProbeWorkingDirectory) already used the correct precedence — the resume builders were the inconsistent ones.

The fix

Flip the precedence to launchCommand?.workingDirectory ?? workingDirectory in:

File Location Covers
Sources/RestorableAgentSession.swift AgentResumeCommandBuilder.shellCommand (used by both resumeShellCommand and forkShellCommand) resume + fork, all agent kinds
Sources/RestorableAgentSession.swift customResumeArguments {{cwd}} template custom/registered (vault) agents
cli/cmux.swift agentSurfaceResumeCommand (the cd in the auto-resume binding) all kinds, hook-driven auto-resume
cli/cmux.swift publishAgentSurfaceResumeBinding params["cwd"] metadata all kinds (binding/display cwd)

Scope / blast radius

  • Code: all agent kinds (resume + fork + custom template + CLI auto-resume binding). Not Claude-specific.
  • Observable behavior: changes only when the agent's recorded cwd diverges from its launch dir (cwd drift).
    • No drift (common case): launchCommand.workingDirectory == workingDirectory → byte-identical output, zero change. This is why all existing resume tests (gemini/cursor/copilot/codex/…), which set both values equal, pass unchanged.
    • launchCommand absent (older snapshots / process-scan snapshots): falls back to workingDirectory → identical to old behavior, no regression.
  • Direction: strictly toward "resume where you launched," which is correct for every agent — Claude requires it; codex/gemini/cursor/copilot/etc. all want the project dir they started in rather than a drifted one.

Tests

  • Commit 1 adds a failing regression test (testClaudeResumeCommandChangesToLaunchDirectoryNotDriftedCwd) that reproduces the bug (launch dir ≠ drifted cwd) and asserts the resume command cds to the launch dir.
  • Commit 2 applies the fix, making the test pass.

Verified locally with Xcode 26.5:

Test Case '...testClaudeResumeCommandChangesToLaunchDirectoryNotDriftedCwd' passed (0.002s)
Executed 1 test, with 0 failures
** TEST SUCCEEDED **

(Two-commit red/green structure per the repo's regression-test policy.)


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Summary by cubic

Resuming and forking now cd into the agent’s launch directory instead of a drifted cwd, fixing failed resumes (e.g., Claude Code “No conversation found”). Custom resume templates also honor cwd: .ignore by omitting the cwd.

  • Bug Fixes

    • Prefer launchCommand.workingDirectory over drifted workingDirectory in resume/fork builders and the {{cwd}} template.
    • Update CLI auto-resume to cd into the launch dir and publish that as cwd metadata.
    • Suppress {{cwd}} when a custom registration sets cwd: .ignore.
    • Applies to all agent kinds; changes only under cwd drift or when templates set cwd: .ignore.
  • Tests

    • Added claudeResumeCommandChangesToLaunchDirectoryNotDriftedCwd.
    • Added vault template tests for launch-dir precedence and cwd: .ignore, moved into a Swift Testing suite.

Written for commit 511cdae. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Corrected working-directory resolution for session restore so resume commands prefer the launch directory when available, and omit cwd when instructed to ignore it.
  • Tests

    • Added tests validating resume-command working-directory precedence, including preserved launch-directory behavior and suppressed cwd when ignored.

Resuming an agent (e.g. Claude Code) after the app reopens generated a
`cd <dir> && <agent> --resume <id>` command that changed into the agent's
*drifted* current working directory instead of the directory it was
launched in. Claude keys its session transcript to the launch directory,
so resuming elsewhere fails with "No conversation found with session ID".

This commit adds the regression test only; it fails against the current
behavior. The fix follows in the next commit.
The resume/fork command builders resolved the `cd` target as
`workingDirectory ?? launchCommand?.workingDirectory`, preferring the
agent's recorded (drifted) cwd over the directory it was launched in.
Claude Code keys its session transcript to the launch directory, so
resuming from a drifted cwd fails with "No conversation found with
session ID".

Flip the precedence to `launchCommand?.workingDirectory ?? workingDirectory`
in both the app-side builder (AgentResumeCommandBuilder.shellCommand and
the custom-resume {{cwd}} template) and the CLI auto-resume binding
(agentSurfaceResumeCommand + the binding cwd metadata). This matches the
precedence already used by AgentForkSupport.openCodeProbeWorkingDirectory.

Behavior is unchanged when the launch dir and recorded cwd agree (the
common case) or when launchCommand is absent (falls back to the recorded
cwd); it only differs under cwd drift, where launch dir is correct for
every agent kind. Makes the regression test from the previous commit pass.
Copilot AI review requested due to automatic review settings May 30, 2026 22:23
@vercel

vercel Bot commented May 30, 2026

Copy link
Copy Markdown

@TravelThePath is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR changes resume-command cwd resolution to prefer launchCommand?.workingDirectory across CLI and session builder code paths, and adds tests confirming launch-directory precedence and .ignore suppression for custom vault resume templates.

Changes

Working Directory Precedence for Resume Commands

Layer / File(s) Summary
Core cwd precedence changes
CLI/cmux.swift, Sources/RestorableAgentSession.swift
cwd selection at the CLI entry point and the workingDirectory parameter to agentSurfaceResumeShellCommand now prioritize launchCommand?.workingDirectory. AgentResumeCommandBuilder.shellCommand and customResumeArguments use launchCommand?.workingDirectory ?? workingDirectory when resolving the "cwd" template replacement (or "" when none).
Test validation
cmuxTests/RestorableAgentHookProviderResumeTests.swift
Added RestorableAgentResumeWorkingDirectoryTests with three tests: Claude resume uses the launch command’s working directory (not a drifted snapshot cwd); custom vault resume template uses launch directory when registration.cwd == .preserve; and resume template omits cwd when registration.cwd == .ignore.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit hops to check the path so keen,
LaunchCommand leads where once drift had been.
CD to the home the launcher chose,
Ignore when told — no stray cwd shows.
Resume now springs where roots were seen.

🚥 Pre-merge checks | ✅ 17 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (17 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: fixing agent resume behavior to use the launch directory instead of a drifted working directory.
Description check ✅ Passed The PR description is comprehensive and includes all required template sections: Summary (what changed and why), Testing (how tested), and Checklist items. It provides excellent context about the root cause, fix locations, scope, and test coverage.
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 Changes only reorder nil-coalescing operators on immutable String? values in static enum functions and struct methods. No MainActor, mutable Sendable, or UI context violations.
Cmux Swift Blocking Runtime ✅ Passed All changes are simple value precedence adjustments using nullish coalescing; no blocking synchronization (semaphores, waits, sleeps, delays, polling, locks) introduced in production code.
Cmux No Hacky Sleeps ✅ Passed All PR files are Swift files. The "runtime-no-hacky-sleeps" rule explicitly excludes Swift code, applying only to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts.
Cmux Algorithmic Complexity ✅ Passed PR changes working directory precedence via nullable coalescing on fixed-size data (command args, template words, 5-key dict). No nested scans or unbounded operations on user-scalable collections.
Cmux Swift Concurrency ✅ Passed PR changes only working directory precedence logic and adds modern Swift Testing; no legacy async patterns (DispatchQueue, Combine, completion-handlers, fire-and-forget Tasks) introduced.
Cmux Swift @Concurrent ✅ Passed All changes are in synchronous functions performing simple data precedence reordering. No @concurrent, async, or actor-isolated functions modified.
Cmux Swift File And Package Boundaries ✅ Passed Focused bug fix touching existing oversized files incidentally with minimal changes (+0 and +3 net lines). Matches allowed case for small, localized bug fixes to large files.
Cmux Swift Logging ✅ Passed PR makes only logic changes to working directory precedence without adding any print, debugPrint, dump, or NSLog statements in production code, and includes no problematic logging in tests.
Cmux User-Facing Error Privacy ✅ Passed PR changes are internal logic modifications to working directory handling in resume commands; no user-facing errors, alerts, or messages are introduced or modified that could violate privacy rules.
Cmux Full Internationalization ✅ Passed PR removes SSH PTY and diff viewer code with matching catalog deletions; no new user-facing strings or locale entries added. Test additions are exempt.
Cmux Swiftui State Layout ✅ Passed PR contains only backend/business logic changes (CLI, session management, tests) with no SwiftUI constructs. The custom check is not applicable.
Cmux Architecture Rethink ✅ Passed No timing/blocking/locks/observers/side-channels introduced. Correctness fix with clear ownership (AgentResumeCommandBuilder) and invariant (prefer launch dir).
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only modifies working directory logic for agent session restoration; no standalone cmux-owned windows (NSWindow, NSPanel, NSWindowController, Window, WindowGroup) are created or materially changed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 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 and usage tips.

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR updates resume command generation to prefer the launch command’s captured working directory over a potentially drifted session working directory, and adds a regression test to lock in the behavior.

Changes:

  • Prefer launchCommand.workingDirectory over workingDirectory when building resume commands and related parameter maps.
  • Update CLI resume hook parameter construction to align with the new cwd precedence.
  • Add a test ensuring Claude resume commands cd into the launch directory rather than a nested/drifted cwd.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
cmuxTests/RestorableAgentHookProviderResumeTests.swift Adds a regression test for cwd precedence in Claude resume command generation.
Sources/RestorableAgentSession.swift Changes resume/fork command cwd resolution to prioritize launchCommand.workingDirectory.
CLI/cmux.swift Aligns CLI hook payload and resume command building with updated cwd precedence.
Comments suppressed due to low confidence (1)

cmuxTests/RestorableAgentHookProviderResumeTests.swift:10

  • This test appears to be placed under extension SocketListenerAcceptPolicyTests, which doesn’t match the file name (RestorableAgentHookProviderResumeTests.swift) or the behavior under test (resume command cwd precedence). Moving it into the most relevant XCTestCase (e.g., the resume/hook provider test case) will make test organization clearer and reduce the chance of future misclassification when browsing or running focused test suites.
extension SocketListenerAcceptPolicyTests {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift Outdated
Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift Outdated
@greptile-apps

greptile-apps Bot commented May 30, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the session-resume bug where the cd target in generated resume/fork shell commands used the agent's drifted current working directory instead of its launch directory. The fix flips the ?? precedence from workingDirectory ?? launchCommand?.workingDirectory to launchCommand?.workingDirectory ?? workingDirectory in four places across two production files, and also adds a secondary fix to honor cwd: .ignore in the customResumeArguments template path (previously that flag only suppressed the cd prefix, not the {{cwd}} template variable).

  • Precedence is flipped in AgentResumeCommandBuilder.shellCommand, the {{cwd}} template in customResumeArguments, and both CLI auto-resume sites (agentSurfaceResumeCommand / publishAgentSurfaceResumeBinding).
  • Three new Swift Testing @Test cases are added: one covering the main Claude resume path, one covering the custom-vault template path, and one verifying that cwd: .ignore suppresses the {{cwd}} substitution.

Confidence Score: 5/5

Safe to merge. The fix is a pure precedence swap applied consistently across all four resume/fork command-building sites, with no new state, no new branches, and no observable effect when launchCommand is absent or when the two directories are equal.

All four changed sites perform an identical, trivially correct operand swap. The zero-drift path is byte-identical to the old code, the no-launchCommand path falls back to the existing value, and the drift path (the only new behavior) is now covered by three new regression tests that document the before/after contract.

No files require special attention. The test for cwd: .ignore uses slightly imprecise assertions, but this is limited to the test file and does not affect production behavior.

Important Files Changed

Filename Overview
Sources/RestorableAgentSession.swift Two targeted precedence flips: shellCommand now prefers launchCommand?.workingDirectory, and customResumeArguments adds a resolvedCwd local that also respects cwd: .ignore before substituting {{cwd}} in vault-agent templates. Both changes are correct.
CLI/cmux.swift Two parallel precedence flips in the CLI auto-resume path: agentSurfaceResumeCommand now passes launchCommand?.workingDirectory ?? workingDirectory, and publishAgentSurfaceResumeBinding prefers launchCommand?.workingDirectory for the cwd metadata field. Both are consistent with the core fix.
cmuxTests/RestorableAgentHookProviderResumeTests.swift Adds three Swift Testing @test cases covering: (1) Claude resume uses launch dir, (2) custom vault template resolves {{cwd}} to launch dir, (3) cwd: .ignore suppresses both dirs. The cwd: .ignore test assertions are slightly weak — they verify neither dir string appears in the command but do not assert that resumeCommand is nil, which is the precise expected outcome when template resolution fails.

Sequence Diagram

sequenceDiagram
    participant Hook as Agent Hook / Snapshot
    participant Builder as AgentResumeCommandBuilder
    participant CLI as CLI (cmux.swift)
    participant Shell as Terminal

    Hook->>Builder: resumeShellCommand(kind, sessionId, launchCommand, workingDirectory)
    Note over Builder: OLD: workingDirectory ?? launchCommand?.workingDirectory<br/>NEW: launchCommand?.workingDirectory ?? workingDirectory
    Builder->>Builder: shellCommand() resolves cwd
    Builder-->>CLI: shell command string (cd to launch dir)

    Hook->>CLI: agentSurfaceResumeCommand(launchCommand, workingDirectory)
    Note over CLI: OLD: workingDirectory ?? launchCommand?.workingDirectory<br/>NEW: launchCommand?.workingDirectory ?? workingDirectory
    CLI->>CLI: publishAgentSurfaceResumeBinding (params["cwd"])
    Note over CLI: OLD: normalizedHookValue(cwd) ?? normalizedHookValue(launchCommand?.workingDirectory)<br/>NEW: normalizedHookValue(launchCommand?.workingDirectory) ?? normalizedHookValue(cwd)
    CLI-->>Shell: "cd '/launch/dir' && claude --resume session-id"
Loading

Reviews (2): Last reviewed commit: "fix(agent-resume): respect cwd: .ignore ..." | Re-trigger Greptile

Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11478591de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift Outdated

@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/RestorableAgentSession.swift`:
- Line 673: The resume template currently backfills {{cwd}} using
launchCommand?.workingDirectory ?? workingDirectory which ignores registrations
that set cwd: .ignore; update the resolution to consult
SessionEntry.resumeWorkingDirectory (the source of truth) instead of falling
back to launchCommand or workingDirectory so registrations that explicitly
ignore cwd suppress cwd usage in resume flows—i.e., use
SessionEntry.resumeWorkingDirectory (and treat its "ignored" value as nil/empty)
when computing the normalized cwd for RestorableAgentSession template
substitution.
🪄 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

Run ID: 57af8d87-5750-4e28-83c6-226b1c86b089

📥 Commits

Reviewing files that changed from the base of the PR and between 24424e3 and 1147859.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • Sources/RestorableAgentSession.swift
  • cmuxTests/RestorableAgentHookProviderResumeTests.swift

Comment thread Sources/RestorableAgentSession.swift Outdated
customResumeArguments backfilled {{cwd}} from launchCommand?.workingDirectory
even when the registration set cwd: .ignore, bypassing the explicit directive
that shellCommand already honored. Compute the cwd up front and suppress it for
.ignore registrations, matching shellCommand's guard.

Move the new resume regression test to a Swift Testing suite per AGENTS.md and
add coverage for the customResumeArguments {{cwd}} path (launch-dir precedence
and .ignore suppression).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 511cdaec32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +669 to +671
let resolvedCwd = registration.cwd == .ignore
? nil
: normalized(launchCommand?.workingDirectory ?? workingDirectory)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't blank {{cwd}} when only cd-prefix is ignored

For custom Vault agents, cwd is documented as controlling whether Vault should cd before running resumeCommand, not whether the {{cwd}} template value is available. With cwd: .ignore and a template like tool --resume {{sessionId}} --dir {{cwd}}, this makes the replacement empty; the later empty-token guard returns [], so snapshot.resumeCommand becomes nil. Agents that must avoid a shell cd but still need the saved project path as an argv flag can no longer resume; keep suppressing the prefix in shellCommand but continue filling {{cwd}} from the launch/working directory.

Useful? React with 👍 / 👎.

@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 `@cmuxTests/RestorableAgentHookProviderResumeTests.swift`:
- Around line 726-730: The test should explicitly assert the nil contract for
resumeCommand when cwd: .ignore and the template contains {{cwd}}; locate the
test that sets cwd: .ignore and currently checks let command =
snapshot.resumeCommand ?? "" and replace or augment that check with an explicit
nil assertion against snapshot.resumeCommand (e.g., XCTAssertNil or the
project’s `#expect` nil form) so the contract that resumeCommand must be nil in
this case is enforced rather than only checking that the string doesn’t contain
paths.
🪄 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

Run ID: 8e116bf1-01e5-4996-908c-d3753121420c

📥 Commits

Reviewing files that changed from the base of the PR and between 1147859 and 511cdae.

📒 Files selected for processing (2)
  • Sources/RestorableAgentSession.swift
  • cmuxTests/RestorableAgentHookProviderResumeTests.swift

Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Sources/RestorableAgentSession.swift
Comment thread cmuxTests/RestorableAgentHookProviderResumeTests.swift
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.

2 participants