Skip to content

Fix agent session resume: cwd-drift + post-kill cwd, DRY resume engine, lifecycle/fork tests - #5312

Merged
lawrencecchen merged 9 commits into
mainfrom
task-resume-engine-dry
Jun 4, 2026
Merged

lawrencecchen merged 9 commits into
mainfrom
task-resume-engine-dry

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Combined resume-engine PR (supersedes #5300, which is the first two commits here).

Fixes

  • cwd-drift on resume (the original "No conversation found" bug): per-agent cwd-namespacing so a resumed agent cds into the directory its session is actually filed under, not the drift-prone runtime cwd. Unifies the two parallel resume mechanisms (RestorableAgentSessionIndex + SurfaceResumeBinding).
  • Post-kill cwd: after a resumed agent exits, the surface returns to the session's launch dir (the launcher's inner cd no longer leaks).
  • .ignore cwd policy respected on the post-kill return.
  • Custom Vault agents preserve their runtime cwd on restore (honoring .preserve).

Refactor (DRY)

  • Shared value-struct builders in CMUXAgentLaunch: AgentResumeWorkingDirectory, AgentResumeArgv, AgentCwdNamespacing — one source of truth for the app + cli resume paths (removed ~95 duplicated lines). Converted from namespace-enums to value structs per package-design review.

Tests

  • Lifecycle: newest-wins, killed-no-restore (liveness gate), idempotent reloads.
  • Fork: per-agent fork verb + cwd, non-fork agents emit none, fork-after-restore stays forkable via the UI path.
  • Custom-agent runtime-cwd preservation.
  • All CMUXAgentLaunch package tests now run as a real CI gate.

