codex exec resume overrides persisted cwd when -C is omitted

Open 💬 1 comment Opened Aug 25, 2026 by jroesch

What version of Codex is running?

Current source at 725b3a44f8d13c8d2f019e1ecbc8ab686466b950; reproduced with standalone Codex 0.149.1.

What happened?

A thread created with an explicit managed-worktree cwd can silently rebind to the caller's current repository workspace when it is resumed without -C.

The app-server protocol already supports the desired behavior: ThreadResumeParams.cwd is optional, and cold resume falls back to the persisted SessionMeta.cwd. However, codex-rs/exec/src/lib.rs currently builds the resume request with:

cwd: Some(config.cwd.to_string_lossy().to_string()),
runtime_workspace_roots: Some(config.workspace_roots.clone()),

After configuration resolution, this no longer distinguishes an explicit -C from the process cwd used as a default. The resume request therefore overrides the persisted checkout even when the user did not request a rebind.

This becomes especially visible after a proxy disconnect or app-server restart, when the thread is cold-resumed from history.

Reproduction

codex exec -C /path/to/managed-worktree "start a task and print pwd"
# Save the thread id, then reconnect or restart app-server.
cd /path/to/repository-workspace
codex exec resume <thread-id> "print pwd"

Expected: the resumed task remains in /path/to/managed-worktree.

Observed: the resume request supplies /path/to/repository-workspace and the task rebinds there.

Suggested correction

Retain whether -C was explicitly supplied when constructing the exec resume request:

  • omit ThreadResumeParams.cwd when -C was not supplied;
  • omit the implicit runtimeWorkspaceRoots override in the same case;
  • preserve explicit -C as an intentional rebind;
  • apply the same rule to any Desktop remote-resume request builder.

The remote TUI path already follows this model by omitting cwd unless there is an explicit remote override.

Acceptance coverage

  1. Implicit codex exec resume emits no cwd or workspace-root override.
  2. Explicit -C emits both and still intentionally rebinds.
  3. Cold resume after app-server restart preserves the persisted checkout.
  4. Warm reconnect to an idle loaded thread preserves the checkout.
  5. Sandboxed Git and built-in apply_patch continue in the original managed worktree after reconnect.

View original on GitHub ↗

1 Comment

Bishnuu72 · 2 days ago

Confirmed still present on current main (2764e8362). While tracing it I
mapped every affected call site — one more than described in this report:

  • codex-rs/exec/src/lib.rs:1220thread_resume_params_from_config forces

cwd: Some(config.cwd…) and runtime_workspace_roots: Some(…) on every
resume.

  • codex-rs/exec/src/lib.rs:879 — the ThreadForkParams builder repeats the

same pattern, so codex exec fork <thread-id> rebinds the copy to the
caller's directory under the same conditions. The suggested correction
should cover fork alongside resume.

  • codex-rs/exec/src/lib.rs:1190 (ThreadStartParams) is correct as-is —

thread creation must supply a cwd.

Supporting details for whoever picks this up:

  • The desired API shape already exists in the TUI path:

thread_cwd_from_config
(codex-rs/tui/src/app_server_session.rs:1979) returns
Option<String> and yields None for remote threads unless an explicit
override was supplied. The exec builders just need to adopt the same
omit-unless-explicit rule.

  • Omission is fully supported by the protocol:

ThreadResumeParams.cwd: Option<String> is optional-nullable
(codex-rs/app-server-protocol/src/protocol/v2/thread.rs), and cold resume
reads the persisted thread cwd back server-side
(thread_from_stored_thread in
codex-rs/app-server/src/request_processors/thread_processor.rs).

Natural home for the acceptance coverage listed above: the existing app-server
resume/fork suites (codex-rs/app-server/tests/suite/v2/thread_resume.rs,
thread_fork.rs) plus an exec-level test asserting that an implicit
codex exec resume emits no cwd/workspace-root override while an explicit
-C still does.