Direct Codex restore ignores inherited Subrouter routing

Merged
AU
19d ago
+53-11

Summary

Part of #21261.

When a terminal restores a Codex session from a direct (not Subrouter-routed) launch record, cmux restore builds the argv and execs it without a shell. The environment it hands to Codex starts from the restoring shell's own environment. For a proven routed record, the planner already drops inherited SUBROUTER_CODEX_* and CMUX_CUSTOM_CODEX_PATH and refills them from the record. For a direct record, it kept whatever the shell had inherited. That has two effects:

  • The managed Codex wrapper reads CMUX_CUSTOM_CODEX_PATH before it searches PATH. An inherited value (for example from a shell started under sr) can make it exec that launcher instead of the saved Codex command.
  • The wrapper re-exports the Subrouter resume marker when it is present and argv contains model_provider=subrouter. The resumed process is then captured as routed, so the next restore runs sr codex resume.

Now the launch record alone decides routing. For every non-direct Codex restore, the planner drops those restore-owned keys from the inherited environment before merging the record's environment. Routed records still get their proven values back, because that merge comes afterwards. Direct-mode replays (.direct) are unchanged.

What #21261 describes, a bundle path with spaces treated as an unquoted PATH fragment, does not reproduce on current main:

  • Local restore types the bare words cmux restore codex <id>.
  • The bundle Resources/bin directory reaches PATH as an environment value, not shell text (TerminalSurface+RuntimeSurfaceCreation.swift).
  • resolveRestoreExecutable splits PATH only on :.
  • Every shell-string fallback I traced single-quotes its words.

The new regression puts the bundle path with spaces and a shim path with spaces into the same restore. It pins that PATH survives unchanged.

Testing

  • Added AgentRestoreLaunchTests.directCodexRestoreIgnoresInheritedSubrouterRoutingFromSpacedBundlePath. It is a direct Codex record (codex -c model_provider=subrouter, no routing proof) restored from a shell that inherited the Subrouter variables, with bundle and shim paths containing spaces. It asserts that argv starts with the wrapper shim plus resume <checkpoint>, the provider flag and checkpoint are kept, there is no sr, PATH is unchanged, and the inherited routing keys are gone.
  • The test is committed before the fix (b4191385f9fda, fix in 20bc30bbfa92d). I could not record red/green locally: the Swift build host (cmux-lawrence-2) does not resolve from this machine (HQ REPAIR row for nx-remote DNS). The CMUXAgentLaunch package tests in hosted CI are the executed evidence for this PR.
  • python3 scripts/verify-local.py passed: Swift syntax, localization defaults, package groups, feature flags. git diff --check is clean. No native compilation ran locally.

Changelog

Fixed: Restoring a Codex session that was started directly no longer picks up a Subrouter launcher inherited from the terminal's environment

Proof

No UI change. The proof is the CMUXAgentLaunch package tests in CI.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • Reviewed with a subagent before merge (cmux-review), and all bot and human review comments resolved

šŸ¤– Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Stops direct Codex restores from inheriting Subrouter routing from the restoring shell. Previously, a direct launch record kept inherited SUBROUTER_CODEX_* and CMUX_CUSTOM_CODEX_PATH values, so the managed wrapper could exec a Subrouter launcher instead of the saved Codex command and the resumed process could be re-captured as routed. Now the launch record alone decides routing: for every non-direct Codex restore, the planner drops those keys from the inherited environment before merging the record's environment. PATH values pass through unchanged, including paths with spaces.

Testing

  • Adds directCodexRestoreIgnoresInheritedSubrouterRoutingFromSpacedBundlePath, which restores a direct Codex record from a shell with inherited Subrouter variables and spaces in the bundle and shim paths, asserting arg routing, PATH, and environment cleanup.