Independent of the codex surface-jumble work (#5331, surface-mapping axis).

🤖 Generated with Claude Code


Note

Medium Risk
Touches core session restore/resume paths for many agent kinds and both app and CLI; behavior changes are intentional but broad, mitigated by extensive new tests.

Overview
Fixes agent resume failing with “No conversation found” when the hook-reported cwd drifts (e.g. agent launched in repo root, later cd into a worktree). Directory-namespaced agents (Claude, Gemini, Grok, …) now resume from the launch working directory; id-keyed agents (Codex, OpenCode, Amp, …) still use the runtime cwd. The same rules apply to RestorableAgentSessionIndex and CLI surface.resume.set bindings.

Extracts shared logic into CMUXAgentLaunch: AgentCwdNamespacing, AgentResumeWorkingDirectory, and AgentResumeArgv (replacing ~95 duplicated lines in the app and cmux-cli). RestorableAgentKind.cwdNamespacing delegates to the shared classifier.

After a resumed agent exits, the outer login shell cds back to the session directory (resume launcher scripts and surface resume bindings); .ignore custom agents skip that forced return.

Adds regression tests for cwd drift, fork/resume lifecycle, custom Vault .preserve cwd, and package unit tests as a CI gate.

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

Summary by CodeRabbit

  • Bug Fixes

    • Resume now reliably restores the agent’s launch working directory and returns the shell to that directory when resuming sessions.
  • New Features

    • Two directory-restoration modes: session-recorded cwd or launch-directory-pinned, chosen per agent type.
    • More robust resume command construction that correctly handles wrapper launchers and built-in agent kinds.
  • Tests

    • Added unit and integration tests covering resume commands, working-directory resolution, and session restore edge cases.
  • Documentation

    • Added design notes addressing a session-to-surface restoration bug and proposed fixes.

lawrencecchen and others added 3 commits June 3, 2026 12:51
Two regression tests through RestorableAgentSessionIndex.load that fail
against the current resolver:

- Claude: when the transcript exists but encodes to neither candidate and
  the config probe can't confirm, restore must prefer the launch cwd, not
  the drifted recorded cwd.
- A directory-namespaced non-Claude agent (Gemini) with a drifted cwd must
  resolve to the launch cwd; today the Claude-only gate returns the drift.

These reproduce the "No conversation found with session ID" failure on
session restore for cwd-namespaced agents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Agent session stores are namespaced by the directory the agent was
launched in; cmux was resuming from the agent's drifted runtime cwd (a
worktree it cd'd into mid-session), so `<agent> --resume <id>` looked in
the wrong namespace and failed with "No conversation found with session
ID". Two independent paths carried the drift, fixed via one shared engine:

New shared package logic (Packages/CMUXAgentLaunch/AgentResumeWorkingDirectory):
classifies each agent kind (cwd-in-file vs directory-namespaced) and resolves
the resume working directory, preferring the launch cwd for directory-
namespaced agents (Claude, Gemini, Cursor, Grok, Pi, Qoder) and keeping the
runtime cwd for id-keyed agents (Codex, OpenCode, Amp, ...). Linked by both
the app and the standalone cmux-cli, so there is one source of truth.

1. CLI surface resume binding (publishAgentSurfaceResumeBinding) — the path
   that re-runs the agent on a restored surface — now pins the binding to the
   launch cwd via the shared resolver instead of baking the drifted runtime
   cwd. This is the path that actually failed on a build that already had the
   Claude-only #5154 fix.
2. App-side resolver (restorableWorkingDirectory) generalized beyond Claude
   via RestorableAgentKind.cwdNamespacing, which now delegates to the shared
   classifier. Claude still verifies against the transcript first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-agent resume-verb switch was duplicated across the app target
(AgentResumeCommandBuilder.resumeArguments) and the standalone cmux-cli
target (agentSurfaceResumeArguments + helpers), because cmux-cli cannot
import the app's Sources. Extract the launcher pre-resolution and the
built-in-kind verb switch into one pure builder, AgentResumeArgv, in the
shared CMUXAgentLaunch package (linked by app + cli + tests).

Behavior-preserving: the app and CLI switches were verified identical, and
the shared builder is golden-tested per built-in kind and launcher wrapper
(AgentResumeArgvTests). The app keeps its Vault/.custom path locally
(CmuxVaultAgentRegistration lives in the app target); only the built-in and
wrapper resolution is shared. Deletes ~95 lines of duplicated CLI helpers.

Stacks on the resume cwd-drift fix (task-session-restore-resume / #5300).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 4, 2026 8:04am
cmux-staging Building Building Preview, Comment Jun 4, 2026 8:04am

@coderabbitai

coderabbitai Bot commented Jun 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bb4819df-413c-4787-b398-e2a284c75ddd

📥 Commits

Reviewing files that changed from the base of the PR and between 53697db and 9b80482.

📒 Files selected for processing (3)
  • Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift
  • Sources/RestorableAgentSession.swift
  • plans/feat-codex-surface-jumble/DESIGN.md

📝 Walkthrough

Walkthrough

This PR centralizes cwd-namespace policy and deterministic resume argv construction. It adds AgentCwdNamespacing, AgentResumeArgv, and AgentResumeWorkingDirectory, wires them into session restoration and CLI resume script generation (including working-directory-aware return scripts), and adds tests and fixtures covering restoration/fork edge-cases.

Changes

Agent Resume Working Directory Namespacing

Layer / File(s) Summary
CWD Namespace Policy Enum
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentCwdNamespacing.swift
AgentCwdNamespacing enum defines two session-directory strategies: byDirectory and cwdInFile.
Resume Working Directory Selector
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeWorkingDirectory.swift, Packages/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentResumeWorkingDirectoryTests.swift
AgentResumeWorkingDirectory maps kinds to namespacing policies, normalizes recorded/runtime/launch cwd inputs, and selects the appropriate directory per policy; tests assert behavior across known kinds and fallback handling.
Resume Argv Resolution
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift, Packages/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentResumeArgvTests.swift
AgentResumeArgv deterministically builds resume argv by resolving wrapper launchers (e.g., claudeTeams, passthrough, one-shot wrappers) and then per-kind built-in resume commands using sanitizer-preserved tail args; tests validate patterns and executable-path override.
CLI Surface Resume Integration
CLI/cmux.swift
CLI resume binding now computes resumeWorkingDirectory and passes it into script generation; resume argv assembly replaced with AgentResumeArgv().launcherResolution(...) and prior per-kind helpers removed.
Session Restoration Integration
Sources/RestorableAgentSession.swift
resumeArguments delegates to AgentResumeArgv; resumeStartupCommand passes a computed workingDirectory into launcher script writing; restorableWorkingDirectory replaced with registration- and namespace-aware logic, including Claude transcript verification gating.
Shell Script Working Directory Support
Sources/SessionPersistence.swift
TerminalStartupReturnShellScript.commandThenReturnLines accepts an optional workingDirectory and conditionally prepends a cd step before exec -l; launcher-script generation supplies binding.cwd.
Shared Agent Kind Extension
Sources/RestorableAgentTypes.swift
Added RestorableAgentKind.cwdNamespacing computed property delegating to AgentResumeWorkingDirectory.
Integration & Fixture Tests
cmuxTests/AgentSessionAutoResumeSettingsTests.swift, cmuxTests/RestorableAgentSessionIndexTests.swift
Added tests for fork behavior, return-to-launch-cwd script content, cwd-policy correctness, custom Vault .preserve, per-agent fork verbs, non-fork-capable agents, session selection/idempotence, dead-process refusal; introduced driftedAgentHookRecord and generalized writeHookStore.
Design Doc
plans/feat-codex-surface-jumble/DESIGN.md
Adds a design document describing related codex surface jumble diagnostics and staged fix plan (documentation-only).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

"🐰 I hop between dirs both near and far,
Pin the launch-cwd and follow the star,
Argv refactored, wrappers all tamed,
Tests check the paths where sessions are named,
New restore steps hum — a rabbit's small cheer!"

🚥 Pre-merge checks | ✅ 17 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.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 directly references the main changes: agent session resume fixes (cwd-drift, post-kill cwd), DRY refactoring of resume engine, and new lifecycle/fork tests.
Description check ✅ Passed The description covers the required template sections: Summary (what changed and why), Testing (test coverage added), and includes the review trigger block and checklist, though Testing lacks specific manual verification details.
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 Pure value types marked Sendable with no mutable properties. TerminalStartupReturnShellScript marked nonisolated. No MainActor or actor isolation violations.
Cmux Swift Blocking Runtime ✅ Passed PR introduces no new blocking synchronization. New CMUXAgentLaunch types are pure value structs; modified SessionPersistence generates shell scripts without blocking primitives.
Cmux No Hacky Sleeps ✅ Passed PR modifies only Swift and Markdown files; rule applies to TypeScript/JavaScript/shell only (Swift covered by separate rule).
Cmux Algorithmic Complexity ✅ Passed New value-struct builders use O(1) switch-statement dispatch called once per user action, not in loops over scalable collections. Existing nested loops use proper caching.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns. New value structs are pure types; refactored code uses modern patterns; pre-existing async code unchanged.
Cmux Swift @Concurrent ✅ Passed Three new async functions use Task.detached(priority: .utility) providing explicit actor hops from MainActor callers; file I/O operations are properly bounded; @concurrent not required.
Cmux Swift File And Package Boundaries ✅ Passed New CMUXAgentLaunch package extracts pure value logic shared between app and CLI targets. Both affected files show net line reductions (-112, -77). Under 250-line growth threshold for oversized files.
Cmux Swift Logging ✅ Passed New production source files contain no print/debugPrint/dump/NSLog. No improper Logger declarations, ad-hoc file logging, or exposed secrets detected.
Cmux User-Facing Error Privacy ✅ Passed No user-facing errors, alerts, or command output added. PR refactors resume logic into shared structs without exposing vendor names, env vars, session IDs, credentials, or upstream details.
Cmux Full Internationalization ✅ Passed No user-facing strings added without localization. All changes are test files, documentation, or technical tokens (command flags, agent names). No xcstrings, Info.plist, or web/messages modifications.
Cmux Swiftui State Layout ✅ Passed No SwiftUI state or layout violations found. PR modifies only non-UI agent resume logic, data types, and shell script utilities with no View components, state management, or GeometryReader usage.
Cmux Architecture Rethink ✅ Passed Pure value structs with no timing, dispatch, locks, or split lifecycle. Clean DRY refactor with single ownership and clear invariants.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR makes no NSWindow/NSPanel/NSWindowController or SwiftUI Window/WindowGroup changes; it's a backend refactor of agent resume logic with no standalone cmux-owned window creation.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-resume-engine-dry

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.

@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: 2cfdd0cc8f

ℹ️ 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".

/// (``launcherResolution(launcher:sessionId:executablePath:arguments:)``) is checked first, then the
/// per-kind verb (``builtInKind(kind:sessionId:executablePath:arguments:)``). Callers that also
/// support custom Vault agents slot that resolution between the two.
public enum AgentResumeArgv {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace the namespace enum with an injectable type

The /workspace/cmux AGENTS.md package-design rule explicitly forbids no-case namespace enums (enum Foo { static func ... }) for new package code because they bypass DI/test seams; this new AgentResumeArgv type is exactly a static-only namespace in Packages/. Please make this a value type with instance methods that callers construct/inject (or otherwise follow the documented package discipline) instead of adding another static namespace API.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — converted both AgentResumeArgv and AgentResumeWorkingDirectory from no-case namespace enums to stateless value structs with public init() + instance methods; all callers (app, cli, tests) now construct an instance. 61 package tests still pass (51a65dc).

— Claude Code

@greptile-apps

greptile-apps Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the "No conversation found" resume failure caused by cwd drift (agents cd-ing into worktrees mid-session) by introducing AgentResumeWorkingDirectory as the single authority on whether to pin the launch cwd or keep the runtime cwd. Also fixes the post-kill shell landing directory and DRYs away ~95 duplicated lines of launcher/kind resume-argv logic into AgentResumeArgv.

  • Cwd-drift fix: directory-namespaced agents (Claude, Grok, Gemini, etc.) now resume from the launch cwd captured at session start, not the drift-prone hook-reported cwd; id-keyed agents (Codex, Amp, etc.) keep the runtime cwd.
  • Post-kill cwd: TerminalStartupReturnShellScript.commandThenReturnLines gains an optional workingDirectory parameter that emits an outer-shell cd before exec -l, returning the user to the session's directory after killing a resumed agent.
  • DRY resume engine: AgentResumeArgv and AgentResumeWorkingDirectory in CMUXAgentLaunch replace two parallel implementations (app + CLI), backed by lifecycle, fork, cwd, and custom-agent tests.

Confidence Score: 5/5

Safe to merge. The cwd-drift fix is well-reasoned, the DRY refactor preserves all existing argument shapes, and the new shell script cd is properly quoted and fail-safe.

The refactored resume engine is a mechanical consolidation of two parallel switch tables into AgentResumeArgv and AgentResumeWorkingDirectory, both of which are stateless value types with deterministic outputs verified by the new tests. All changed behavior is covered by the new lifecycle, fork, cwd-drift, custom-agent, and return-to-cwd tests.

No files require special attention.

Important Files Changed

Filename Overview
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeWorkingDirectory.swift New stateless value struct that is the single source of truth for per-agent cwd policy; classification table and resolve() logic are clean and well-documented.
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift New stateless value struct consolidating all per-launcher and per-kind resume argv construction; mirrors the old split implementations with correct argument ordering and quoting.
Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentCwdNamespacing.swift Tiny enum expressing the two cwd-keying strategies; well-commented with rationale for each case.
Sources/SessionPersistence.swift Adds optional workingDirectory to commandThenReturnLines; new outer-shell cd is properly quoted via singleQuoted() and guarded with
Sources/RestorableAgentSession.swift AgentResumeCommandBuilder now delegates to AgentResumeArgv; restorableWorkingDirectory extended with cwdNamespacing dispatch and custom-agent cwd policy; claudeVerifiedRestorableWorkingDirectory extracted and now returns nil when transcript is unverifiable, letting the caller prefer launchCwd.
Sources/RestorableAgentTypes.swift Adds cwdNamespacing computed property that delegates to the shared CMUXAgentLaunch classifier, ensuring app-side and CLI apply identical policy.
CLI/cmux.swift publishAgentSurfaceResumeBinding now computes resumeWorkingDirectory via AgentResumeWorkingDirectory; ~100 lines of duplicated launcher/kind switch removed in favour of AgentResumeArgv.
cmuxTests/RestorableAgentSessionIndexTests.swift Adds tests for cwd-drift fix (Claude + Gemini), transcript-unverifiable fallback, lifecycle (newest-wins, killed-process gate, idempotent reload), fork verb + cwd, custom-agent runtime-cwd preservation, and shared-classifier round-trip.
cmuxTests/AgentSessionAutoResumeSettingsTests.swift Adds testRestoredSessionRemainsForkable and testResumeLauncherReturnsToLaunchCwdAfterAgentExits covering new behavioral guarantees introduced by this PR.
plans/feat-codex-surface-jumble/DESIGN.md Internal planning document for a distinct follow-on bug (codex surface-mapping axis, PR #5331); no production code change.

Sequence Diagram

sequenceDiagram
    participant Hook as Agent Hook
    participant CLI as cmux CLI
    participant ARW as AgentResumeWorkingDirectory
    participant ARA as AgentResumeArgv
    participant Script as Shell Script Store
    participant Shell as Outer Shell

    Note over Hook,Shell: Session resume flow (post-PR)

    Hook->>CLI: publishAgentSurfaceResumeBinding(kind, cwd, launchCommand)
    CLI->>ARW: resolve(kind, runtimeCwd, launchWorkingDirectory)
    alt cwdInFile (Codex/Amp/…)
        ARW-->>CLI: runtimeCwd (id-keyed: cwd lives in session file)
    else byDirectory (Claude/Grok/Gemini/…)
        ARW-->>CLI: launchCwd (pinned to session namespace)
    end
    CLI->>ARA: launcherResolution(launcher, sessionId, executablePath, arguments)
    alt cmux wrapper launcher (claudeTeams/omo/codexTeams)
        ARA-->>CLI: .resolved([cmd argv])
    else plain agent launcher
        ARA-->>CLI: .passthrough
        CLI->>ARA: builtInKind(kind, sessionId, …)
        ARA-->>CLI: [cmd argv] or nil
    end
    CLI->>Script: writeLauncherScript(command, workingDirectory: resolvedCwd)
    Script-->>Shell: zsh script with cd to resolvedCwd + agent command + outer cd back + exec -l

    Note over Shell: Agent runs, user kills it
    Shell->>Shell: cd resolvedCwd (outer shell lands in session dir, not surface default)
Loading

Reviews (7): Last reviewed commit: "Address CodeRabbit on the combined diff:..." | Re-trigger Greptile

Comment on lines +86 to +91
// One-shot wrappers have no resumable form.
#expect(
AgentResumeArgv.launcherResolution(
launcher: "omx", sessionId: "SID", executablePath: nil, arguments: ["cmux", "omx"]
) == .resolved(nil)
)

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.

P2 The launcherWrappers test verifies omx returns .resolved(nil) but never exercises the omc branch. Both live behind the same case "omx", "omc" arm, so a future refactor that accidentally splits or reorders those cases would go undetected.

Suggested change
// One-shot wrappers have no resumable form.
#expect(
AgentResumeArgv.launcherResolution(
launcher: "omx", sessionId: "SID", executablePath: nil, arguments: ["cmux", "omx"]
) == .resolved(nil)
)
// One-shot wrappers have no resumable form.
#expect(
AgentResumeArgv.launcherResolution(
launcher: "omx", sessionId: "SID", executablePath: nil, arguments: ["cmux", "omx"]
) == .resolved(nil)
)
#expect(
AgentResumeArgv.launcherResolution(
launcher: "omc", sessionId: "SID", executablePath: nil, arguments: ["cmux", "omc"]
) == .resolved(nil)
)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added — there's now an explicit omc assertion next to omx so both one-shot wrapper cases in the shared arm are exercised (51a65dc).

