Skip to content

fix(codex): avoid runtime shadow reindex on TUI startup#639

Open
fnmendez wants to merge 1 commit into
ndycode:mainfrom
fnmendez:fix/runtime-shadow-startup
Open

fix(codex): avoid runtime shadow reindex on TUI startup#639
fnmendez wants to merge 1 commit into
ndycode:mainfrom
fnmendez:fix/runtime-shadow-startup

Conversation

@fnmendez

@fnmendez fnmendez commented Jul 26, 2026

Copy link
Copy Markdown

Summary

  • run interactive TUI sessions against the canonical Codex home so Codex reuses its state database and thread index
  • inject the runtime rotation provider through ephemeral CLI overrides without mutating config.toml or exposing the proxy token in argv
  • forward the same non-secret overrides to the TUI app-server shim

Why

Runtime rotation created a fresh shadow CODEX_HOME that omitted Codex state SQLite files. With a large sessions directory, every TUI launch rebuilt the thread index and could remain blank for tens of seconds.

Validation

  • npm run build
  • npm run lint:scripts
  • npm run typecheck:scripts
  • focused canonical-home and app-helper tests pass
  • full wrapper suite: 107 passed, 6 skipped; 3 pre-existing Windows path resolver tests fail when run on macOS
  • adversarial review: no Critical or Major findings
  • real macOS TUI startup improved from about 40 seconds to 9.5 seconds on the second measured launch

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

this change moves interactive tui rotation from a shadow home to the canonical codex home.

  • supplies the runtime provider through ephemeral -c overrides.
  • forwards the same non-secret overrides through the app-server shim.
  • keeps the proxy token in the environment rather than argv or canonical config.toml.
  • adds vitest coverage for canonical state reuse, override forwarding, and token argv safety.

Confidence Score: 4/5

the change appears safe to merge, with non-blocking missing vitest coverage for concurrent canonical-home launches.

the provider overrides and token handling are internally consistent, but the test suite does not exercise multiple detached helpers sharing canonical filesystem state.

Files Needing Attention: test/codex-bin-wrapper.test.ts

Important Files Changed

Filename Overview
scripts/codex.js switches interactive tui sessions to canonical codex state and forwards ephemeral provider configuration without placing the proxy token in argv.
test/codex-bin-wrapper.test.ts validates the single-session canonical-home path and token argv safety, but omits concurrent canonical-home coverage.

Sequence Diagram

sequenceDiagram
  participant TUI
  participant Wrapper
  participant Helper
  participant Proxy
  participant CodexHome as Canonical CODEX_HOME
  participant AppServer
  TUI->>Wrapper: interactive launch
  Wrapper->>Helper: start detached helper
  Helper->>Proxy: start with ephemeral token
  Helper-->>Wrapper: provider overrides and environment
  Wrapper->>CodexHome: run codex against canonical state
  CodexHome->>AppServer: launch through shim
  AppServer->>Proxy: use forwarded provider overrides
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
test/codex-bin-wrapper.test.ts:2761
**cover concurrent canonical-home launches**

this vitest covers only one tui launch, while the changed path lets independent detached helpers share the canonical codex home and helper status file. add a concurrent-launch case to catch sqlite-state and status-file races, including windows filesystem behavior.

Reviews (1): Last reviewed commit: "fix(codex): avoid runtime shadow reindex..." | Re-trigger Greptile

Context used (3)

  • Context used - test/AGENTS.md (source)
  • Context used - speak in lowercase, concise sentences. act like th... (source)
  • Knowledge Base — Build and Packaging

Run interactive TUI sessions against the canonical Codex home so its state database and thread index remain reusable. Route both the TUI and its app-server through ephemeral non-secret proxy overrides, preserving account rotation without mutating config or exposing credentials.
@fnmendez
fnmendez requested a review from ndycode as a code owner July 26, 2026 12:08
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Minor-risk performance and startup architecture update: the interactive Codex TUI now reuses the canonical Codex home, preserving its state database and thread index instead of rebuilding a shadow home, reducing repeat startup from ~40s to ~9.5s on macOS. Runtime proxy configuration is injected through ephemeral environment-backed CLI overrides without persisting config.toml changes or exposing the proxy token in arguments; regression coverage includes a focused canonical-home wrapper test and 107 passing wrapper tests, with three pre-existing macOS failures.

Walkthrough

