cloud-journey: Codex in stub mode, the stub on a free port, backend.yml watches the journey inputs
This PR does three things for the Cloud journey (scripts/cmux-next/cloud-journey.ts):
- the two Codex review follow-ups from #35301;
- Codex wiring for the stub mode;
- the login copy for either provider.
What changes
- Stub on a free port:
--port 0. The journey reads the port from the stub's ready file, so a busy port can no longer fail the hourly run. backend.ymlpath filters now includePackages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/**andtests/fixtures/model-stub/**, the journey's two other inputs.--agent codexwith--model stub:- The journey passes a made-up ChatGPT sign-in (
auth.jsonwith unsigned JWTs and a freshlast_refresh) through the same login copy the Mac uses, with providercodex. - It checks the sign-in with the app's own rules (
CloudCredentialTransfer.validate). - It points a Codex model provider (
requires_openai_auth = true) at the shared stub's/v1. - It checks that every
/v1/responsesrequest carried the forwarded access token. - A real Codex run takes
CODEX_AUTH_JSON. No CI secret holds one, so the scheduled jobs stay Claude-only.
- The journey passes a made-up ChatGPT sign-in (
Observed / Not run
-
Production, Claude, stub mode (local run): every step passed through the stub check. The reply was "Stub reply a93edeb89372. The tool printed a93edeb89372-tool." It came from 4 model requests, each carrying the forwarded login, and the stub ran on a port it chose itself.
connectis still red on production: link_token answersauth.forbidden"the original receiving VM has no live ready attempt". That red is not from this PR. -
Production, Codex, stub mode (local run):
pass Codex loginpassed, and the launcher sees the own login. Every Codex request reached the stub with the same credential hash. The turn is not green, for two reasons:- Codex cannot decode the shared stub's
GET /v1/modelsanswer ("failed to decode models response"), since it expects Codex's own models schema. It then sends the turn with no tools, and the stub replies "Stub side reply.". - With a ChatGPT sign-in, Codex also calls chatgpt.com plugin endpoints with that sign-in. Those calls get a 401, since the token is made up. A login-free check for Codex therefore also needs plugins off.
Both are follow-ups, for the shared stub's owner and the Codex config.
- Codex cannot decode the shared stub's
-
One flaky run: the first Codex run failed preflight on a fresh machine. The same preflight passed on a second machine and on retry.
-
Not run here:
bun test, since agents don't run tests on this Mac. The new helpers were checked withbun -e, and actionlint is clean.
Changelog
none
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds Codex support to the Cloud journey's stub mode, and fixes two follow-ups from #35301.
- Stub on a free port:
--port 0lets the stub choose its port, which the journey reads from the stub's ready file; a busy port can no longer fail the hourly run. backend.ymlpath filters now includePackages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/**andtests/fixtures/model-stub/**, the journey's two other inputs.--agent codexwith--model stub: passes a made-up ChatGPT sign-in (auth.jsonwith unsigned JWTs) through the app's own login copy and validation, points a Codex model provider (requires_openai_auth = true) at the shared stub, and checks every/v1/responsesrequest carried the forwarded token. A real Codex run readsCODEX_AUTH_JSON; no CI secret holds one, so scheduled jobs stay Claude-only.
Known limitations
- Codex cannot decode the stub's
GET /v1/modelsanswer, so it sends the turn with no tools and the stub replies with its side reply. - With a ChatGPT sign-in, Codex calls chatgpt.com plugin endpoints, which return 401 since the token is made up; a login-free Codex check also needs plugins off.