— Claude Code

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

No issues found across 4 files

Re-trigger cubic

Bug: after a resumed agent exits, the surface landed in the default surface
cwd, not the session's launch directory. The resume command's `cd` runs
inside the `-lic` child shell (TerminalStartupReturnShellScript), so the
outer `exec -l` login shell stayed in the script's launch cwd (nil ->
baseConfig default). Fix: thread the resume working directory through the
launcher and `cd` the outer shell back into it before `exec -l`, so killing
a resumed agent leaves you in the session's directory.

Tests (cmuxTests):
- testResumeLauncherReturnsToLaunchCwdAfterAgentExits: the launcher returns
  the outer shell to the launch dir before exec -l (and not when no dir).
- testReplacementRestoresNewestSessionForSurface: spawn/replace -> restore
  picks the newest session for the surface, not the stale one.
- testRestoreIsIdempotentAcrossReloads: repeated reopen restores the same
  session + command.
- testKilledSessionWithDeadProcessDoesNotRestore: a session whose recorded
  process is dead is dropped from the restore index (via processArgumentsProvider).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread Sources/RestorableAgentSession.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: b101eb22ba

ℹ️ 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 Sources/RestorableAgentSession.swift Outdated
temporaryDirectory: temporaryDirectory,
returnToLoginShell: true
returnToLoginShell: true,
workingDirectory: workingDirectory ?? launchCommand?.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 Preserve cwd-ignore during auto-resume return shell