the runtime rotation helper now supports canonical-home execution, forwards dynamic app-server config arguments through its shim, and propagates helper-provided arguments into the final runtime process. interactive tui routing enables this mode and adds regression coverage.

Changes

canonical runtime rotation

layer / file(s) summary
canonical context selection
scripts/codex.js:77-80, scripts/codex.js:3497-3528, scripts/codex.js:3832-3926
the helper selects canonical or shadow-home execution, forwards the original codex_home in canonical mode, and uses unified cleanup.
app-server argument forwarding
scripts/codex.js:3543-3562, scripts/codex.js:3608, scripts/codex.js:3665-3669, scripts/codex.js:3770, scripts/codex.js:3935-3936
the shim parses injected config arguments, appends them to app-server launches, clears the internal environment variable, and reports runtime arguments and environment.
launcher propagation and tui coverage
scripts/codex.js:3997-4018, scripts/codex.js:4084-4108, scripts/codex.js:4149, test/codex-bin-wrapper.test.ts:2763-2857
the launcher propagates canonical-home selection and helper arguments into the final command; the interactive tui test verifies the original home, persisted state, shim overrides, and proxy lifecycle markers.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: bug

Suggested reviewers: ndycode, ndycode

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed the title matches the main change and follows the required fix(codex): summary format.
Description check ✅ Passed the description covers summary, rationale, and validation, but it omits the what changed, risk/rollback, and docs/governance sections.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

test/codex-bin-wrapper.test.ts