When auto-resuming a Vault/custom agent whose registration has cwd == .ignore, shellCommand deliberately omits the cwd prefix, and scanners store snapshot.workingDirectory as nil, but this new fallback still passes launchCommand?.workingDirectory into the launcher script. In that scenario, after the resumed agent exits, TerminalStartupReturnShellScript now cds the outer login shell into the ignored cwd, so a cwd-ignored agent changes the terminal directory anyway; use the same cwd policy here instead of falling back to the launch command cwd for ignored registrations.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — the outer-shell cd is now gated on registration.cwd != .ignore, so the post-kill landing matches the resume command's own cwd policy (51a65dc).

— Claude Code

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

1 issue found across 4 files (changes from recent commits).

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

Re-trigger cubic

Comment thread Sources/RestorableAgentSession.swift Outdated
… cwd, fork tests

Review feedback (#5312):
- Codex P1: AgentResumeArgv + AgentResumeWorkingDirectory were no-case namespace
  enums, which the package design discipline forbids for new package code. Convert
  both to stateless value structs with init() + instance methods; callers construct
  an instance. (61 package tests still green.)
- Codex/Cursor: the post-kill return-to-launch-dir cd bypassed the `.ignore` cwd
  policy. Match the resume command's own cd: when registration.cwd == .ignore the
  agent resumes from the current dir (no cd), so the post-exit shell must not force
  the launch dir either.

Fork coverage (requested): forked sessions must resume properly per agent.
- testForkCommandUsesPerAgentVerbAndSessionCwd: codex (`fork <id>`) and opencode
  (`--session <id> --fork`) forks use the right verb, cd into the session dir, and
  are launchable (claude fork already covered).
- testNonForkAgentsProduceNoForkCommand: gemini/grok/amp/cursor still resume but
  emit no (malformed) fork command, pinning the fork-capable set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review (#5300 P2): a custom Vault agent defaults to cwd: .preserve and its
resume template can expand {{cwd}}, but restorableWorkingDirectory classified
the unknown custom id as .byDirectory and returned the launch dir — so a custom
agent that cd'd into a worktree would resume in the wrong directory. Thread the
registration into restorableWorkingDirectory and keep the runtime cwd whenever a
registration is present (covers both .preserve and .ignore).

(The sibling antigravity P2 is already handled: .custom("antigravity").rawValue
is "antigravity", which the shared cwd classifier lists as .cwdInFile.)

Test: testCustomVaultAgentPreservesRuntimeCwdOnRestore — a drifted custom agent
restores into its runtime cwd, not the launch dir.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/RestorableAgentSession.swift">

<violation number="1" location="Sources/RestorableAgentSession.swift:1304">
P1: All registry-backed agents now force runtime cwd, which regresses Grok/Pi resume when cwd drifts from launch directory.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/RestorableAgentSession.swift Outdated
Confirms fork-after-restore works end-to-end: restoreSessionSnapshot captures
the agent snapshot into restoredAgentSnapshotsByPanelId, and the UI fork action
(Workspace.forkableAgentSnapshot(forPanelId:)) resolves it back with a valid
per-agent fork command + launchable fork startup input. (Already wired; this
pins it so a future change can't silently break forking a resumed session.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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: 450f30298d

ℹ️ 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 Sources/RestorableAgentSession.swift Outdated
Comment on lines +1304 to +1305
if registration != nil {
return recordedCwd ?? launchCwd

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 Preserve launch cwd for registry-owned built-ins

When restoring registry-owned built-ins such as Grok or Pi, registration is non-nil because those built-ins are supplied by the Vault registry rather than RestorableAgentKind.allCases; this early return therefore bypasses the kind.cwdNamespacing == .byDirectory branch below. If the agent was launched in /repo and later cd'd into a worktree, the hook record's cwd is the drifted runtime cwd, so the resume command is run from the wrong directory and directory-namespaced agents can fail to find the conversation. Apply the cwd-namespacing decision before the generic custom-registration shortcut, or exclude registry-owned by-directory built-ins from this return.

Useful? React with 👍 / 👎.

lawrencecchen added a commit that referenced this pull request Jun 4, 2026
Root cause (adversarially-verified): the CLI agent launchers stamped the
operator's FOCUSED pane into CMUX_WORKSPACE_ID/CMUX_SURFACE_ID instead of the
launch surface, while leaving CMUX_PANEL_ID/CMUX_TAB_ID at the launch surface.
So a codex launched in surface B while surface A is focused recorded surface A
and restored into the wrong surface after reload (the "jumble"). Flows through
the codex-family omx/codex-teams launchers; plain `codex` is unaffected because
its env comes from the matched per-surface terminal startup env.

- New shared resolver CMUXAgentLaunch/AgentSpawnIdentity: prefer the launcher's
  OWN surface/workspace identity, falling back to the focused pane only when the
  launcher has none. Pure value logic with golden tests.
- configureTmuxCompatEnvironment (CLI/cmux.swift): resolve via AgentSpawnIdentity
  so omx/claude-teams stamp the launch surface, keeping CMUX_SURFACE_ID matched
  with the inherited CMUX_PANEL_ID.
- runCodexTeams watcher args: derive the root workspace/surface from the launch
  env, not focusedContext, so the codex-teams watcher records the right surface.

Independent of the cwd-resolution work (#5300/#5312): that is the directory axis,
this is the surface-mapping axis. Full root cause + e2e survives-reload test plan
in plans/feat-codex-surface-jumble/DESIGN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen lawrencecchen changed the title Extract shared agent resume-argv builder (DRY resume engine) Fix agent session resume: cwd-drift + post-kill cwd, DRY resume engine, lifecycle/fork tests Jun 4, 2026
@lawrencecchen
lawrencecchen changed the base branch from task-session-restore-resume to main June 4, 2026 07:29

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 53697db. Configure here.

Comment thread CLI/cmux.swift

@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: 53697db65e

ℹ️ 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".

@@ -0,0 +1,109 @@
import CMUXAgentLaunch

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 Add the new package tests to CI

These new CMUXAgentLaunch package tests are not exercised by the current workflow: I checked .github/workflows/ci.yml and the SwiftPM package-test loop still only lists CmuxFoundation, CmuxSettings, CmuxSettingsUI, and CmuxSocketControl, while the workflow comment says the cmux-unit scheme does not run SPM package test targets. As a result, regressions in the newly extracted resume argv/cwd logic can pass CI even though the tests exist; add CMUXAgentLaunch to that package list (or otherwise wire this package’s tests into CI).

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: 2

🤖 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 `@Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift`:
- Around line 159-160: Normalize arguments.first before choosing the fallback so
whitespace-only argv[0] doesn't bypass fallback: in AgentResumeArgv.swift update
the executable assignment to use normalized(executablePath) ??
normalized(arguments.first) ?? fallbackExecutable (and keep tail computed the
same using Array(arguments.dropFirst())). This ensures any candidate argv[0] is
run through normalized(...) before falling back to fallbackExecutable.

In `@Sources/RestorableAgentSession.swift`:
- Around line 1300-1306: The code for custom registrations unconditionally
returns recordedCwd ?? launchCwd which violates cwd: .ignore; update the branch
that handles non-nil registration (in the method that computes
resumeWorkingDirectory/produces SessionRestorableAgentSnapshot.workingDirectory)
to check the registration.cwd policy and return nil when registration.cwd ==
.ignore, otherwise return recordedCwd ?? launchCwd; reference the registration
variable and SessionEntry.resumeWorkingDirectory /
SessionRestorableAgentSnapshot.workingDirectory so the snapshot uses the single
source of truth.
🪄 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: be26ce6a-266e-4ec0-b390-01bf2d0436c7

📥 Commits

Reviewing files that changed from the base of the PR and between ff172b0 and 53697db.

📒 Files selected for processing (11)
  • CLI/cmux.swift
  • Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentCwdNamespacing.swift
  • Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift
  • Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeWorkingDirectory.swift
  • Packages/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentResumeArgvTests.swift
  • Packages/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentResumeWorkingDirectoryTests.swift
  • Sources/RestorableAgentSession.swift
  • Sources/RestorableAgentTypes.swift
  • Sources/SessionPersistence.swift
  • cmuxTests/AgentSessionAutoResumeSettingsTests.swift
  • cmuxTests/RestorableAgentSessionIndexTests.swift

Comment thread Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentResumeArgv.swift Outdated
Comment thread Sources/RestorableAgentSession.swift
…nore cwd in snapshot

- AgentResumeArgv.commandParts: normalize arguments.first so a whitespace-only argv[0] falls back to
  the default executable instead of emitting an invalid one.
- restorableWorkingDirectory: a custom registration with cwd: .ignore now yields nil workingDirectory
  in the snapshot (it resumes from the current dir), so downstream restore consumers don't place the
  terminal in a saved cwd and break the cwd-policy contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/RestorableAgentSession.swift">

<violation number="1" location="Sources/RestorableAgentSession.swift:1306">
P2: `.ignore` cwd policy can be bypassed for custom resume templates because `{{cwd}}` still falls back to launch cwd.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

// no saved cwd at all (downstream restore consumers read `workingDirectory` directly, not just
// the command builder). The by-directory namespace below is only for built-in agents.
if let registration {
return registration.cwd == .ignore ? nil : (recordedCwd ?? launchCwd)

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: .ignore cwd policy can be bypassed for custom resume templates because {{cwd}} still falls back to launch cwd.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/RestorableAgentSession.swift, line 1306:

<comment>`.ignore` cwd policy can be bypassed for custom resume templates because `{{cwd}}` still falls back to launch cwd.</comment>

<file context>
@@ -1299,10 +1299,11 @@ struct RestorableAgentSessionIndex: Sendable {
+        // no saved cwd at all (downstream restore consumers read `workingDirectory` directly, not just
+        // the command builder). The by-directory namespace below is only for built-in agents.
+        if let registration {
+            return registration.cwd == .ignore ? nil : (recordedCwd ?? launchCwd)
         }
 
</file context>

@lawrencecchen
lawrencecchen merged commit 5b7ec86 into main Jun 4, 2026
32 checks passed
@austinywang austinywang mentioned this pull request Jun 4, 2026
lawrencecchen added a commit that referenced this pull request Jun 4, 2026
…5350) (#5351)

* Fix codex sessions restoring into the wrong surface after reload (#4920)

Root cause (adversarially-verified): the CLI agent launchers stamped the
operator's FOCUSED pane into CMUX_WORKSPACE_ID/CMUX_SURFACE_ID instead of the
launch surface, while leaving CMUX_PANEL_ID/CMUX_TAB_ID at the launch surface.
So a codex launched in surface B while surface A is focused recorded surface A
and restored into the wrong surface after reload (the "jumble"). Flows through
the codex-family omx/codex-teams launchers; plain `codex` is unaffected because
its env comes from the matched per-surface terminal startup env.

- New shared resolver CMUXAgentLaunch/AgentSpawnIdentity: prefer the launcher's
  OWN surface/workspace identity, falling back to the focused pane only when the
  launcher has none. Pure value logic with golden tests.
- configureTmuxCompatEnvironment (CLI/cmux.swift): resolve via AgentSpawnIdentity
  so omx/claude-teams stamp the launch surface, keeping CMUX_SURFACE_ID matched
  with the inherited CMUX_PANEL_ID.
- runCodexTeams watcher args: derive the root workspace/surface from the launch
  env, not focusedContext, so the codex-teams watcher records the right surface.

Independent of the cwd-resolution work (#5300/#5312): that is the directory axis,
this is the surface-mapping axis. Full root cause + e2e survives-reload test plan
in plans/feat-codex-surface-jumble/DESIGN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add e2e test for the spawn-env surface fix via a hidden debug seam

testTmuxCompatEnvStampsLaunchSurfaceNotFocusedPane drives the real `cmux` binary
through the real configureTmuxCompatEnvironment (via a hidden __debug-tmux-compat-env
subcommand) with a mock socket reporting focused=surface A and the launcher's own
env=surface B. Asserts the stamped CMUX_SURFACE_ID == launch surface B (== CMUX_PANEL_ID),
not the focused surface A. Exercises the actual launcher env path, not just the resolver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Address review: drop stray design doc, fix debug-seam socket source, surface-scope test tab id

- Remove plans/feat-codex-surface-jumble/DESIGN.md (design docs live in the control repo, not cmux).
- cubic P2: debugDumpTmuxCompatEnvironment now injects the passed socketPath into the env before
  resolving the focused context, so it uses the socket the command was pointed at.
- CodeRabbit: the e2e fixture uses a surface-scoped CMUX_TAB_ID (distinct from the workspace id) and
  asserts it passes through, matching the repo's surface-scoped tab identity contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: run CMUXAgentLaunch package tests as a real gate

The CI swift-test step ran a hardcoded package list that omitted CMUXAgentLaunch,
so its tests (AgentSpawnIdentity resolver, AgentLaunchSanitizer, Hermes/RovoDev
resolvers, and the resume-engine builders once they land) only compiled, never
executed. The package resolves standalone via SwiftPM (no GhosttyKit/app dep),
so add it to the gate. Verified locally: 59 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Capture CODEX_HOME for plain codex resume/fork (#5350)

Plain codex (no cmux launcher, no resolvable PID at hook time) produced an
empty launchCommand, so the agent's launch env was never captured. When codex
runs under the subrouter account manager, CODEX_HOME points at
~/.codex-accounts/<account>, not ~/.codex; dropping it made resume/fork emit a
bare `codex resume/fork <id>` against the default home and fail with
"No saved session found".

agentLaunchCommandFromEnvironment now still emits an env-only record carrying
the selected launch env (CODEX_HOME, CLAUDE_CONFIG_DIR, ...) when argv is
unavailable but the env is non-empty. AgentResumeCommandBuilder already prefixes
that env ahead of the kind's fallback verb, so resume and fork reproduce
`CODEX_HOME=<home> codex resume/fork <id>`. Default home stays nil (unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Override leaked env surface id with PID/TTY truth in agent hooks (#5333)

The codex surface jumble: a launcher or inherited subprocess can leak the
operator's FOCUSED pane into CMUX_SURFACE_ID, and the generic hook trusted that
env surface whenever both env ids were present (the corrector was suppressed),
routing the session to the wrong pane. The no-pid-gate resume binding then
persisted the wrong surface across reload.

The hook now always resolves the agent process's own terminal binding (TTY
first, then PID) and, when it points to a different accessible surface inside
the SAME workspace, overrides the ambient-env surface with that ground truth.
It stays a no-op when the surface came from an explicit --surface flag, when the
TTY/PID binding is unavailable (remote/SSH), or when it already agrees, so the
common path and remote sessions are unaffected.

Adds two app-hosted CLI integration tests: CODEX_HOME survives an unavailable
launch command (#5350), and a leaked env surface is overridden by the TTY-bound
pane (#5333). Both are RED before their respective fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Review: clean up debug-seam shim dir; make new hook tests hermetic

- debugDumpTmuxCompatEnvironment leaked a /tmp shim dir per invocation (incl.
  each CI run of the tmux-compat seam test); it is the one-shot dump path that
  never spawns a long-lived agent, so defer-remove the dir on exit. (greptile,
  cubic)
- The two new codex-hook integration tests inherited the runner HOME; set
  HOME to the per-test temp root before spawning cmux, matching the existing
  runGenericHookPersistenceScenario pattern. (cubic)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Persist the env-only launch record so fork/resume keep CODEX_HOME

The hook session store's update() only assigned launchCommand when arguments
was non-empty, so the new env-only record (empty argv, carrying CODEX_HOME) was
never written. A later hook event that falls back to mapped?.launchCommand, or
the fork path reading the persisted store after reload, could then drop
CODEX_HOME and fail with "No saved session found" again.

Persist an argv-less env-only record when no argv-bearing record exists yet,
without ever downgrading a richer earlier capture. Extends the G2 test to assert
the persisted launchCommand carries CODEX_HOME. (cursor Bugbot)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Keep AgentSpawnIdentity a coherent workspace/surface pair

resolve() fell back independently per axis, so a launcher inheriting only
CMUX_WORKSPACE_ID while focus is in a different workspace produced an impossible
(own workspace, focused surface) pair. Call sites stamped that into the spawn
env, and hook routing then rejected the surface-not-in-workspace pair and
dropped the hook (no resume binding).

Borrow the focused surface only when the focused pane is in the resolved
workspace; otherwise leave the surface nil so the hook's PID/TTY resolution
picks the agent's real pane. Composes with the G3 hook override. Updates the
per-axis test to the coherent semantics and adds the same-workspace case.
(autoreview Codex / greptile / codex bot)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Let a stale ambient surface fall through to PID/TTY instead of dropping the hook

The G3 correction only ran when the ambient CMUX_SURFACE_ID resolved to an
accessible surface. A stale/invalid ambient surface (closed, or in another
workspace) made resolvedDirectSurfaceArg nil, which set hasInvalidDirectSurfaceArg
and hasUnusableDirectBinding, so resolveAgentHookTarget aborted BEFORE the TTY/PID
binding could recover — the stale-env variant of the codex jumble still no-op'd
the hook and lost the session across reload.

Treat an invalid AMBIENT env id differently from an invalid EXPLICIT flag: only an
explicit --workspace/--surface that fails to resolve is a hard error; a stale
ambient id is treated as absent so routing falls through to the PID/TTY binding
(ground truth). Adds a stale-env regression test. (autoreview Codex P1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Make AgentSpawnIdentity coherent in both partial-env directions

The prior coherence fix handled (own workspace, no own surface) but the mirror
remained: an orphan own surface (inherited CMUX_SURFACE_ID with no
CMUX_WORKSPACE_ID) was still paired with the focused workspace, producing a
cross-workspace (focused workspace, own surface) identity.

Treat the own surface as authoritative only paired with its own workspace; an
orphan own surface is not trusted against the focused context, so the resolver
uses the coherent focused pair or leaves it nil for PID/TTY recovery. Adds two
orphan-surface tests (8 total, all green). (autoreview Codex P2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Keep non-restorable launches non-resumable: env-only fallback only for absent argv

The env-only CODEX_HOME fallback also fired when sanitizedAgentLaunchArguments
REJECTED a captured argv. That rejection is exactly how AgentLaunchSanitizer
suppresses non-restorable invocations (codex exec/review/login, claude config,
...), so routing them through the env-only record persisted a bogus resumable
record (and could publish a default `codex resume <id>`) for one-shot commands.

Restrict the env-only fallback to the genuinely-unavailable-argv case (no
CMUX_AGENT_LAUNCH_ARGV_B64 and an unresolved/exited PID). A captured-but-rejected
argv returns nil again, so non-restorable launches stay non-resumable even when
CODEX_HOME is present. The G2 test forces the no-argv path via a dead PID, so it
stays green. Adds a non-restorable-exec regression test. (autoreview Codex P1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: lawrencecchen <lawrence@cmux.com>

This branch was successfully deployed

1 active deployment
Preview – cmux — 9b80482b Deployed Jun 4, 2026 by vercel[bot]
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