Oops! Something went wrong! :(

ESLint: 10.0.0

Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
at /node_modules/eslint/lib/config/config-loader.js:145:10
at async loadTypeScriptConfigFileWithJiti (/node_modules/eslint/lib/config/config-loader.js:144:3)
at async loadConfigFile (/node_modules/eslint/lib/config/config-loader.js:265:11)
at async ConfigLoader.calculateConfigArray (/node_modules/eslint/lib/config/config-loader.js:588:23)
at async #calculateConfigArray (/node_modules/eslint/lib/config/config-loader.js:369:19)
at async Promise.all (index 0)
at async findFiles (/node_modules/eslint/lib/eslint/eslint-helpers.js:635:25)
at async ESLint.lintFiles (/node_modules/eslint/lib/eslint/eslint.js:1014:21)
at async Object.execute (/node_modules/eslint/lib/cli.js:386:14)
at async main (/node_modules/eslint/bin/eslint.js:175:19)


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.

@coderabbitai coderabbitai Bot left a comment

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.

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 `@scripts/codex.js`:
- Around line 3497-3528: Prevent interactive TUI and app-helper modes from
concurrently using the real Codex home without protection: update
createRuntimeRotationProxyCanonicalCodexHome to restore isolated shadow-home
behavior with safe sync-back, or enforce an equivalent single-instance
lock/flock with backoff around canonical sessions. In
test/codex-bin-wrapper.test.ts lines 2763-2857, add a regression that launches
two canonical sessions concurrently and verifies session/SQLite state is
preserved without corruption or loss.

In `@test/codex-bin-wrapper.test.ts`:
- Around line 2763-2857: The canonical Codex home test covers only one
interactive session and does not reproduce concurrent access. Extend the test
around “uses the canonical Codex home for interactive TUI runtime routing” to
launch overlapping TUI processes sharing the same canonical home, coordinate
their execution, and assert both complete without state/config corruption.
Preserve the existing routing and persistence assertions while adding the
concurrency regression coverage.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 839395a9-0370-4212-92c1-84143c42999d

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdf7a4 and a170622.

📒 Files selected for processing (2)
  • scripts/codex.js
  • test/codex-bin-wrapper.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (10)
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Write Vitest test suites with globals enabled (describe, it, expect)
Maintain 80%+ coverage threshold across statements, branches, functions, and lines
Use removeWithRetry() for Windows filesystem cleanup instead of bare fs.rm to handle EBUSY, EPERM, and ENOTEMPTY errors
Do not rely on dist/ in tests; use source files instead
Do not skip tests without justification
Relax lint rules for test files as configured in eslint.config.js

Files:

  • test/codex-bin-wrapper.test.ts
test/**/codex-bin-wrapper.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

Test bin wrapper lazy-load and missing dist handling with concurrent invocations in codex-bin-wrapper.test.ts

Files:

  • test/codex-bin-wrapper.test.ts
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM only ("type": "module"), Node >= 18.17

Files:

  • test/codex-bin-wrapper.test.ts
  • scripts/codex.js
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use as any, @ts-ignore, or @ts-expect-error in TypeScript files

Files:

  • test/codex-bin-wrapper.test.ts
{scripts/**/*.js,test/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use bare recursive delete logic in Windows-sensitive scripts/tests without retry handling

Files:

  • test/codex-bin-wrapper.test.ts
  • scripts/codex.js
**/*.{js,ts,mjs,cjs}

📄 CodeRabbit inference engine (README.md)

**/*.{js,ts,mjs,cjs}: Keep npm installation scripts side-effect-free; postinstall may print a short notice but must not modify runtime state or perform setup, especially in CI or non-interactive installs.
Never run npm install or update commands automatically; only display a manual upgrade notice when appropriate.
Do not publish or take ownership of a global codex binary; preserve the official OpenAI installation as the owner of the codex command.
Keep runtime rotation and local bridge services loopback-only, and protect local bridge access with hashed client tokens.
Keep OAuth credentials and account state local; do not send them to external services as part of normal account management.
Treat Responses background mode as opt-in: requests with background: true must use stateful store=true, while default stateless routing uses store=false.
Use bounded outbound request budgets, avoid whole-pool replay when every account is rate-limited, and enter cooldown after repeated cross-account 5xx bursts.
Make experimental synchronization and backup flows non-destructive by default: preview before applying sync, preserve destination-only accounts, and fail safely on backup filename collisions.

Files:

  • test/codex-bin-wrapper.test.ts
  • scripts/codex.js
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/codex-bin-wrapper.test.ts
scripts/codex*.js

📄 CodeRabbit inference engine (AGENTS.md)

Do not bypass the official Codex CLI by reimplementing general Codex commands in the wrapper

Files:

  • scripts/codex.js
{scripts/*.js,lib/codex-manager/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Canonical package name is codex-multi-auth; canonical command family is codex-multi-auth ...

Files:

  • scripts/codex.js
{lib,scripts}/**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Windows filesystem safety: retry transient EBUSY/EPERM/ENOTEMPTY cleanup and write failures where tests cover Windows locks

Files:

  • scripts/codex.js
🧠 Learnings (2)
📚 Learning: 2026-06-04T06:14:18.093Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/scheduling-strategy-config.test.ts:1-1
Timestamp: 2026-06-04T06:14:18.093Z
Learning: In ndycode/codex-multi-auth, do not flag explicit imports from "vitest" (e.g., describe, it, expect, beforeEach/afterEach, etc.) in test files as issues—even if the Vitest config sets `globals: true`. The repo’s established convention is to keep these imports for consistency with neighboring tests; removing them would make files outliers.

Applied to files:

  • test/codex-bin-wrapper.test.ts
📚 Learning: 2026-06-04T06:14:24.975Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/runtime-rotation-proxy.test.ts:2478-2491
Timestamp: 2026-06-04T06:14:24.975Z
Learning: In ndycode/codex-multi-auth test files (e.g. `test/*.test.ts`), when creating V3 storage fixtures for accounts, it’s an intentional convention to use `as never` for deliberately minimal stored-account objects that only include `refreshToken`, `addedAt`, and `lastUsed`. Do not treat `as never` here as a type-safety problem: optional/other fields are expected to be populated by the runtime during execution, and the cast is used solely to keep the fixture minimal and consistent across existing tests.

Applied to files:

  • test/codex-bin-wrapper.test.ts
🪛 ast-grep (0.44.1)
test/codex-bin-wrapper.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (9)
scripts/codex.js (9)

77-80: LGTM!


3543-3562: LGTM!


3608-3608: LGTM!

Also applies to: 3665-3669


3770-3770: LGTM!


3832-3832: LGTM!

Also applies to: 3869-3869, 3900-3926, 3935-3936


3997-3997: LGTM!

Also applies to: 4017-4018


4084-4091: LGTM!

Also applies to: 4108-4108


4143-4151: LGTM!


3506-3506: 🎯 Functional Correctness

APP_SERVER_ACCOUNT_DISPLAY_NAME is declared at scripts/codex.js:66, so this reference is safe.

Comment thread scripts/codex.js
Comment on lines +3497 to +3528
function createRuntimeRotationProxyCanonicalCodexHome(
baseEnv,
proxyBaseUrl,
clientApiKey,
configTomlModule,
) {
const originalCodexHome = resolveRuntimeRotationProxyOriginalCodexHome(baseEnv);
const providerTable = `model_providers.${RUNTIME_ROTATION_PROXY_PROVIDER_ID}`;
const configArgs = [
`${providerTable}.name=${configTomlModule.tomlStringLiteral(APP_SERVER_ACCOUNT_DISPLAY_NAME)}`,
`${providerTable}.base_url=${configTomlModule.tomlStringLiteral(proxyBaseUrl)}`,
`${providerTable}.env_key=${configTomlModule.tomlStringLiteral("OPENAI_API_KEY")}`,
`${providerTable}.requires_openai_auth=false`,
`${providerTable}.wire_api=${configTomlModule.tomlStringLiteral("responses")}`,
"disable_response_storage=false",
].flatMap((assignment) => ["-c", assignment]);

return {
args: configArgs,
env: {
...baseEnv,
CODEX_HOME: originalCodexHome,
OPENAI_API_KEY: clientApiKey,
CODEX_MULTI_AUTH_DIR: resolveRuntimeRotationOriginalMultiAuthDir(
originalCodexHome,
baseEnv,
),
},
cleanup: () => {},
};
}

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'repo files matching codex.js/codex-bin-wrapper.test.ts:\n'
fd -a 'codex\.js$|codex-bin-wrapper\.test\.ts$' . | sed 's#^\./##'

printf '\noutline scripts/codex.js around target functions:\n'
ast-grep outline scripts/codex.js --match createRuntimeRotationProxyCanonicalCodexHome --view expanded || true
ast-grep outline scripts/codex.js --match createRuntimeRotationProxyCodexHome --view expanded || true

printf '\nrelevant scripts/codex.js lines:\n'
nl -ba scripts/codex.js | sed -n '3360,3560p'

printf '\nlock/guard terms in scripts/codex.js:\n'
rg -n "lock|single|instance|FILE_LOCK|tryLock|flock|lockfile|fs\\.lock|fs\\.open|mkdtemp|tmpdir|ORIGINAL_CODEX_HOME|CODEX_HOME=|CODEX_MULTI_AUTH_REAL_CODEX_BIN|createRuntimeRotation" scripts/codex.js || true

printf '\ntest lines:\n'
nl -ba test/codex-bin-wrapper.test.ts | sed -n '2700,2875p'

Repository: ndycode/codex-multi-auth

Length of output: 614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'scripts/codex.js relevant lines:\n'
awk 'NR>=3360 && NR<=3560 {printf "%6d\t%s\n", NR, $0}' scripts/codex.js

printf '\nlock/guard terms in scripts/codex.js:\n'
rg -n "lock|single|instance|FILE_LOCK|tryLock|flock|lockfile|fs\\.lock|fs\\.open|mkdtemp|tmpdir|ORIGINAL_CODEX_HOME|CODEX_HOME=|CODEX_MULTI_AUTH_REAL_CODEX_BIN|createRuntimeRotation" scripts/codex.js || true

printf '\ncreateRuntimeRotationProxyCanonicalCodexHome call sites in scripts/codex.js:\n'
rg -n "createRuntimeRotationProxyCanonicalCodexHome|createRuntimeRotationProxyCodexHome|app-bridge|app-server|--canonical-shim" scripts/codex.js || true

printf '\ntest/codex-bin-wrapper.test.ts relevant lines:\n'
awk 'NR>=2700 && NR<=2875 {printf "%6d\t%s\n", NR, $0}' test/codex-bin-wrapper.test.ts

printf '\nconcurrency-related terms in test/codex-bin-wrapper.test.ts:\n'
rg -n "concurrent|concurren|Promise\\s*<|all\\(|race\\(|spawnSync|fork\\(|parallel|simultan|same fixture home|same home" test/codex-bin-wrapper.test.ts || true

Repository: ndycode/codex-multi-auth

Length of output: 28523


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'codex.js lines around canonical context creation and app helper startup:\n'
awk 'NR>=3870 && NR<=3940 {printf "%6d\t%s\n", NR, $0}' scripts/codex.js
awk 'NR>=4010 && NR<=4155 {printf "%6d\t%s\n", NR, $0}' scripts/codex.js
awk 'NR>=4210 && NR'=4295' {printf "%6d\t%s\n", NR, $0}' scripts/codex.js

printf '\nsearch canonical home tests near usages:\n'
rg -n "canonical Codex home|canonical-shim|canonical.*home|app-server --canonical-shim|createRuntimeRotationProxyCanonicalCodexHome" test/codex-bin-wrapper.test.ts scripts/codex.js || true

printf '\nPython source scan for lock/single-instance around canonical home env:\n'
python3 - <<'PY'
from pathlib import Path
p=Path('scripts/codex.js')
s=p.read_text()
for name in ['createRuntimeRotationProxyCanonicalCodexHome','createRuntimeRotationProxyContextIfEnabled','createRuntimeRotationAppHelperContext']:
    idx=s.find(f'function {name}')
    print(f'{name}: found={idx!=-1}')
print()
for needle in [
    'FILE_LOCK', 'single-instance', 'tryLock', 'flock', 'lockfile', 'mkdtempSync(join(shadowRoot',
    'CODEX_HOME: originalCodexHome', 'cleanup: () => {}',
]:
    print(needle, '=>', s.find(needle))
PY

Repository: ndycode/codex-multi-auth

Length of output: 41998


don’t give interactive TUI/app-helper modes unguarded access to the real codex home.

scripts/codex.js:3903-3938 selects createRuntimeRotationProxyCanonicalCodexHome for TUI and scripts/codex.js:4078-4124 gives the helper the same env; it sets CODEX_HOME to originalCodexHome with cleanup: () => {}, while scripts/codex.js:3419-3495 previously used a shadow home and sync-back. no lock/single-instance guard is in play, so two concurrent canonical codex-multi-auth sessions using the same Codex home can race on session/sqlite state. add a regression in test/codex-bin-wrapper.test.ts:2763-2857 that launches two canonical sessions concurrently and asserts no state corruption/loss, or enforce a single-instance/flock/backoff design.

📍 Affects 2 files
  • scripts/codex.js#L3497-L3528 (this comment)
  • test/codex-bin-wrapper.test.ts#L2763-L2857
🤖 Prompt for 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.

In `@scripts/codex.js` around lines 3497 - 3528, Prevent interactive TUI and
app-helper modes from concurrently using the real Codex home without protection:
update createRuntimeRotationProxyCanonicalCodexHome to restore isolated
shadow-home behavior with safe sync-back, or enforce an equivalent
single-instance lock/flock with backoff around canonical sessions. In
test/codex-bin-wrapper.test.ts lines 2763-2857, add a regression that launches
two canonical sessions concurrently and verifies session/SQLite state is
preserved without corruption or loss.

Source: Path instructions

Comment on lines +2763 to +2857
it("uses the canonical Codex home for interactive TUI runtime routing", async () => {
const fixtureRoot = createWrapperFixture();
createRuntimeRotationProxyFixtureModule(fixtureRoot);
const fakeBin = createCustomFakeCodexBin(fixtureRoot, [
"#!/usr/bin/env node",
'const { spawnSync } = require("node:child_process");',
'const fs = require("node:fs");',
'const path = require("node:path");',
'const args = process.argv.slice(2);',
'if (args[0] === "app-server") {',
' console.log(`APP_SERVER_FORWARDED:${args.join(" ")}`);',
" process.exit(0);",
"}",
'const statePath = path.join(process.env.CODEX_HOME ?? "", "state_5.sqlite");',
'const originalState = path.join(process.env.ORIGINAL_CODEX_HOME ?? "", "state_5.sqlite");',
'console.log(`TUI_HOME_IS_ORIGINAL:${process.env.CODEX_HOME === process.env.ORIGINAL_CODEX_HOME}`);',
'console.log(`TUI_STATE_EXISTED:${fs.existsSync(statePath)}`);',
'fs.writeFileSync(statePath, "first-run-state\\n", "utf8");',
'console.log(`TUI_STATE_PERSISTED:${fs.readFileSync(originalState, "utf8").includes("first-run-state")}`);',
'console.log(`TUI_HAS_BASE_URL_OVERRIDE:${args.some((arg) => arg.includes("model_providers.codex-multi-auth-runtime-proxy.base_url="))}`);',
'console.log(`TUI_HAS_ENV_KEY_OVERRIDE:${args.includes(\'model_providers.codex-multi-auth-runtime-proxy.env_key="OPENAI_API_KEY"\')}`);',
'console.log(`TUI_HAS_AUTH_OVERRIDE:${args.includes("model_providers.codex-multi-auth-runtime-proxy.requires_openai_auth=false")}`);',
'console.log(`TUI_HAS_WIRE_OVERRIDE:${args.includes(\'model_providers.codex-multi-auth-runtime-proxy.wire_api="responses"\')}`);',
'console.log(`TUI_HAS_STORAGE_OVERRIDE:${args.includes("disable_response_storage=false")}`);',
'console.log(`TUI_KEY_IN_ARGS:${args.includes(process.env.OPENAI_API_KEY ?? "__missing__")}`);',
'const shimExe = path.join(process.env.CODEX_CLI_PATH ?? "", process.platform === "win32" ? "codex.exe" : "codex");',
'const shimResult = spawnSync(shimExe, ["app-server", "--canonical-shim"], { encoding: "utf8", env: process.env });',
'console.log(`TUI_SHIM_STATUS:${shimResult.status}`);',
'console.log(`TUI_SHIM_STDOUT:${(shimResult.stdout ?? "").trim()}`);',
"process.exit(0);",
]);
const originalHome = join(fixtureRoot, "codex-home");
const markerPath = join(fixtureRoot, "proxy-marker.txt");
mkdirSync(originalHome, { recursive: true });
writeFileSync(
join(originalHome, "config.toml"),
'model_provider = "openai"\n',
"utf8",
);

const result = runWrapper(fixtureRoot, [], {
CODEX_MULTI_AUTH_REAL_CODEX_BIN: fakeBin,
CODEX_HOME: originalHome,
ORIGINAL_CODEX_HOME: originalHome,
CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY: "1",
CODEX_MULTI_AUTH_APP_ROTATION_IDLE_MS: "200",
CODEX_MULTI_AUTH_TEST_PROXY_MARKER: markerPath,
OPENAI_API_KEY: undefined,
});

const output = combinedOutput(result);
if (result.status !== 0) {
throw new Error(output);
}
expect(output).toContain("TUI_HOME_IS_ORIGINAL:true");
expect(output).toContain("TUI_STATE_EXISTED:false");
expect(output).toContain("TUI_STATE_PERSISTED:true");
expect(output).toContain("TUI_HAS_BASE_URL_OVERRIDE:true");
expect(output).toContain("TUI_HAS_ENV_KEY_OVERRIDE:true");
expect(output).toContain("TUI_HAS_AUTH_OVERRIDE:true");
expect(output).toContain("TUI_HAS_WIRE_OVERRIDE:true");
expect(output).toContain("TUI_HAS_STORAGE_OVERRIDE:true");
expect(output).toContain("TUI_KEY_IN_ARGS:false");
expect(output).toContain("TUI_SHIM_STATUS:0");
expect(output).toContain(
"APP_SERVER_FORWARDED:app-server --canonical-shim",
);
expect(output).toContain(
"model_providers.codex-multi-auth-runtime-proxy.base_url=",
);
expect(output).toContain(
'model_providers.codex-multi-auth-runtime-proxy.env_key="OPENAI_API_KEY"',
);
expect(output).toContain(
"model_providers.codex-multi-auth-runtime-proxy.requires_openai_auth=false",
);
expect(output).toContain(
'model_providers.codex-multi-auth-runtime-proxy.wire_api="responses"',
);
expect(output).toContain("disable_response_storage=false");
expect(output).toContain(
'model_provider="codex-multi-auth-runtime-proxy"',
);
expect(readFileSync(join(originalHome, "state_5.sqlite"), "utf8")).toBe(
"first-run-state\n",
);
expect(readFileSync(join(originalHome, "config.toml"), "utf8")).toBe(
'model_provider = "openai"\n',
);
await waitForFileText(
markerPath,
"start:http://127.0.0.1:4567\nclose\n",
);
});

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

solid single-session coverage, but missing a concurrency regression case for canonical-home sharing.

see scripts/codex.js:3497-3528 for the root cause — canonical mode now shares the live codex home across processes with no isolation. as per path instructions, test/** should "demand regression cases that reproduce concurrency bugs" and this new test only exercises one TUI session at a time.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for 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.

In `@test/codex-bin-wrapper.test.ts` around lines 2763 - 2857, The canonical Codex
home test covers only one interactive session and does not reproduce concurrent
access. Extend the test around “uses the canonical Codex home for interactive
TUI runtime routing” to launch overlapping TUI processes sharing the same
canonical home, coordinate their execution, and assert both complete without
state/config corruption. Preserve the existing routing and persistence
assertions while adding the concurrency regression coverage.

Source: Path instructions

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