Skip to content

Refactor: complete codex-only migration and repair build/test - #6

Merged
ndycode merged 11 commits into
mainfrom
worktree/isolated-20260228-005048
Feb 27, 2026
Merged

Refactor: complete codex-only migration and repair build/test#6
ndycode merged 11 commits into
mainfrom
worktree/isolated-20260228-005048

Conversation

@ndycode

@ndycode ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Remove all remaining opencode references from the tracked codebase and docs.
  • Remove leftover opencode-named assets/config templates.
  • Rename remaining script entry points to codex naming.
  • Repair type/lint regressions introduced during prior aggressive rename edits.
  • Keep project behavior/build quality intact under the codex-only naming scope.

Validation

  • npm run lint ✅
  • npm run typecheck ✅
  • npm test ✅ (79 files / 1970 tests)
  • strict scan for opencode patterns (excluding dist/node_modules) ✅ no matches

@coderabbitai

coderabbitai Bot commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

walkthrough

broad migration from opencode → codex across imports, env vars, config paths, prompt filenames, tests, and vendor packages. prompt handling renamed to host-codex variants. see lib/prompts/host-codex-prompt.ts:line and lib/runtime-paths.ts:line.

changes

Cohort / File(s) Summary
workflow & ci
.github/workflows/ci.yml
codex compatibility test runs test/host-codex-prompt.test.ts instead of test/opencode-codex.test.ts.
documentation
AGENTS.md, lib/AGENTS.md, config/README.md, test/AGENTS.md, test/README.md
branding and docs switched from opencode → codex; config examples and notes updated to codex-*.
configuration files
config/codex-legacy.json, config/codex-modern.json, config/minimal-codex.json, config/schema/config.schema.json
$schema refs consolidated to local ./schema/config.schema.json; new schema file added.
core deps & imports
package.json, index.ts, lib/accounts.ts, lib/types.ts, lib/tools/hashline-tools.ts, lib/recovery.ts
imports updated from @opencode-ai/*@codex-ai/*; vendorized package entries added; index.ts now prewarms host prompt (lib/prompts/host-codex-prompt.ts:line).
prompt handling
lib/prompts/host-codex-prompt.ts, lib/prompts/codex-host-bridge.ts
public APIs and constants renamed to host variants: getHostCodexPrompt, prewarmHostCodexPrompt, CODEX_HOST_BRIDGE; cache filenames migrated and legacy cache support added (lib/prompts/host-codex-prompt.ts:line).
env vars & runtime paths
lib/cli.ts, lib/config.ts, lib/runtime-paths.ts
env gates OPENCODE_* → CODEX_*; legacy dir fn renamed to getLegacyCodexDir() and points to ~/.codex; runtime path selection now prefers dirs with account files (lib/runtime-paths.ts:line, lib/config.ts:line).
request pipeline
lib/request/fetch-helpers.ts, lib/request/helpers/input-utils.ts, lib/request/request-transformer.ts
client/type and token-refresh flow updated to CodexClient/CodexAuthSetter; prompt/system helpers renamed to host variants (isHostSystemPrompt, filterHostSystemPrompts*). review auth-setter validation in lib/request/fetch-helpers.ts:line.
storage & recovery
lib/recovery/storage.ts, lib/recovery/types.ts
header/comments updated to codex naming; no behavioral changes.
bench & tooling scripts
scripts/bench-format/codex-host.mjs, scripts/bench-format/models.mjs, scripts/benchmark-edit-formats.mjs, scripts/test-model-matrix.js, scripts/install-codex-auth.js
opencode executable resolvers and helpers renamed to codex equivalents; env OPENCODE_BIN → CODEX_BIN; windows discovery regex and matching adjusted (watch scripts/test-model-matrix.js:line).
tests & test infra
test/*.ts, test/...
many tests updated to import @codex-ai and host prompt modules; new tests added for runtime paths, installer, fetch-helper edge cases, and model-matrix helpers. see test/host-codex-prompt.test.ts:line and test/runtime-paths.test.ts:line.
config infra & tooling
eslint.config.js, vitest.config.ts
eslint ignores vendor/**; vitest ignores .codex/** instead of .opencode/**.
vendor packages
vendor/codex-ai-sdk/package.json, vendor/codex-ai-plugin/package.json
new local vendor manifests added with exports and types mapping to ./dist/*.js and ./dist/*.d.ts.
runtime paths tests & logic
lib/runtime-paths.ts, test/paths.test.ts, test/runtime-paths.test.ts
getCodexMultiAuthDir now considers account-storage files and deduplicates candidates; tests added for fallback selection and windows case-insensitive dedupe (test/runtime-paths.test.ts:line).

missing regression tests: migration from ~/.opencode to ~/.codex and the bridge constant rename lack explicit regression coverage. verify migration behavior and logged warnings in lib/config.ts:line and the bridge rename in lib/prompts/codex-host-bridge.ts:line.

windows edge cases: executable discovery and windows path deduplication changed; review windows-specific logic in scripts/test-model-matrix.js:line and scripts/bench-format/codex-host.mjs:line. run windows-focused tests manually.

concurrency risks: multi-auth dir selection and config migration could race under concurrent installers/processes. inspect lib/runtime-paths.ts:line and test/paths.test.ts:line for potential races; consider locking or atomic migration patterns.

sequence diagram(s)

(section skipped)

estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed title follows conventional commits format with type 'refactor' and 61-character lowercase imperative summary clearly describing the main change: complete codex-only migration and repair build/test.
Description check ✅ Passed description is directly related to the changeset, clearly outlining the removal of opencode references, migration to codex naming, repair of regressions, and validation of build/test quality.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch worktree/isolated-20260228-005048

Comment @coderabbitai help to get the list of available commands and usage tips.

@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: 27

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
scripts/install-codex-auth.js (2)

134-137: ⚠️ Potential issue | 🟡 Minor

no error handling for cache removal on windows/concurrent access.

rm() can throw if files are locked by another process (common on windows when codex is running). consider wrapping in try/catch with a warning instead of failing the entire install.

🛡️ proposed defensive handling
   } else {
-    await rm(cacheNodeModules, { recursive: true, force: true });
-    await rm(cacheBunLock, { force: true });
+    try {
+      await rm(cacheNodeModules, { recursive: true, force: true });
+      await rm(cacheBunLock, { force: true });
+    } catch (error) {
+      log(`Warning: Could not clear cache (${error.message}). You may need to restart Codex first.`);
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install-codex-auth.js` around lines 134 - 137, Wrap the cache removal
calls that use rm(cacheNodeModules, { recursive: true, force: true }) and
rm(cacheBunLock, { force: true }) in a try/catch so failures (e.g., file locks
on Windows or concurrent access) do not abort the install; on catch, log a
non-fatal warning (e.g., console.warn or processLogger.warn) including the
caught error and continue instead of rethrowing, preserving the existing
behavior when removals succeed.

159-164: ⚠️ Potential issue | 🟡 Minor

openai provider config is silently overwritten.

line 162 unconditionally replaces provider.openai with the template's value. users with custom openai configurations (api keys, base urls, etc.) will have them overwritten without warning.

if template.provider is undefined, this will throw. consider a defensive check.

🛡️ proposed safer merge
        const provider = (existing.provider && typeof existing.provider === "object")
          ? { ...existing.provider }
          : {};
-       provider.openai = template.provider.openai;
+       if (template.provider?.openai) {
+         provider.openai = { ...provider.openai, ...template.provider.openai };
+       }
        merged.provider = provider;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install-codex-auth.js` around lines 159 - 164, The current logic
unconditionally overwrites provider.openai and can throw if template.provider is
undefined; update the merge so it first checks template.provider and
template.provider.openai exist, then merge template.provider.openai into the
existing provider without overwriting existing keys (i.e., only fill missing
fields), preserving existing.provider values like apiKey/baseUrl, and assign the
result back to merged.provider and nextConfig; reference the variables provider,
existing, template, merged, and nextConfig when implementing the defensive check
and non-destructive merge.
scripts/test-model-matrix.js (2)

33-44: ⚠️ Potential issue | 🟠 Major

filter where stderr output before picking the windows executable.

line [37] merges stdout and stderr. when where Codex fails, a non-path stderr line can become candidates[0], then line [65] returns that text as the command. this breaks windows fallback behavior.

proposed fix
-	const candidates = `${whereResult.stdout ?? ""}\n${whereResult.stderr ?? ""}`
+	const candidates = `${whereResult.stdout ?? ""}`
 		.split(/\r?\n/)
 		.map((line) => line.trim())
-		.filter(Boolean);
+		.filter((line) => /^[A-Za-z]:\\.+\.(exe|cmd)$/i.test(line));

 	if (candidates.length === 0) {
 		return { command: "Codex", shell: false };
 	}

ref: lib/scripts/test-model-matrix.js:37, lib/scripts/test-model-matrix.js:65.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test-model-matrix.js` around lines 33 - 44, The merged stdout/stderr
can include non-path error text from whereResult which then becomes
candidates[0] and is mistakenly returned as the command; change the filtering of
candidates (the variable computed from whereResult) to exclude stderr error
lines by only keeping entries that look like valid Windows paths (e.g.
path.isAbsolute(line) or regex matching drive-letter or UNC paths and/or
filenames ending with .exe) before picking the executable to return; update the
logic around spawnSync/whereResult/candidates so only real path-like candidates
are considered when deciding the fallback command.

22-66: ⚠️ Potential issue | 🟡 Minor

add regression tests for windows resolver and cleanup behavior

test coverage is missing for the new resolveCodexExecutable() and stopCodexServers() functions. add tests for windows where parsing edge cases (no results, stderr, .exe vs .cmd detection) and env override paths. also test cleanup safety: the stopCodexServers() function runs taskkill then pkill without coordination, creating a race condition if concurrent test runs exit at the same time.

scripts/test-model-matrix.js:22-66 (resolver), scripts/test-model-matrix.js:116-121 (cleanup); expected tests in test/...

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test-model-matrix.js` around lines 22 - 66, Add unit tests under
test/ that cover resolveCodexExecutable and stopCodexServers from
scripts/test-model-matrix.js: for resolveCodexExecutable, mock
child_process.spawnSync and process.env.CODEX_BIN to assert behavior for env
override (trim, .cmd detection -> shell:true), Windows no-results (returns
"Codex"), where output in stdout vs stderr, exact npm\\Codex.exe vs
npm\\Codex.cmd selection, and fallback to first candidate and any .cmd
detection; for stopCodexServers, add tests that mock spawn/spawnSync to simulate
taskkill success/failure and delayed pkill so you can call stopCodexServers
concurrently (invoke twice) and assert both calls complete without throwing and
that taskkill is invoked before pkill (or that pkill is resilient), plus a test
that one of the commands fails but the function still returns/cleans up safely.
Ensure tests reference the resolveCodexExecutable and stopCodexServers symbols
when importing.
test/cli.test.ts (1)

298-304: ⚠️ Potential issue | 🔴 Critical

case mismatch breaks this test: TERM_PROGRAM = "Codex" won't match implementation check for "codex" (lowercase).

test/cli.test.ts:300 sets process.env.TERM_PROGRAM = "Codex" but lib/cli.ts:22 checks process.env.TERM_PROGRAM === "codex". the test assertion expect(isNonInteractiveMode()).toBe(true) will fail because javascript string comparison is case-sensitive. this test does not validate the intended non-interactive mode behavior and should either fix the test to use lowercase "codex" or fix the implementation to use a case-insensitive check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cli.test.ts` around lines 298 - 304, The test fails due to
case-sensitive comparison of TERM_PROGRAM; update the isNonInteractiveMode
function to perform a case-insensitive check by normalizing
process.env.TERM_PROGRAM (e.g., using process.env.TERM_PROGRAM?.toLowerCase())
before comparing to "codex" and ensure you guard for undefined/null so the
function still returns correct boolean values.
test/hashline-tools.test.ts (1)

149-176: 🧹 Nitpick | 🔵 Trivial

consider adding windows ebusy test case.

test/hashline-tools.test.ts:149-176 tests the edit tool's happy path. lib/tools/hashline-tools.ts:584 and :618 use writeFile without retry logic for windows EBUSY errors when files are locked. per coding guidelines, tests should cover windows filesystem behavior edge cases.

want me to draft a test case that simulates EBUSY retry scenarios for the hashline edit tool?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/hashline-tools.test.ts` around lines 149 - 176, Add a Windows EBUSY
retry test alongside the existing "executes edit tool in hashline mode" by
mocking the file write operation used in createHashlineEditTool's execute flow
to throw an error with code "EBUSY" on the first attempt and succeed on the
second; call editTool.execute (same args: path, lineRef, operation, content) and
assert the final file contents match the replaced text, that the write was
retried (mock call count >= 2), and that context.ask behavior is unchanged.
Target the write points in lib/tools/hashline-tools.ts (the writeFile calls
around the referenced locations) by stubbing or spying the module-level
writeFile used by createHashlineEditTool, ensure the mock restores after the
test, and include a variant that simulates persistent EBUSY to assert the tool
surface an appropriate error if retries exhaust.
lib/tools/hashline-tools.ts (1)

4-8: ⚠️ Potential issue | 🔴 Critical

import path migrated to @codex-ai/plugin/tool, but dist files missing from vendor package and EBUSY handling needed per windows filesystem guidelines.

the import change is correct—test file already uses @codex-ai/plugin/tool successfully (18 tests in test/hashline-tools.test.ts:5). however, two issues block this:

  1. vendor/codex-ai-plugin/dist/ does not exist. the package.json exports ./dist/tool.d.ts and ./dist/tool.js but neither file is in the repository. ensure npm run build or tsc output includes vendor dist files before merge.

  2. writeFile calls lack EBUSY handling. lib/tools/hashline-tools.ts:584 and :618 use generic error catch but don't retry on EBUSY (windows filesystem lock). per coding guidelines for lib/**, implement retry with exponential backoff for EBUSY or add a helper from lib/request/rate-limit-backoff.ts.

no token/email leaks in logging detected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tools/hashline-tools.ts` around lines 4 - 8, The vendor package is
missing its compiled dist outputs and writeFile calls need EBUSY retry handling:
first, ensure the codex-ai-plugin package is built before merging by running the
project's build/tsc step so that ./dist/tool.js and ./dist/tool.d.ts are present
in the vendor package (update CI/build script if needed); second, update the two
failing write operations in lib/tools/hashline-tools.ts (the writeFile usages
around the blocks referenced at :584 and :618) to catch EBUSY and retry with
exponential backoff (or reuse the existing backoff helper from
lib/request/rate-limit-backoff.ts) instead of failing immediately—implement a
small retry loop that checks for err.code === 'EBUSY', waits with increasing
delay, and reattempts the fs write before throwing the error.
package.json (1)

85-101: ⚠️ Potential issue | 🟠 Major

runtime file dependency breaks published installs.

@codex-ai/plugin at line 101 uses file:vendor/codex-ai-plugin as a runtime dependency, but files[] doesn't include vendor/. consumers who install from npm won't get the plugin files, breaking resolution at lib/request/fetch-helpers.ts:6 and similar usage points.

add vendor/codex-ai-plugin/ to files[] and configure bundleDependencies to ensure the local dependency ships with the package:

packaging-safe fix
   "files": [
     "dist/",
     "assets/",
     "config/",
     "scripts/",
+    "vendor/codex-ai-plugin/",
     "README.md",
     "LICENSE"
   ],
+  "bundleDependencies": [
+    "@codex-ai/plugin"
+  ],

missing: no regression test for published artifacts. also note that @codex-ai/sdk in devDependencies has the same issue (file:vendor/codex-ai-sdk), though dev-only scope reduces impact.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 85 - 101, The package.json currently references
runtime local file deps ("@codex-ai/plugin": "file:vendor/codex-ai-plugin")
which are not included in the published tarball; update package.json to add
"vendor/codex-ai-plugin/" (and also "vendor/codex-ai-sdk/" if needed) to the
top-level "files" array and add a "bundleDependencies" (or
"bundledDependencies") entry that lists these vendor folders so they are
packaged with the module, then add a regression test that installs the generated
package (npm pack / npm install ./pkg.tgz) and verifies resolution of
lib/request/fetch-helpers.ts and similar import points to prevent regressions.
lib/runtime-paths.ts (1)

116-129: ⚠️ Potential issue | 🟠 Major

add vitest regressions for legacy-root precedence and windows case-folding before merge.

lib/runtime-paths.ts:116-124 now includes a root legacy candidate from lib/runtime-paths.ts:171-173. this can change which directory wins (<codexHome>/multi-auth vs <home>/.codex) and is sensitive to case-insensitive comparisons on windows. i do not see matching runtime-path tests in the touched set.

As per coding guidelines, lib/**: focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios.

Also applies to: 171-173

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/runtime-paths.ts` around lines 116 - 129, The change introduced a legacy
root candidate into fallbackCandidates which can alter precedence between
getFallbackCodexHomeDirs().map(... "multi-auth") and getLegacyCodexDir(), and it
is sensitive to Windows case-folding; add vitest tests that exercise
runtimePaths resolution using the functions
fallbackCandidates/deduplicatePaths/getFallbackCodexHomeDirs/getLegacyCodexDir/hasStorageSignals/primary
to assert that (1) legacy-root precedence is correct (legacy wins only when
hasStorageSignals returns true and candidate !== primary) and (2) Windows-style
case-insensitive duplicates are treated as equal (normalize paths for
comparisons on Windows or make deduplicatePaths perform case-insensitive dedupe
when process.platform === "win32"); also add tests simulating concurrent
IO/queue behavior to ensure new queues surface and handle EBUSY and 429 retry
semantics (retries/backoff) so these scenarios are covered by vitest before
merge.
test/host-codex-prompt.test.ts (1)

181-209: ⚠️ Potential issue | 🟠 Major

remove timer-based synchronization and add a concurrent stale-refresh regression.

test/host-codex-prompt.test.ts:201 uses setTimeout(..., 0) as the barrier. this is flaky under scheduler variance and does not prove concurrency behavior for stale refresh in test/host-codex-prompt.test.ts:181-209.

proposed fix
-      await new Promise((resolve) => setTimeout(resolve, 0));
+      await vi.waitFor(() =>
+        expect(writeFile).toHaveBeenCalledWith(
+          expect.stringContaining("host-codex-prompt.txt"),
+          "New content",
+          "utf-8",
+        ),
+      );
       const second = await getHostCodexPrompt();

also add a regression case that runs Promise.all([getHostCodexPrompt(), getHostCodexPrompt()]) against stale cache and asserts a single refresh path is executed.

As per coding guidelines, test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/host-codex-prompt.test.ts` around lines 181 - 209, Replace the flaky
setTimeout-based synchronization in the test for getHostCodexPrompt with a
deterministic concurrent regression: remove the setTimeout barrier and add an
assertion that calling Promise.all([getHostCodexPrompt(), getHostCodexPrompt()])
against a stale cache triggers exactly one network refresh and one disk write;
keep the stale cache setup (readFile mocked to return "Old cached content" and a
lastChecked older than the freshness window), mock fetch to resolve with "New
content" and the new etag, then await Promise.all(...) and assert the returned
values and that mockFetch (or fetch) and writeFile were called once (use
vi.mocked and expect(...).toHaveBeenCalledTimes(1)); use Vitest utilities (vi)
for deterministic behavior rather than setTimeout so the test deterministically
verifies the single-refresh concurrency path for getHostCodexPrompt.
scripts/bench-format/codex-host.mjs (1)

23-33: ⚠️ Potential issue | 🟠 Major

stderr text can become the executable path on windows—add validation and regression test.

scripts/bench-format/codex-host.mjs:27 mixes stderr into candidates. when where Codex fails on windows, stderr text (e.g. "INFO: could not find...") passes the filter(Boolean) check. lines 36-49 validate specific patterns but line 51 returns candidates[0] unvalidated, so stderr text bypasses all checks and becomes the command.

remove stderr from candidates or validate that each candidate is a real path before returning it on line 51. also check whereResult.status !== 0 to detect command failure. add a regression test for stderr-only where output on windows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/bench-format/codex-host.mjs` around lines 23 - 33, The current logic
builds candidates from both whereResult.stdout and whereResult.stderr, letting
error messages become a returned command; update the logic in
scripts/bench-format/codex-host.mjs to (1) check whereResult.status !== 0 and
treat that as failure before using output, (2) only use whereResult.stdout (drop
stderr) or validate each candidate with fs.existsSync/path.isAbsolute to ensure
it is a real executable path before returning candidates[0], and (3) add a
regression test that simulates a Windows `where` that writes only to stderr
(non-zero status or stderr text) to ensure we never accept stderr-only output as
a command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Line 87: Remove the trailing blank line at the end of the CI workflow file so
the YAML ends cleanly (no extra empty line); open the workflow file and delete
the final blank line so yamllint’s empty-lines rule passes and commit the
change.

In `@AGENTS.md`:
- Around line 67-68: Update the documented account paths in AGENTS.md to include
the missing multi-auth directory level: change the per-project path to
"~/.codex/multi-auth/projects/<project-key>/openai-codex-accounts.json" and the
global path to "~/.codex/multi-auth/openai-codex-accounts.json"; use
lib/runtime-paths.ts (lines around the logic in runtimePaths) and
lib/storage/paths.ts as the source of truth to ensure the strings match the
actual resolved paths and any path formatting used elsewhere.

In `@config/codex-legacy.json`:
- Around line 572-573: Remove the two extra blank lines at the end of the JSON
file so the file ends with a single newline (no trailing empty lines); open the
file that contains the codex-legacy JSON content, delete the extra blank lines
at EOF, and ensure the file terminates with just one newline character.

In `@config/codex-modern.json`:
- Line 2: The $schema value in config/codex-modern.json points to an endpoint
that returns HTML instead of a JSON Schema; update the "$schema" property to
reference a valid JSON Schema URL (or a local schema file) so IDEs and tooling
can validate the JSON correctly. Locate the "$schema" key in
config/codex-modern.json and replace "https://codex.ai/config.json" with a URL
that serves a proper JSON Schema (or "./schema/config.schema.json" if you add a
local schema file), ensuring the referenced resource returns application/json
and a valid schema document.

In `@config/minimal-codex.json`:
- Line 2: The $schema value in config/minimal-codex.json is pointing to a broken
URL ("https://codex.ai/config.json") that redirects; update the "$schema" entry
to the correct schema endpoint (e.g. "https://opencode.ai/config.json") or
restore the original valid schema URL so IDEs can validate the file; locate and
modify the "$schema" key in the JSON to the verified URL.

In `@config/README.md`:
- Around line 7-11: Add required blank lines around the markdown heading and the
table and fix the fenced-code-block formatting: ensure there's a blank line
before and after the heading that introduces the table, a blank line above and
below the table containing the rows for `codex-modern.json`,
`codex-legacy.json`, and `minimal-codex.json`, and make sure any fenced code
block uses proper triple-backticks (with an optional language) and is separated
from surrounding text with blank lines so markdownlint warnings for
headings/tables/fenced code blocks are resolved.

In `@eslint.config.js`:
- Line 6: The new ignores entry adds "vendor/**" to the ignores array in
eslint.config.js but lacks a regression guard; add a lightweight CI test that
verifies ESLint skips files under the vendor directory to prevent future
removal. Create a fixture file under a vendor/ directory (e.g.,
vendor/should-be-ignored.js) and a small CI script or test that runs ESLint
against that path and asserts zero lint results (or that the file is not
reported), failing the run if the file is linted; reference the ignores array
and the "vendor/**" glob when implementing the check so reviewers can easily
find the change.

In `@lib/cli.ts`:
- Around line 351-352: Remove the extra trailing blank lines at the end of
lib/cli.ts so the file ends with a single newline (or no blank lines) per
project style; open lib/cli.ts, go to the end-of-file and delete the extra empty
lines after the last token (ensure the file still ends with a single newline
character).
- Around line 17-25: The TERM_PROGRAM comparison in isNonInteractiveMode
currently uses a case-sensitive equality check which conflicts with the test
(test sets "Codex"); update isNonInteractiveMode to normalize
process.env.TERM_PROGRAM (e.g., use .toLowerCase() safely when defined) and
compare to "codex" so the check is case-insensitive, or alternatively adjust the
test to set TERM_PROGRAM = "codex"—prefer making the function robust by
normalizing TERM_PROGRAM before comparing.

In `@lib/config.ts`:
- Around line 20-23: The legacy auth fallback currently only checks
LEGACY_CODEX_AUTH_CONFIG_PATH (built from getLegacyCodexDir()) and thus ignores
a customized CODEX_HOME; add a new constant LEGACY_CODEX_HOME_AUTH_CONFIG_PATH
analogous to LEGACY_CODEX_HOME_CONFIG_PATH that points to
`${CODEX_HOME}/openai-codex-auth-config.json`, update the fallback chain in
resolvePluginConfigPath (or wherever LEGACY_CODEX_AUTH_CONFIG_PATH is used) to
check LEGACY_CODEX_HOME_AUTH_CONFIG_PATH before falling back to
LEGACY_CODEX_AUTH_CONFIG_PATH, and add a vitest in test/config.test.ts that sets
process.env.CODEX_HOME to a temp dir containing openai-codex-auth-config.json to
assert the new branch is exercised.

In `@lib/prompts/host-codex-prompt.ts`:
- Around line 27-28: The new constants CACHE_FILE and CACHE_META_FILE replaced
previous cache filenames but lack a legacy-read/migration path; update the
module that reads/writes the cache (referencing CACHE_FILE and CACHE_META_FILE
in lib/prompts/host-codex-prompt.ts) to: 1) attempt to read the new files first,
2) if missing, attempt to read the legacy filenames and on success write them
back to CACHE_FILE/CACHE_META_FILE (atomic rename or write+fsync) to migrate,
and 3) retry/mask transient filesystem errors (EBUSY) and rate/IO-like failures
(treat 429-like conditions as transient and retry with backoff) to handle
Windows IO/concurrency; add a vitest regression that seeds the old filenames,
initializes the module to prove it migrates to the new
CACHE_FILE/CACHE_META_FILE and remains offline-capable, and update tests to
simulate EBUSY/429 transient errors to verify retry behavior.

In `@lib/request/fetch-helpers.ts`:
- Around line 339-348: The token refresh path currently assumes client.auth.set
exists and double-casts to CodexAuthSetter before calling it; add an explicit
guard around the call that checks client.auth exists and typeof client.auth.set
=== "function" (where the current call happens around the Cast to
CodexAuthSetter and the await client.auth.set({...}) invocation) and if missing
throw a clear Error describing that auth.set is not available for rotation;
update the token refresh handling to skip or fail safely when the guard fails.
Also add a vitest regression in test/fetch-helpers.test.ts that simulates a
client lacking auth or having auth.set as non-function and asserts the refresh
path throws the expected error.

In `@lib/request/request-transformer.ts`:
- Around line 327-328: The code calls
parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE) twice with the
nullish coalescing operator, making the right-hand call dead code; replace the
`parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE) ??
parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE)` expression with a
single call to parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE), or
if a fallback was intended, use a real fallback value (e.g.,
parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE) ??
<desiredDefault>) so the intent is clear; update the occurrence referencing
parseCollaborationMode and CODEX_COLLABORATION_MODE accordingly.

In `@scripts/bench-format/codex-host.mjs`:
- Around line 19-21: The POSIX branch returns the wrong capitalized executable
name ("Codex"), causing ENOENT; change both occurrences of the return object
that set command: "Codex" to command: "codex" (i.e., in the process.platform !==
"win32" branch and the other return around the non-windows resolver path) so the
lowercase POSIX binary is used, and add a regression test that simulates a
non-windows platform (or calls the resolver function directly) to assert the
returned command is "codex".

In `@scripts/bench-format/models.mjs`:
- Line 2: The import in models.mjs is pointing to the wrong module name; update
the import that brings in resolveCodexExecutable so it references the existing
module (codex-host.mjs) instead of ./Codex.mjs. Locate the import of
resolveCodexExecutable in models.mjs and change its module specifier to the
correct file name (codex-host.mjs) so runtime imports (e.g.,
scripts/benchmark-edit-formats.mjs) succeed.

In `@scripts/benchmark-edit-formats.mjs`:
- Around line 13-16: Update the broken import in
scripts/benchmark-edit-formats.mjs to import the actual module that exports
getRepoRoot, resolveCodexExecutable, runCodexJson, etc. (replace the current
import source that references Codex.mjs with the module that contains those
exports, e.g., the codex-host module) and add a small smoke test that imports
the same module and calls a trivial function (e.g., getRepoRoot or
resolveCodexExecutable) to verify the import path resolves at runtime so this
cannot regress on case-sensitive filesystems.

In `@scripts/install-codex-auth.js`:
- Around line 36-40: The template filename casing in
scripts/install-codex-auth.js is incorrect: update the templatePath construction
(the join call that uses useLegacy ? "Codex-legacy.json" : "Codex-modern.json")
to use the actual lowercase filenames "codex-legacy.json" and
"codex-modern.json" so existsSync-based checks succeed on case-sensitive
filesystems; after fixing templatePath, add a regression test that simulates or
runs on a case-sensitive filesystem (or a case-sensitive temp directory) to
assert the script finds the template files (i.e., exercises the code path that
constructs templatePath and calls fs.existsSync) to prevent future regressions.
- Around line 42-44: The installer lacks tests and Windows compatibility: add
unit tests for normalizePluginList covering empty arrays, non-string entries,
and duplicates; add tests that the dry-run flag prevents any file writes (no
deletes, no backups); add tests for backup creation and restore logic
(referencing the backup/restore functions used around line ~155) and for cache
clearing atomicity under concurrent runs (target the cache clear function, e.g.,
clearCache or equivalent). Fix path portability by using APPDATA fallback for
Windows (replace usage of configDir/configPath/cacheDir that build "~/.config"
and "~/.cache" with a helper that uses process.env.APPDATA on Windows and
otherwise XDG paths), and make writes to Codex.json safe by introducing file
locking/atomic replace (lock the config file or write to a temp file and
atomically rename) so backup/restore and concurrent clear operations cannot
corrupt state; ensure dry-run mode bypasses locking/writes so tests can validate
no changes.

In `@scripts/test-model-matrix.js`:
- Around line 116-121: The stopCodexServers function currently issues global
kills (taskkill /IM "Codex.exe" and pkill -f "Codex"), which can terminate other
concurrent runs; change the cleanup to only target this run's processes by
tracking PIDs when you spawn Codex (e.g., store childProcess.pid from wherever
you start Codex) and then have stopCodexServers call process.kill(pid) on those
tracked PIDs (or use taskkill/pkill with the specific PID list) instead of
name/image-wide kills; update the launcher/spawn logic to record those PIDs in a
run-scoped array and reference that array in stopCodexServers to perform safe,
per-run termination.
- Around line 201-206: Add a timeout to the spawnSync call that runs external
model providers: update the options passed to spawnSync(CodexExecutable.command,
args, { ... }) to include a timeout (e.g. timeout: 120000) so the process cannot
hang indefinitely, and then detect and handle a timeout by checking
finalized.error (or finalized.signal/status) after the call — log an explicit
timeout error and exit non-zero (or fail the test) when the timeout occurs;
reference the spawnSync call and the finalized variable (from
CodexExecutable.command invocation) when making these changes.

In `@test/chaos/fault-injection.test.ts`:
- Line 14: The test imports the Auth type from `@codex-ai/sdk` in
test/chaos/fault-injection.test.ts but the vendor package (vendor/codex-ai-sdk)
only contains a package.json pointing to ./dist/index.d.ts and ./dist/index.js
with no dist/, causing type resolution failures where Auth is used (in the test
at the Auth references around lines 238, 243, 248, 253, 258, 264). Fix by
producing the missing dist artifacts for the vendor package (build the SDK so
./dist/index.d.ts and ./dist/index.js exist and are published into
vendor/codex-ai-sdk) or, if intended, update the package stub to include the
correct type definitions (or replace the import with a local type declaration
for Auth) so the Auth symbol resolves during typecheck.

In `@test/config-files.test.ts`:
- Around line 212-216: The boolean expression for hasCodexIgnores contains a
duplicate check for '.codex'; update the expression in test/config-files.test.ts
(the hasCodexIgnores variable that uses content.includes(...)) to remove the
redundant content.includes('.codex') so each check is unique (e.g., keep
content.includes('Codex.json') and one content.includes('.codex') or add the
intended alternate if a different ignore filename was meant).

In `@test/copy-oauth-success.test.ts`:
- Line 15: The temp directory prefix passed to mkdtemp currently uses
"Codex-oauth-success-" with a capital C; change that literal to
"codex-oauth-success-" to match project casing conventions where names like
.codex and codex-multi-auth are lowercase (update the mkdtemp call in the test
that uses mkdtemp(join(tmpdir(), "Codex-oauth-success-")) accordingly).

In `@test/documentation.test.ts`:
- Around line 94-99: The test in the it block that iterates over userDocs
lowercases file content into the variable content but then checks for 'Codex'
(capital C), causing the check to never match; update the assertion in that test
to search for the lowercase string 'codex' (e.g., content.includes('codex')) or
remove the toLowerCase() call so the check and content case match; modify the
check around the variable content and the contains check to use the same casing
to make the expect in the test actually validate for references to Codex.

In `@test/package-bin.test.ts`:
- Line 12: The test is asserting the wrong bin key due to inconsistent casing;
update the assertion that currently checks
pkg.bin?.["codex-multi-auth-Codex-install"] to use the correct kebab-case
key—either pkg.bin?.["codex-multi-auth-codex-install"] if you meant to check the
codex entry, or pkg.bin?.["codex-multi-auth-opencode-install"] if the intent was
to verify the old opencode entry was removed—so the test key matches the
project's kebab-case naming.

In `@test/README.md`:
- Around line 204-208: Update the README section to remove the trailing blank
lines and ensure the config references consistently use the new codex-era names:
replace any lingering legacy/modern mentions with `Codex-legacy.json` and
`Codex-modern.json`, then trim the extra blank lines after that list so the
block ends immediately after the second item.

In `@test/request-transformer.test.ts`:
- Line 14: Replace the hard-coded string "Codex Host Bridge" in the assertions
with the imported constant CODEX_HOST_BRIDGE (the file already imports it).
Locate the assertions in test/request-transformer.test.ts that currently compare
to the literal "Codex Host Bridge" and update them to assert against
CODEX_HOST_BRIDGE instead, preserving the existing assertion method (e.g.,
expect(...).toEqual(...)) and surrounding test logic.

---

Outside diff comments:
In `@lib/runtime-paths.ts`:
- Around line 116-129: The change introduced a legacy root candidate into
fallbackCandidates which can alter precedence between
getFallbackCodexHomeDirs().map(... "multi-auth") and getLegacyCodexDir(), and it
is sensitive to Windows case-folding; add vitest tests that exercise
runtimePaths resolution using the functions
fallbackCandidates/deduplicatePaths/getFallbackCodexHomeDirs/getLegacyCodexDir/hasStorageSignals/primary
to assert that (1) legacy-root precedence is correct (legacy wins only when
hasStorageSignals returns true and candidate !== primary) and (2) Windows-style
case-insensitive duplicates are treated as equal (normalize paths for
comparisons on Windows or make deduplicatePaths perform case-insensitive dedupe
when process.platform === "win32"); also add tests simulating concurrent
IO/queue behavior to ensure new queues surface and handle EBUSY and 429 retry
semantics (retries/backoff) so these scenarios are covered by vitest before
merge.

In `@lib/tools/hashline-tools.ts`:
- Around line 4-8: The vendor package is missing its compiled dist outputs and
writeFile calls need EBUSY retry handling: first, ensure the codex-ai-plugin
package is built before merging by running the project's build/tsc step so that
./dist/tool.js and ./dist/tool.d.ts are present in the vendor package (update
CI/build script if needed); second, update the two failing write operations in
lib/tools/hashline-tools.ts (the writeFile usages around the blocks referenced
at :584 and :618) to catch EBUSY and retry with exponential backoff (or reuse
the existing backoff helper from lib/request/rate-limit-backoff.ts) instead of
failing immediately—implement a small retry loop that checks for err.code ===
'EBUSY', waits with increasing delay, and reattempts the fs write before
throwing the error.

In `@package.json`:
- Around line 85-101: The package.json currently references runtime local file
deps ("@codex-ai/plugin": "file:vendor/codex-ai-plugin") which are not included
in the published tarball; update package.json to add "vendor/codex-ai-plugin/"
(and also "vendor/codex-ai-sdk/" if needed) to the top-level "files" array and
add a "bundleDependencies" (or "bundledDependencies") entry that lists these
vendor folders so they are packaged with the module, then add a regression test
that installs the generated package (npm pack / npm install ./pkg.tgz) and
verifies resolution of lib/request/fetch-helpers.ts and similar import points to
prevent regressions.

In `@scripts/bench-format/codex-host.mjs`:
- Around line 23-33: The current logic builds candidates from both
whereResult.stdout and whereResult.stderr, letting error messages become a
returned command; update the logic in scripts/bench-format/codex-host.mjs to (1)
check whereResult.status !== 0 and treat that as failure before using output,
(2) only use whereResult.stdout (drop stderr) or validate each candidate with
fs.existsSync/path.isAbsolute to ensure it is a real executable path before
returning candidates[0], and (3) add a regression test that simulates a Windows
`where` that writes only to stderr (non-zero status or stderr text) to ensure we
never accept stderr-only output as a command.

In `@scripts/install-codex-auth.js`:
- Around line 134-137: Wrap the cache removal calls that use
rm(cacheNodeModules, { recursive: true, force: true }) and rm(cacheBunLock, {
force: true }) in a try/catch so failures (e.g., file locks on Windows or
concurrent access) do not abort the install; on catch, log a non-fatal warning
(e.g., console.warn or processLogger.warn) including the caught error and
continue instead of rethrowing, preserving the existing behavior when removals
succeed.
- Around line 159-164: The current logic unconditionally overwrites
provider.openai and can throw if template.provider is undefined; update the
merge so it first checks template.provider and template.provider.openai exist,
then merge template.provider.openai into the existing provider without
overwriting existing keys (i.e., only fill missing fields), preserving
existing.provider values like apiKey/baseUrl, and assign the result back to
merged.provider and nextConfig; reference the variables provider, existing,
template, merged, and nextConfig when implementing the defensive check and
non-destructive merge.

In `@scripts/test-model-matrix.js`:
- Around line 33-44: The merged stdout/stderr can include non-path error text
from whereResult which then becomes candidates[0] and is mistakenly returned as
the command; change the filtering of candidates (the variable computed from
whereResult) to exclude stderr error lines by only keeping entries that look
like valid Windows paths (e.g. path.isAbsolute(line) or regex matching
drive-letter or UNC paths and/or filenames ending with .exe) before picking the
executable to return; update the logic around spawnSync/whereResult/candidates
so only real path-like candidates are considered when deciding the fallback
command.
- Around line 22-66: Add unit tests under test/ that cover
resolveCodexExecutable and stopCodexServers from scripts/test-model-matrix.js:
for resolveCodexExecutable, mock child_process.spawnSync and
process.env.CODEX_BIN to assert behavior for env override (trim, .cmd detection
-> shell:true), Windows no-results (returns "Codex"), where output in stdout vs
stderr, exact npm\\Codex.exe vs npm\\Codex.cmd selection, and fallback to first
candidate and any .cmd detection; for stopCodexServers, add tests that mock
spawn/spawnSync to simulate taskkill success/failure and delayed pkill so you
can call stopCodexServers concurrently (invoke twice) and assert both calls
complete without throwing and that taskkill is invoked before pkill (or that
pkill is resilient), plus a test that one of the commands fails but the function
still returns/cleans up safely. Ensure tests reference the
resolveCodexExecutable and stopCodexServers symbols when importing.

In `@test/cli.test.ts`:
- Around line 298-304: The test fails due to case-sensitive comparison of
TERM_PROGRAM; update the isNonInteractiveMode function to perform a
case-insensitive check by normalizing process.env.TERM_PROGRAM (e.g., using
process.env.TERM_PROGRAM?.toLowerCase()) before comparing to "codex" and ensure
you guard for undefined/null so the function still returns correct boolean
values.

In `@test/hashline-tools.test.ts`:
- Around line 149-176: Add a Windows EBUSY retry test alongside the existing
"executes edit tool in hashline mode" by mocking the file write operation used
in createHashlineEditTool's execute flow to throw an error with code "EBUSY" on
the first attempt and succeed on the second; call editTool.execute (same args:
path, lineRef, operation, content) and assert the final file contents match the
replaced text, that the write was retried (mock call count >= 2), and that
context.ask behavior is unchanged. Target the write points in
lib/tools/hashline-tools.ts (the writeFile calls around the referenced
locations) by stubbing or spying the module-level writeFile used by
createHashlineEditTool, ensure the mock restores after the test, and include a
variant that simulates persistent EBUSY to assert the tool surface an
appropriate error if retries exhaust.

In `@test/host-codex-prompt.test.ts`:
- Around line 181-209: Replace the flaky setTimeout-based synchronization in the
test for getHostCodexPrompt with a deterministic concurrent regression: remove
the setTimeout barrier and add an assertion that calling
Promise.all([getHostCodexPrompt(), getHostCodexPrompt()]) against a stale cache
triggers exactly one network refresh and one disk write; keep the stale cache
setup (readFile mocked to return "Old cached content" and a lastChecked older
than the freshness window), mock fetch to resolve with "New content" and the new
etag, then await Promise.all(...) and assert the returned values and that
mockFetch (or fetch) and writeFile were called once (use vi.mocked and
expect(...).toHaveBeenCalledTimes(1)); use Vitest utilities (vi) for
deterministic behavior rather than setTimeout so the test deterministically
verifies the single-refresh concurrency path for getHostCodexPrompt.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 97fd13d and 110af23.

⛔ Files ignored due to path filters (2)
  • assets/opencode-logo-ornate-dark.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (47)
  • .github/workflows/ci.yml
  • AGENTS.md
  • config/README.md
  • config/codex-legacy.json
  • config/codex-modern.json
  • config/minimal-codex.json
  • eslint.config.js
  • index.ts
  • lib/AGENTS.md
  • lib/accounts.ts
  • lib/cli.ts
  • lib/config.ts
  • lib/prompts/codex-host-bridge.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/recovery.ts
  • lib/recovery/storage.ts
  • lib/recovery/types.ts
  • lib/request/fetch-helpers.ts
  • lib/request/helpers/input-utils.ts
  • lib/request/request-transformer.ts
  • lib/runtime-paths.ts
  • lib/tools/hashline-tools.ts
  • lib/types.ts
  • package.json
  • scripts/bench-format/codex-host.mjs
  • scripts/bench-format/models.mjs
  • scripts/benchmark-edit-formats.mjs
  • scripts/install-codex-auth.js
  • scripts/test-model-matrix.js
  • test/AGENTS.md
  • test/README.md
  • test/chaos/fault-injection.test.ts
  • test/cli.test.ts
  • test/config-files.test.ts
  • test/copy-oauth-success.test.ts
  • test/documentation.test.ts
  • test/hashline-tools.test.ts
  • test/host-codex-prompt.test.ts
  • test/index-retry.test.ts
  • test/index.test.ts
  • test/input-utils.test.ts
  • test/package-bin.test.ts
  • test/request-transformer.test.ts
  • test/storage.test.ts
  • vendor/codex-ai-plugin/package.json
  • vendor/codex-ai-sdk/package.json
  • vitest.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/config-files.test.ts
  • test/storage.test.ts
  • test/AGENTS.md
  • test/cli.test.ts
  • test/copy-oauth-success.test.ts
  • test/package-bin.test.ts
  • test/documentation.test.ts
  • test/index.test.ts
  • test/index-retry.test.ts
  • test/request-transformer.test.ts
  • test/chaos/fault-injection.test.ts
  • test/hashline-tools.test.ts
  • test/input-utils.test.ts
  • test/README.md
  • test/host-codex-prompt.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/AGENTS.md
  • lib/cli.ts
  • lib/request/fetch-helpers.ts
  • lib/runtime-paths.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/recovery/storage.ts
  • lib/tools/hashline-tools.ts
  • lib/types.ts
  • lib/prompts/codex-host-bridge.ts
  • lib/request/helpers/input-utils.ts
  • lib/recovery/types.ts
  • lib/recovery.ts
  • lib/request/request-transformer.ts
  • lib/accounts.ts
  • lib/config.ts
🧬 Code graph analysis (12)
test/config-files.test.ts (1)
scripts/install-codex-auth.js (1)
  • content (67-67)
test/cli.test.ts (1)
lib/cli.ts (1)
  • isNonInteractiveMode (17-25)
scripts/bench-format/codex-host.mjs (1)
scripts/test-model-matrix.js (6)
  • envOverride (23-23)
  • command (25-25)
  • whereResult (33-36)
  • exactExe (46-48)
  • candidates (37-40)
  • exactCmd (53-55)
lib/prompts/host-codex-prompt.ts (2)
lib/runtime-paths.ts (1)
  • getCodexCacheDir (142-144)
lib/logger.ts (2)
  • error (389-393)
  • logDebug (325-331)
test/request-transformer.test.ts (3)
lib/types.ts (2)
  • InputItem (81-87)
  • RequestBody (92-113)
lib/request/helpers/input-utils.ts (2)
  • isHostSystemPrompt (60-92)
  • filterHostSystemPromptsWithCachedPrompt (94-115)
lib/request/request-transformer.ts (3)
  • isHostSystemPrompt (27-27)
  • filterHostSystemPromptsWithCachedPrompt (28-28)
  • filterHostSystemPrompts (739-754)
lib/request/helpers/input-utils.ts (1)
lib/request/request-transformer.ts (2)
  • isHostSystemPrompt (27-27)
  • filterHostSystemPromptsWithCachedPrompt (28-28)
test/input-utils.test.ts (1)
lib/request/helpers/input-utils.ts (2)
  • isHostSystemPrompt (60-92)
  • filterHostSystemPromptsWithCachedPrompt (94-115)
lib/config.ts (1)
lib/runtime-paths.ts (2)
  • getCodexHomeDir (13-16)
  • getLegacyCodexDir (171-173)
scripts/benchmark-edit-formats.mjs (2)
scripts/bench-format/codex-host.mjs (2)
  • runCodexJson (148-205)
  • resolveCodexExecutable (12-52)
scripts/bench-format/models.mjs (2)
  • executable (51-51)
  • listCodexModels (50-67)
index.ts (1)
lib/prompts/host-codex-prompt.ts (1)
  • prewarmHostCodexPrompt (305-309)
test/host-codex-prompt.test.ts (1)
lib/prompts/host-codex-prompt.ts (1)
  • getHostCodexPrompt (255-286)
scripts/test-model-matrix.js (1)
scripts/bench-format/codex-host.mjs (8)
  • resolveCodexExecutable (12-52)
  • envOverride (13-13)
  • command (15-15)
  • whereResult (23-26)
  • exactExe (36-36)
  • candidates (27-30)
  • exactCmd (41-41)
  • args (160-160)
🪛 markdownlint-cli2 (0.21.0)
config/README.md

[warning] 7-7: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 10-10: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 11-11: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 11-11: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 YAMLlint (1.38.0)
.github/workflows/ci.yml

[error] 87-87: too many blank lines (1 > 0)

(empty-lines)

🔇 Additional comments (33)
test/storage.test.ts (1)

299-299: looks good.

this rename is correct for codex-only migration and keeps the windows/unix eacces hint assertions aligned in test/storage.test.ts:299.

eslint.config.js (1)

77-77: no action needed for trailing newline.

this change is formatting-only.
refs: lib/...:line, test/...:line.

scripts/install-codex-auth.js (3)

13-27: lgtm on help text updates.

all user-facing strings correctly reference codex naming. consistent with the migration objectives.


42-44: ⚠️ Potential issue | 🔴 Critical

template filenames are lowercase but script references uppercase.

script references Codex-legacy.json and Codex-modern.json at scripts/install-codex-auth.js:36-39, but actual files in config/ are codex-legacy.json and codex-modern.json (lowercase). this causes immediate failure at scripts/install-codex-auth.js:84 when the template check runs.

fix the reference at scripts/install-codex-auth.js:39 to use lowercase filenames:

-	useLegacy ? "Codex-legacy.json" : "Codex-modern.json"
+	useLegacy ? "codex-legacy.json" : "codex-modern.json"

add regression test for the install script - it currently has no test coverage. at minimum, verify template loading succeeds with both --modern and --legacy flags.

concurrency risk in backup logic at scripts/install-codex-auth.js:13-17. timestamp collision is unlikely but possible within milliseconds if two installs run simultaneously. consider adding process id or random component to backupPath.

no regression tests exist for windows paths - verify mkdir(..., { recursive: true }) and path.join behavior on windows in a real test suite, not just node:path documentation.

Likely an incorrect or invalid review comment.


183-187: the version number is accurate. test/README.md confirms the config format split at v1.0.210: "Codex-modern.json: Variant-based example for host runtime v1.0.210+". legacy config supports v1.0.209 and older. no changes needed.

test/AGENTS.md (1)

36-36: documentation update looks good.

the test catalog entry correctly reflects the renamed file host-codex-prompt.test.ts. aligns with the broader codex-only migration.

test/chaos/fault-injection.test.ts (2)

236-267: good coverage on shouldRefreshToken edge cases.

tests cover non-oauth auth, missing access token, expired token, valid token, skew parameter, and negative skew clamping. this is solid regression coverage for token refresh races mentioned in the coding guidelines.


412-474: stress tests look deterministic and thorough.

test/chaos/fault-injection.test.ts:422-474 uses fake timers and property-based testing for rapid alternation and recovery scenarios. no flakiness concerns here.

lib/types.ts (2)

1-1: sdk import path updated consistently.

lib/types.ts:1 mirrors the change in test/chaos/fault-injection.test.ts:14. the Auth, Provider, and Model types are re-exported at line 143 for downstream consumers.


145-146: useful type alias for oauth-specific auth details.

OAuthAuthDetails extracts the oauth variant from the Auth union. this is cleaner than repeating Extract<Auth, { type: "oauth" }> everywhere.

test/copy-oauth-success.test.ts (1)

19-29: test cleanup is solid.

the finally block at test/copy-oauth-success.test.ts:27-29 ensures temp dirs are removed even on failure. good for avoiding test pollution on windows where leftover files can cause EBUSY issues.

lib/recovery/types.ts (1)

4-4: comment updated for codex branding.

lib/recovery/types.ts:4 changes the attribution comment. no functional impact.

vitest.config.ts (1)

10-14: exclusion patterns updated for codex directories.

vitest.config.ts:10,14 now excludes .codex/** instead of .opencode/**. this prevents test discovery from picking up cached codex artifacts.

lib/accounts.ts (1)

1-1: import namespace migration looks correct.

the Auth type import from @codex-ai/sdk aligns with the broader codex migration. no functional change expected since this is a type-only import.

lib/AGENTS.md (1)

35-36: documentation updates match the renamed files.

the updated references to codex-host-bridge.ts and host-codex-prompt.ts correctly reflect the module renames in lib/prompts/.

test/cli.test.ts (2)

282-296: environment variable renames look correct.

the tests now reference CODEX_TUI and CODEX_DESKTOP which aligns with the migration. cleanup at lines 287 and 295 properly deletes the env vars.


460-468: non-interactive mode tests updated correctly.

the beforeEach/afterEach at test/cli.test.ts:463-467 properly sets and cleans up CODEX_TUI.

lib/recovery/storage.ts (1)

1-5: comment update aligns with codex migration.

the module header at lib/recovery/storage.ts:4 now references codex-antigravity-auth. no functional changes in this file.

lib/recovery.ts (2)

1-1: import namespace migration is correct.

lib/recovery.ts:1 now imports PluginInput from @codex-ai/plugin. this aligns with the broader sdk migration.


108-112: body construction simplification is safe.

lib/recovery.ts:110 inlines the body object directly. functionally equivalent to the previous intermediate variable approach.

.github/workflows/ci.yml (1)

85-86: lgtm on the test path update.

the codex compatibility smoke step now runs the renamed test/host-codex-prompt.test.ts instead of the old opencode-codex test. aligns with the broader migration.

lib/tools/hashline-tools.ts (1)

629-629: trailing newline is fine.

extra blank line at eof is harmless. no action needed unless linter complains.

test/README.md (1)

36-36: test file reference updated correctly.

test/host-codex-prompt.test.ts matches the renamed test file from the migration.

test/hashline-tools.test.ts (1)

5-5: import migrated to @codex-ai/plugin/tool.

matches the source file lib/tools/hashline-tools.ts:8 migration.

AGENTS.md (1)

8-8: documentation updated for codex migration.

overview, config paths, and skill mapping references now use codex namespace. lgtm.

Also applies to: 17-17, 86-86

config/README.md (1)

5-19: config/README.md lines 13–19: remove incorrect compatibility aliases section.

the readme claims backward-compatible filenames (Codex-modern.json, Codex-legacy.json, minimal-Codex.json) are "still shipped" but these files don't exist in config/. only the lowercase variants (codex-modern.json, codex-legacy.json, minimal-codex.json) are present. either delete the aliases section or add the actual backward-compatible files if they're needed.

Likely an incorrect or invalid review comment.

config/codex-legacy.json (1)

2-2: schema url returns 302 redirect instead of valid json schema.

https://codex.ai/config.json redirects to https://generaltranslation.com rather than serving a schema. this will cause schema validation failures. fix the endpoint or use a local schema reference.

also note: .opencode.json:2 still references the old https://opencode.ai/config.json url and wasn't migrated. decide if it should be updated or if it's intentionally kept separate.

remove trailing blank lines at end of file (lines 572-573).

lib/prompts/codex-host-bridge.ts (1)

11-178: rename alignment looks good.

line 11, line 151, and line 159 in lib/prompts/codex-host-bridge.ts keep the bridge constant/interface/meta names consistent, with no new token or email logging surface.

test/index-retry.test.ts (1)

5-5: mock path migration stays deterministic.

line 5 in test/index-retry.test.ts updates only the mocked module path and keeps the test deterministic under vitest.

as per coding guidelines test/**: tests must stay deterministic and use vitest.

index.ts (1)

26-29: codex wiring is consistent across entrypoint and prompt module.

line 26, line 185, line 662, and line 1146 in index.ts are aligned with lib/prompts/host-codex-prompt.ts:305, and the test-side module rename at test/index-retry.test.ts:5 matches this direction.

Also applies to: 185-186, 662-663, 1145-1147

lib/request/helpers/input-utils.ts (1)

45-115: rename propagation is consistent and behavior-preserving.

lib/request/helpers/input-utils.ts:45-115 keeps the cached-prefix checks and preserved-context extraction intact while moving to host naming. this aligns cleanly with lib/request/request-transformer.ts:26-29.

test/input-utils.test.ts (1)

244-359: host helper rename coverage stays intact.

test/input-utils.test.ts:244-359 updates names consistently and still validates cached-prompt matching plus context-preserving filtering behavior.

test/index.test.ts (1)

5-5: module rename updates in mocks look correct.

test/index.test.ts:5 and test/index.test.ts:183-185 are consistent with the codex migration and keep test wiring aligned with current prompt entry points.

Also applies to: 183-185

Comment thread .github/workflows/ci.yml Outdated
Comment thread AGENTS.md Outdated
Comment thread config/codex-legacy.json Outdated
Comment thread config/codex-modern.json Outdated
Comment thread config/minimal-codex.json Outdated
Comment thread test/copy-oauth-success.test.ts Outdated
Comment thread test/documentation.test.ts Outdated
Comment thread test/package-bin.test.ts Outdated
Comment thread test/README.md Outdated
Comment thread test/request-transformer.test.ts

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/runtime-paths.ts`:
- Around line 75-83: Add vitest unit tests that exercise hasAccountsStorage and
getCodexMultiAuthDir: create temporary directories and assert
hasAccountsStorage(dir) returns true when "openai-codex-accounts.json" exists,
true when "codex-accounts.json" exists, and false when neither exists; then add
tests for getCodexMultiAuthDir to ensure it prefers candidate directories where
hasAccountsStorage returns true over ones that only contain signal files
(simulate candidates and verify ordering/selection). Use the same helper
utilities the test suite uses for creating temp dirs and cleaning up, and
reference the functions hasAccountsStorage and getCodexMultiAuthDir in the tests
so failures directly point to those implementations.
- Around line 127-139: Add vitest regression tests for getCodexMultiAuthDir:
cover (1) migration where fallback (e.g., "~/.codex") contains accounts but
primary "~/.codex/multi-auth" is empty so getCodexMultiAuthDir should return the
legacy path (exercise hasAccountsStorage preference), (2) the TOCTOU race by
stubbing or mocking hasAccountsStorage to change result between the initial
check and subsequent usage to ensure code behaves as expected under a race, (3)
Windows case-insensitive path normalization by passing candidate paths that
differ only by case and asserting normalization logic holds, and (4) env
override + fallback-with-accounts vs primary-with-signals-only by stubbing
hasStorageSignals and hasAccountsStorage to simulate signals-only on primary and
accounts on fallback and asserting the function returns the fallback; implement
these tests with vitest and use stubs/mocks for hasAccountsStorage and
hasStorageSignals (and any filesystem helpers used by getCodexMultiAuthDir) so
tests are deterministic.

In `@test/paths.test.ts`:
- Around line 68-82: The mocked existsSync in this test is too implicit and may
pass for the wrong reason; update the mock used in the "prefers fallback with
accounts when primary only has non-account signals" test so it mirrors the
earlier test's stubs: explicitly return true for primary's config.json,
dashboard-settings.json, projects (and settings.json as already present),
explicitly return false for primary's openai-codex-accounts.json and
codex-accounts.json, and explicitly return true for fallback's
openai-codex-accounts.json; keep getConfigDir() as the function under test so
the behavior is validated for the same path checks as the prior test.
- Around line 68-82: Add a Windows-specific parallel test in test/paths.test.ts
that mirrors the existing "prefers fallback with accounts" case but uses a
Windows homedir (e.g., "C:\\Users\\test") and Windows-style joins so
getConfigDir's fallback-with-accounts logic is exercised on win32; mock
homedir() to return the Windows path, use path.win32.join or construct "C:\\..."
paths for primary and fallback, reuse mockedExistsSync to return true/false for
the same config filenames (settings.json, openai-codex-accounts.json,
codex-accounts.json) at those Windows paths, and assert the result equals the
Windows-style fallback path to ensure getConfigDir behaves correctly on Windows
(similar to the existing getProjectConfigDir Windows test).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 110af23 and 4758003.

📒 Files selected for processing (2)
  • lib/runtime-paths.ts
  • test/paths.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/paths.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/runtime-paths.ts
🧬 Code graph analysis (1)
test/paths.test.ts (1)
lib/storage/paths.ts (1)
  • getConfigDir (23-25)
🔇 Additional comments (4)
test/paths.test.ts (1)

74-77: good: adding codex-accounts.json check aligns with migration.

the test now covers both legacy openai-codex-accounts.json and new codex-accounts.json account file names, which matches the opencode→codex migration intent.

lib/runtime-paths.ts (3)

191-193: rename getLegacyOpenCodeDirgetLegacyCodexDir looks correct.

the function now returns ~/.codex which aligns with the migration. no functional change beyond the name and path constant.

one nit: the legacy directory is now the same as getCodexHomeDir()'s default (join(homedir(), ".codex") at line 15). if CODEX_HOME is unset, these two functions return identical paths. this is probably intentional but worth a comment if the distinction matters for future maintainers.


122-126: potential duplicate candidate in fallback list.

getFallbackCodexHomeDirs() already includes join(homedir(), ".codex") (line 99), which maps to ~/.codex/multi-auth via line 124. then line 125 adds getLegacyCodexDir() which is ~/.codex directly.

so the fallback list contains both:

  • ~/.codex/multi-auth (from line 124)
  • ~/.codex (from line 125)

this is likely intentional to support legacy layouts where accounts lived directly in ~/.codex, but the deduplication at line 127 won't collapse them since they're different paths. just confirming this is the expected behavior for migration scenarios.


59-73: test assertions already prepared for migration — no brittleness risk, but add regression test for the new signal file.

test/paths.test.ts:72-78 already mocks codex-accounts.json alongside the old name, and test/storage.test.ts:265-266 validates the migration path. the signal list addition is safe.

however, hasStorageSignals() has no windows-specific test coverage. if this function or its callers ever move to async paths or encounter antivirus file locking, existsSync will silently fail. consider adding a test case that verifies the function's behavior when a signal file exists but can't be accessed (simulating EBUSY on windows). see lib/storage.ts:718-734 for the retry pattern used elsewhere.

Comment thread lib/runtime-paths.ts
Comment thread lib/runtime-paths.ts
@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

Addressed the review batch from #pullrequestreview-3868186045 in commit 15abb9a.\n\nKey fixes included:\n- safer config installer merge + Windows-safe cache clear handling\n- case-insensitive TERM_PROGRAM handling in CLI\n- local JSON schema for config templates + lowercase template consistency\n- request-transformer env parsing cleanup\n- benchmark/model-matrix script hardening (Windows resolver filtering, timeout handling, executable import path fixes, scoped stop logic, test coverage)\n- host prompt stale-refresh determinism and concurrency regression coverage\n- runtime path fallback regression tests and package publishing metadata checks\n\nValidation run: npm run lint, npm run typecheck, npm test (all passing).\n\nPlease re-run/refresh CodeRabbit so resolved threads can auto-close against latest diff.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/copy-oauth-success.test.ts (1)

12-30: ⚠️ Potential issue | 🟡 Minor

add a windows path regression case for the copy flow.

line 12 currently validates one path shape. add a win32-style destination-path case to guard separator regressions in test/copy-oauth-success.test.ts:12.

As per coding guidelines, "test/**: 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."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/copy-oauth-success.test.ts` around lines 12 - 30, The test only checks a
POSIX-style destination path for copyOAuthSuccessHtml; add a second test case in
test/copy-oauth-success.test.ts that uses a Win32-style destination path (e.g.,
backslashes in the dest string or build the path using path.win32.join) to
ensure copyOAuthSuccessHtml handles Windows separators correctly; locate the
existing test block (the it("copies oauth-success.html to the requested
destination"...) that imports ../scripts/copy-oauth-success.js and calls
copyOAuthSuccessHtml({ src, dest })) and duplicate/assert the same behavior with
a Windows-style dest, creating temp dirs with mkdtemp and cleaning up the same
way to keep tests deterministic under vitest.
scripts/install-codex-auth.js (1)

71-83: ⚠️ Potential issue | 🟡 Minor

backup nonce improves collision resistance but lacks atomicity.

the nonce using process.pid and Math.random().toString(36) helps avoid backup filename collisions, which is good. however, there's still no file locking for the config write at lib/install-codex-auth.js:191 - concurrent installer runs could still race on the same config file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install-codex-auth.js` around lines 71 - 83, backupConfig currently
generates a nonce but does not ensure atomicity or prevent concurrent installers
from racing; update the flow around backupConfig and the config write (e.g., the
backupConfig function and the code that writes the config file) to perform a
safe atomic swap by writing the new content to a uniquely named temp file in the
same directory (use the same nonce/timestamp pattern), fsync the temp file, then
atomically rename it over the original (fs.rename) and remove the backup;
additionally implement a lightweight lock (e.g., create a lockfile with
exclusive create/open flags or use advisory file locking) around the backup +
write + rename sequence to prevent concurrent runs from colliding, and ensure
the lock is always released in finally blocks so failures don’t leave the
lockfile in place.
♻️ Duplicate comments (3)
scripts/bench-format/codex-host.mjs (2)

32-34: ⚠️ Potential issue | 🔴 Critical

fallback command also uses uppercase "Codex".

same case-sensitivity issue as above - the fallback at line 33 should be lowercase for posix compatibility.

proposed fix
   if (candidates.length === 0) {
-    return { command: "Codex", shell: false };
+    return { command: "codex", shell: false };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/bench-format/codex-host.mjs` around lines 32 - 34, The fallback
returned when candidates.length === 0 uses an uppercase command string; update
the returned object’s command property from "Codex" to lowercase "codex" in the
block that returns { command: "Codex", shell: false } so the fallback is
POSIX-compatible (look for the candidates.length === 0 check and the returned
command property).

19-21: ⚠️ Potential issue | 🔴 Critical

posix command is still uppercase "Codex" - will fail with ENOENT on linux/macos.

the @openai/codex npm package installs the binary as lowercase codex on posix systems. line 20 returns "Codex" which won't be found. same issue exists in scripts/test-model-matrix.js:27.

proposed fix
   if (process.platform !== "win32") {
-    return { command: "Codex", shell: false };
+    return { command: "codex", shell: false };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/bench-format/codex-host.mjs` around lines 19 - 21, The POSIX branch
returns the command string "Codex" which is capitalized and will cause ENOENT on
Linux/macOS; change the returned command value to lowercase "codex" in the
conditional that checks process.platform !== "win32" (the object returned with
command: "Codex" in codex-host.mjs) and make the same lowercase fix for the
identical occurrence in scripts/test-model-matrix.js (the uppercase "Codex"
string there).
scripts/install-codex-auth.js (1)

42-44: ⚠️ Potential issue | 🟠 Major

windows path compatibility still not addressed.

~/.config/Codex and ~/.cache/Codex are unix-style paths. on windows, users expect %APPDATA%/Codex and %LOCALAPPDATA%/Codex. the current implementation will create directories under C:\Users\<user>\.config\Codex on windows, which is non-standard.

proposed fix
+function getConfigDir() {
+  if (process.platform === "win32" && process.env.APPDATA) {
+    return join(process.env.APPDATA, "Codex");
+  }
+  return join(homedir(), ".config", "Codex");
+}
+
+function getCacheDir() {
+  if (process.platform === "win32" && process.env.LOCALAPPDATA) {
+    return join(process.env.LOCALAPPDATA, "Codex");
+  }
+  return join(homedir(), ".cache", "Codex");
+}
+
-const configDir = join(homedir(), ".config", "Codex");
-const configPath = join(configDir, "Codex.json");
-const cacheDir = join(homedir(), ".cache", "Codex");
+const configDir = getConfigDir();
+const configPath = join(configDir, "Codex.json");
+const cacheDir = getCacheDir();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install-codex-auth.js` around lines 42 - 44, On Windows the current
configDir/configPath/cacheDir use Unix-style "~/.config" and "~/.cache"; update
the logic that defines configDir, configPath and cacheDir to pick
platform-appropriate locations: when process.platform === 'win32' use
process.env.APPDATA (for configDir) and process.env.LOCALAPPDATA (for cacheDir)
with a fallback to homedir() if those env vars are missing, otherwise keep using
join(homedir(), ".config", "Codex") and join(homedir(), ".cache", "Codex");
ensure configPath continues to be join(configDir, "Codex.json") so filenames
follow the chosen directory.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/install-codex-auth.test.ts`:
- Around line 6-19: Tests only assert string literals in
test/install-codex-auth.test.ts and don't exercise real installer behavior; add
focused integration/unit tests that call the actual functions from
scripts/install-codex-auth.js: invoke normalizePluginList() with edge cases
(empty arrays, non-string entries, duplicates) and assert normalized output; run
the installer in dry-run mode and assert no filesystem writes occur (mock fs or
use a temp directory and verify files unchanged); test the backup
creation/restore logic by triggering backup and restore paths and validating
contents; and add a concurrency regression that simulates two parallel runs of
the backup function (the function referenced in scripts/install-codex-auth.js at
the backup step) to ensure backup filenames are collision-resistant (use
process.pid + Math.random or atomic tmp file creation) and include a
Windows-specific filesystem test (path separators, case-insensitivity) to cover
platform behavior.

In `@test/runtime-paths.test.ts`:
- Around line 53-67: The test "deduplicates Windows-style fallback paths
case-insensitively" installs a platform spy (platformSpy) but restores it only
at the end, which can leak if the assertion throws; update the test to ensure
platformSpy.mockRestore() always runs by wrapping the test body in a try/finally
or moving the restore to an afterEach hook so platformSpy is restored even on
failure; locate the spy created with vi.spyOn(process, "platform", "get") and
ensure its mockRestore is invoked in a finally block or in the suite-level
afterEach cleanup.

---

Outside diff comments:
In `@scripts/install-codex-auth.js`:
- Around line 71-83: backupConfig currently generates a nonce but does not
ensure atomicity or prevent concurrent installers from racing; update the flow
around backupConfig and the config write (e.g., the backupConfig function and
the code that writes the config file) to perform a safe atomic swap by writing
the new content to a uniquely named temp file in the same directory (use the
same nonce/timestamp pattern), fsync the temp file, then atomically rename it
over the original (fs.rename) and remove the backup; additionally implement a
lightweight lock (e.g., create a lockfile with exclusive create/open flags or
use advisory file locking) around the backup + write + rename sequence to
prevent concurrent runs from colliding, and ensure the lock is always released
in finally blocks so failures don’t leave the lockfile in place.

In `@test/copy-oauth-success.test.ts`:
- Around line 12-30: The test only checks a POSIX-style destination path for
copyOAuthSuccessHtml; add a second test case in test/copy-oauth-success.test.ts
that uses a Win32-style destination path (e.g., backslashes in the dest string
or build the path using path.win32.join) to ensure copyOAuthSuccessHtml handles
Windows separators correctly; locate the existing test block (the it("copies
oauth-success.html to the requested destination"...) that imports
../scripts/copy-oauth-success.js and calls copyOAuthSuccessHtml({ src, dest }))
and duplicate/assert the same behavior with a Windows-style dest, creating temp
dirs with mkdtemp and cleaning up the same way to keep tests deterministic under
vitest.

---

Duplicate comments:
In `@scripts/bench-format/codex-host.mjs`:
- Around line 32-34: The fallback returned when candidates.length === 0 uses an
uppercase command string; update the returned object’s command property from
"Codex" to lowercase "codex" in the block that returns { command: "Codex",
shell: false } so the fallback is POSIX-compatible (look for the
candidates.length === 0 check and the returned command property).
- Around line 19-21: The POSIX branch returns the command string "Codex" which
is capitalized and will cause ENOENT on Linux/macOS; change the returned command
value to lowercase "codex" in the conditional that checks process.platform !==
"win32" (the object returned with command: "Codex" in codex-host.mjs) and make
the same lowercase fix for the identical occurrence in
scripts/test-model-matrix.js (the uppercase "Codex" string there).

In `@scripts/install-codex-auth.js`:
- Around line 42-44: On Windows the current configDir/configPath/cacheDir use
Unix-style "~/.config" and "~/.cache"; update the logic that defines configDir,
configPath and cacheDir to pick platform-appropriate locations: when
process.platform === 'win32' use process.env.APPDATA (for configDir) and
process.env.LOCALAPPDATA (for cacheDir) with a fallback to homedir() if those
env vars are missing, otherwise keep using join(homedir(), ".config", "Codex")
and join(homedir(), ".cache", "Codex"); ensure configPath continues to be
join(configDir, "Codex.json") so filenames follow the chosen directory.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4758003 and 15abb9a.

📒 Files selected for processing (24)
  • AGENTS.md
  • config/README.md
  • config/codex-legacy.json
  • config/codex-modern.json
  • config/minimal-codex.json
  • config/schema/config.schema.json
  • lib/cli.ts
  • lib/request/request-transformer.ts
  • package.json
  • scripts/bench-format/codex-host.mjs
  • scripts/bench-format/models.mjs
  • scripts/benchmark-edit-formats.mjs
  • scripts/install-codex-auth.js
  • scripts/test-model-matrix.js
  • test/README.md
  • test/config-files.test.ts
  • test/copy-oauth-success.test.ts
  • test/documentation.test.ts
  • test/host-codex-prompt.test.ts
  • test/install-codex-auth.test.ts
  • test/package-bin.test.ts
  • test/request-transformer.test.ts
  • test/runtime-paths.test.ts
  • test/test-model-matrix-script.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/install-codex-auth.test.ts
  • test/documentation.test.ts
  • test/host-codex-prompt.test.ts
  • test/request-transformer.test.ts
  • test/test-model-matrix-script.test.ts
  • test/copy-oauth-success.test.ts
  • test/config-files.test.ts
  • test/runtime-paths.test.ts
  • test/package-bin.test.ts
  • test/README.md
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/cli.ts
  • lib/request/request-transformer.ts
🧬 Code graph analysis (10)
test/install-codex-auth.test.ts (1)
scripts/install-codex-auth.js (1)
  • content (67-67)
test/host-codex-prompt.test.ts (1)
lib/prompts/host-codex-prompt.ts (1)
  • getHostCodexPrompt (255-286)
test/request-transformer.test.ts (2)
lib/request/helpers/input-utils.ts (2)
  • isHostSystemPrompt (60-92)
  • filterHostSystemPromptsWithCachedPrompt (94-115)
lib/prompts/codex-host-bridge.ts (1)
  • CODEX_HOST_BRIDGE (11-149)
scripts/bench-format/models.mjs (2)
scripts/bench-format/codex-host.mjs (2)
  • resolveCodexExecutable (12-52)
  • child (166-178)
scripts/test-model-matrix.js (2)
  • resolveCodexExecutable (20-64)
  • models (320-320)
test/config-files.test.ts (1)
scripts/install-codex-auth.js (1)
  • content (67-67)
scripts/install-codex-auth.js (1)
lib/logger.ts (1)
  • error (389-393)
scripts/bench-format/codex-host.mjs (1)
scripts/test-model-matrix.js (7)
  • resolveCodexExecutable (20-64)
  • envOverride (21-21)
  • command (23-23)
  • whereResult (31-34)
  • candidates (35-38)
  • exactExe (44-46)
  • exactCmd (51-53)
lib/request/request-transformer.ts (4)
lib/types.ts (1)
  • InputItem (81-87)
lib/prompts/host-codex-prompt.ts (1)
  • getHostCodexPrompt (255-286)
lib/request/helpers/input-utils.ts (1)
  • filterHostSystemPromptsWithCachedPrompt (94-115)
lib/prompts/codex-host-bridge.ts (1)
  • CODEX_HOST_BRIDGE (11-149)
scripts/test-model-matrix.js (1)
scripts/bench-format/codex-host.mjs (7)
  • resolveCodexExecutable (12-52)
  • envOverride (13-13)
  • command (15-15)
  • whereResult (23-26)
  • candidates (27-30)
  • exactExe (36-36)
  • exactCmd (41-41)
scripts/benchmark-edit-formats.mjs (3)
scripts/bench-format/codex-host.mjs (2)
  • runCodexJson (148-205)
  • resolveCodexExecutable (12-52)
scripts/bench-format/models.mjs (2)
  • executable (51-51)
  • listCodexModels (50-72)
scripts/test-model-matrix.js (1)
  • resolveCodexExecutable (20-64)
🔇 Additional comments (47)
lib/cli.ts (2)

351-352: no action on eof blank lines in this pass.

this is formatting-only at lib/cli.ts:351-352, previously called out, and not a release-risk item.


20-22: test coverage for isNonInteractiveMode() already exists and is comprehensive.

test/cli.test.ts contains detailed regression tests for all env-precedence cases:

  • FORCE_INTERACTIVE_MODE override: test/cli.test.ts:276-280
  • CODEX_TUI: test/cli.test.ts:282-288
  • CODEX_DESKTOP: test/cli.test.ts:290-296
  • TERM_PROGRAM case-insensitive matching: test/cli.test.ts:298-304
  • ELECTRON_RUN_AS_NODE: test/cli.test.ts:306-312
  • TTY fallback: test/cli.test.ts:314-335
  • integration test for non-interactive behavior: test/cli.test.ts:460-474

environment isolation is properly handled with vi.resetModules(), beforeEach/afterEach cleanup, and explicit env var deletion. no concurrent tests exist, so no race conditions. logging in lib/cli.ts:20-22 contains no token or email leaks.

Likely an incorrect or invalid review comment.

test/config-files.test.ts (1)

212-215: good cleanup on the codex ignore predicate.

this removes the prior duplicate check and keeps the assertion deterministic in test/config-files.test.ts:212.

test/copy-oauth-success.test.ts (1)

15-15: prefix rename is consistent with codex naming.

test/copy-oauth-success.test.ts:15 now matches the repository’s lowercase codex convention.

config/schema/config.schema.json (1)

1-21: new local schema foundation looks good.

centralizing validation in config/schema/config.schema.json:1 is the right direction for codex-era config templates.

config/minimal-codex.json (1)

2-2: local schema reference is correctly wired.

config/minimal-codex.json:2 now aligns with the in-repo schema path.

config/codex-modern.json (1)

2-2: schema migration is consistent here too.

config/codex-modern.json:2 correctly points to the local schema.

config/codex-legacy.json (1)

2-2: legacy template now references the shared schema correctly.

config/codex-legacy.json:2 matches the same local schema strategy as the other templates.

test/README.md (1)

36-36: documentation references are aligned with codex naming.

the updates in test/README.md:36 and test/README.md:204 are consistent with the migration.

Also applies to: 204-206

config/README.md (1)

5-11: config template docs are now consistently codex-era.

the heading/table changes in config/README.md:5 and config/README.md:9 match the renamed files.

AGENTS.md (1)

67-68: lgtm, account paths now include the multi-auth/ directory level.

the per-project and global account paths are correctly documented as ~/.codex/multi-auth/projects/<project-key>/openai-codex-accounts.json and ~/.codex/multi-auth/openai-codex-accounts.json, matching the implementation in lib/runtime-paths.ts:105-111 and lib/storage/paths.ts.

test/runtime-paths.test.ts (1)

1-22: good test coverage for runtime-paths selection logic.

tests cover primary vs fallback preference, legacy root fallback, and windows case-insensitive deduplication. uses vitest correctly with module isolation via vi.resetModules().

scripts/bench-format/codex-host.mjs (1)

148-205: runCodexJson function looks good.

naming is consistent with the codex migration. no functional changes to the spawn logic.

package.json (2)

66-73: vendor packaging looks correct.

files array includes vendor directories, bundleDependencies ensures @codex-ai/plugin is bundled in the tarball. this pattern works for distributing local packages with npm publish.


90-90: file: paths for local vendor packages are valid.

using file:vendor/codex-ai-sdk and file:vendor/codex-ai-plugin correctly references the local packages. npm will resolve these during install.

Also applies to: 106-106

test/package-bin.test.ts (1)

4-17: test coverage for vendor packaging looks good.

validates pkg.files contains vendor paths and pkg.bundleDependencies includes @codex-ai/plugin. also confirms old opencode install bin entry is removed.

scripts/bench-format/models.mjs (2)

2-2: import path is now correct.

references ./codex-host.mjs which exists in the same directory.


50-72: listCodexModels function looks good.

timeout handling via CODEX_MODELS_TIMEOUT_MS env var with ETIMEDOUT check is appropriate. error messages updated to codex terminology.

scripts/install-codex-auth.js (2)

36-40: template filenames are now lowercase - addresses past review.

codex-legacy.json and codex-modern.json match the actual files in config/, fixing the case-sensitivity issue on linux.


136-143: cache clearing error handling is improved.

wrapping rm calls in try/catch and logging a warning is better than crashing. the message correctly suggests restarting codex.

lib/request/request-transformer.ts (4)

3-4: import and export renames look good.

the opencode-to-host migration for imports (CODEX_HOST_BRIDGE, getHostCodexPrompt, filterHostSystemPromptsWithCachedPrompt) and re-exports (isHostSystemPrompt, filterHostSystemPromptsWithCachedPrompt) aligns with the broader codex-only naming scope. corresponding tests in test/request-transformer.test.ts verify these exports.

Also applies to: 7-7, 26-29


326-327: duplicate env parsing fixed.

this addresses the past review comment - the nullish coalescing with identical operands is now a single call to parseCollaborationMode(process.env.CODEX_COLLABORATION_MODE).


737-752: async prompt filtering with silent fallback is acceptable.

filterHostSystemPrompts catches errors from getHostCodexPrompt() and falls back to text-based detection only. this is safe since filterHostSystemPromptsWithCachedPrompt handles cachedPrompt: null. no sensitive data logged here.


772-772: bridge message constant usage verified.

CODEX_HOST_BRIDGE is correctly used in addCodexBridgeMessage. tests at test/request-transformer.test.ts:649 now assert against the imported constant.

test/request-transformer.test.ts (4)

7-14: imports updated correctly for host terminology.

renamed imports (isHostSystemPrompt, filterHostSystemPrompts, filterHostSystemPromptsWithCachedPrompt, CODEX_HOST_BRIDGE) align with the source module changes in lib/request/request-transformer.ts.


432-531: isHostSystemPrompt tests cover expected signatures.

test cases verify detection of codex prompt signatures including:

  • "You are a coding agent running in the Codex"
  • "You are Codex, an agent"

role-based filtering (developer/system vs user) is tested. the cachedPrompt exact match and prefix match logic is exercised.


649-649: hardcoded bridge text replaced with constant.

this addresses the past review comment - assertions now use CODEX_HOST_BRIDGE constant instead of the brittle 'Codex Host Bridge' string literal.

Also applies to: 776-776, 797-797, 861-861


533-636: filterHostSystemPrompts tests verify context preservation.

tests confirm:

  • codex.txt prompts are filtered out
  • AGENTS.md content is preserved
  • environment info concatenated with prompts is handled correctly
  • undefined input returns undefined

the test at line 635 uses await filterHostSystemPrompts(undefined) which exercises the async path.

test/host-codex-prompt.test.ts (5)

13-14: test suite renamed to host-codex-prompt terminology.

describe blocks and imports correctly reference getHostCodexPrompt from ../lib/prompts/host-codex-prompt.js.

Also applies to: 24-26


136-153: env var override renamed to CODEX_CODEX_PROMPT_URL.

test verifies the environment variable override is respected. the url is fetched first before default sources.


172-179: metadata file path updated to host-codex-prompt-meta.json.

assertion at line 173 correctly checks the new metadata filename. query params are still stripped from persisted sourceKey.


212-245: good concurrency regression test for stale refresh deduplication.

this test exercises concurrent getHostCodexPrompt() calls and verifies:

  • both calls return immediately with stale content
  • only one fetch is triggered (deduplication)
  • cache is eventually written with new content

this addresses the guideline to demand regression cases for concurrency bugs.


276-294: EBUSY retry test covers windows filesystem edge case.

test at line 276 simulates transient EBUSY errors on cache write and verifies retry succeeds. this exercises windows filesystem behavior per the coding guidelines.

test/documentation.test.ts (1)

94-100: variable rename is cosmetic; logic is correct.

hasLegacyHostWord replaces hasOpencode but the check is unchanged: content.toLowerCase().includes('opencode'). the lowercase comparison is correct here since both sides are lowercase.

note: the past review comment about case mismatch was for a different assertion (checking 'Codex' with capital C in lowercased content). that issue does not apply to this test.

scripts/benchmark-edit-formats.mjs (4)

13-16: import path corrected to codex-host.mjs.

this addresses the past review comment about the broken import. the functions resolveCodexExecutable, runCodexJson are correctly imported from ./bench-format/codex-host.mjs. model helpers from ./bench-format/models.mjs are also updated.


156-231: runCodexWithResilience correctly uses codex functions.

the resilience wrapper properly:

  • calls aliasCandidatesForCodexModel for model alias resolution
  • calls runCodexJson for execution
  • handles transient retries and model-not-found fallbacks

473-473: workspace config renamed to Codex.json.

the benchmark workspace now writes Codex.json instead of opencode.json.


594-595: summary fields renamed to CodexCommand/CodexUsesShell.

consistent with the broader codex naming migration.

test/test-model-matrix-script.test.ts (4)

1-14: test setup correctly mocks spawnSync.

vitest mock for node:child_process.spawnSync allows deterministic testing of executable resolution and process cleanup.


16-25: CODEX_BIN override test verifies .cmd shell detection.

when CODEX_BIN=C:\Tools\Codex.cmd, the resolver returns shell: true. this covers the windows .cmd shim edge case.


27-41: windows where output filtering test is good.

the test mocks noisy where output with INFO: lines and verifies only valid paths matching /^[A-Za-z]:\\.+\.(exe|cmd)$/i are considered candidates. this covers windows filesystem edge cases per the coding guidelines.


52-69: stopCodexServers serialization test covers concurrency.

Promise.all([stopCodexServers(), stopCodexServers()]) verifies the queue serializes calls. the test also verifies USERNAME eq neil filter is passed to taskkill, scoping cleanup to the current user.

scripts/test-model-matrix.js (5)

10-14: config paths renamed to codex equivalents.

local config paths updated to .codex.json, Codex.json. scenario templates renamed to codex-legacy.json, codex-modern.json.


20-64: resolveCodexExecutable mirrors codex-host.mjs implementation.

the function correctly:

  • checks CODEX_BIN env override first
  • returns { command: "Codex", shell: false } on non-windows
  • filters where output to valid paths on windows
  • prefers npm\Codex.exe over .cmd shims

this is consistent with scripts/bench-format/codex-host.mjs:11-51.


114-139: stopCodexServers adds queue serialization and user scoping.

this addresses the past review concerns:

  • queue serialization at line 135-138 prevents overlapping cleanup
  • USERNAME filtering on windows (line 118-122)
  • UID filtering on unix when process.getuid is available (line 126-128)

however, same-user concurrent runs on the same host can still interfere. pid tracking would be more robust but this is a reasonable mitigation.


219-242: timeout added to spawnSync calls.

this addresses the past review - timeout is set from CODEX_MATRIX_TIMEOUT_MS (default 120000ms) with killSignal: "SIGKILL". ETIMEDOUT errors are caught and return exit code 124 with a descriptive message.


448-459: isDirectRun guard allows safe import for testing.

the main() call is now guarded by isDirectRun check, allowing test/test-model-matrix-script.test.ts to import the module without triggering execution.

Comment thread test/install-codex-auth.test.ts Outdated
Comment thread test/runtime-paths.test.ts
@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

Done. I fixed the latest review batch and pushed commit 5f60971.\n\nWhat was fixed in this push:\n- config fallback: added CODEX_HOME-aware legacy auth config path resolution + regression test\n- host prompt cache: added legacy cache filename migration path + regression test\n- refresh safety: guarded missing/non-function client.auth.set in refresh path + tests\n- bench resolvers: switched posix fallback command to lowercase codex + regression tests\n- installer tests: added dry-run behavior test against real script execution\n- path tests: tightened explicit mock coverage as suggested\n- style nits: cleaned EOF blank-line issues in flagged files\n- lint guard: added eslint ignore regression test for �endor/**\n\nValidation (full):\n- npm run lint ✅\n- npm run typecheck ✅\n- npm test ✅ (1989/1989 passing)\n\nPlease re-run CodeRabbit on latest head so stale threads can be auto-resolved.

@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: 12

♻️ Duplicate comments (1)
scripts/test-model-matrix.js (1)

114-139: ⚠️ Potential issue | 🟠 Major

queue serializes cleanup within process but doesn't scope to this run's PIDs.

the stopCodexServersQueue at scripts/test-model-matrix.js:114-138 prevents overlapping cleanup calls within a single node process, but stopCodexServersInternal still issues global kills by image name (Codex.exe) or pattern (-f "Codex"). concurrent matrix runs on shared CI hosts can still terminate each other.

the USERNAME/UID filtering (lines 118-127) helps but isn't sufficient when multiple runs execute under the same user. the past review requested tracking spawned PIDs and killing only those.

if implementing full PID tracking is out of scope, consider documenting this limitation or adding a --skip-cleanup flag for CI environments where isolation is managed externally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test-model-matrix.js` around lines 114 - 139, stopCodexServers
currently serializes cleanup via stopCodexServersQueue but still issues global
kills by image/pattern; change it to only kill tracked PIDs by adding a
module-level registry (e.g., spawnedCodexPids: number[]) and an exported
registerSpawnedCodex(pid) function that the server-launching code calls, then
update stopCodexServersInternal to iterate that registry and call
runQuiet("taskkill"/"kill", [pid]) per PID (and clear the registry) instead of
killing by name/pattern; if tracking spawned PIDs cannot be implemented now, add
an exported boolean option or CLI flag (e.g., skipCleanup) checked by
stopCodexServers to skip global kills and document the limitation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/config.ts`:
- Around line 1104-1110: Remove the extra trailing blank lines at the end of
lib/config.ts (lines around the file EOF); trim the file so it ends with either
zero or a single newline per project style (ensure there is no block of multiple
empty lines after the last token) and save the file to eliminate the six blank
lines currently present.

In `@lib/prompts/host-codex-prompt.ts`:
- Around line 340-342: The file has extraneous trailing blank lines at its end;
open lib/prompts/host-codex-prompt.ts and remove the extra empty lines so the
file ends immediately after the final statement/export (no blank lines after the
last character), then save/commit the change.
- Around line 166-175: The migration loop using LEGACY_CACHE_FILES and tryRead
writes CACHE_FILE and CACHE_META_FILE in parallel with writeFileWithRetry, which
can leave a partial state on failure; change the migration in the loop (the
block that calls mkdir and writeFileWithRetry for CACHE_FILE and
CACHE_META_FILE) to perform an atomic replace: write both content and meta to
temp files in CACHE_DIR (e.g., CACHE_FILE + .tmp and CACHE_META_FILE + .tmp),
fsync/ensure writes complete, then rename the temps to CACHE_FILE and
CACHE_META_FILE (or perform sequential write with rollback if a rename fails) so
either both appear or none do; keep using writeFileWithRetry and handle
EBUSY/429 retries, and add/update vitest cases to cover partial-failure
scenarios and EBUSY handling for this migration path.

In `@test/codex-host-resolver.test.ts`:
- Around line 16-29: Wrap each platform spy restoration in these tests ("uses
lowercase codex command on non-windows" and "uses lowercase codex fallback when
Windows where has no path candidates") in a try/finally: create platformSpy via
vi.spyOn(process, "platform", "get") as you already do, then perform the dynamic
import and expect assertions inside a try block, and call
platformSpy.mockRestore() inside the finally block so the spy is always restored
even if assertions fail; apply the same pattern for the spawnSync mock in the
Windows test if it's mocked globally.

In `@test/eslint-config.test.ts`:
- Around line 5-7: The test is cwd-dependent and brittle about quote style: in
test/eslint-config.test.ts replace the readFileSync("eslint.config.js", "utf8")
call with a deterministic path resolution (e.g. use path.resolve(__dirname,
'..', 'eslint.config.js') or path.join(__dirname, ...) and require/import path
at top) and change the assertion expect(content).toContain('"vendor/**"') to a
format-tolerant check such as expect(content).toMatch(/['"`]?vendor\/\*\*/), so
the test no longer relies on process.cwd() or a specific quote style; update
imports to include path if needed and keep the assertion using readFileSync and
expect(...).toMatch to reference the same symbols (readFileSync, expect,
toMatch) in the file.

In `@test/fetch-helpers.test.ts`:
- Around line 73-85: Update the two tests for refreshAndUpdateToken to assert
that no refresh work is started by spying/guarding the client's auth setter: for
the "missing setter" case, provide a client whose auth property has a setter
accessor or a vitest.fn() that throws if ever invoked and verify
refreshAndUpdateToken rejects and that the spy was not called; for the "auth.set
not a function" case, replace client.auth.set with a vitest.fn() that throws if
called (instead of the string) and assert refreshAndUpdateToken rejects and that
the spy was not called; reference refreshAndUpdateToken and the client.auth.set
spy to locate the changes.

In `@test/host-codex-prompt.test.ts`:
- Around line 59-93: Add a regression test for a partial migration failure of
getHostCodexPrompt: seed the legacy cache files as in the existing test, mock
readFile to return legacy meta + txt, then mock writeFile to succeed on the
first call and throw an EBUSY error on the second call; call getHostCodexPrompt
and assert it still returns the legacy cached content, that writeFile was called
for the host-codex prompt and meta (one succeeded, one errored), and that the
code did not crash and did not call mockFetch—this ensures getHostCodexPrompt
handles non-atomic migration failures gracefully.
- Around line 387-389: The file test/host-codex-prompt.test.ts ends with
unnecessary trailing blank lines; open that file and remove the extra empty
lines at the end so the file ends immediately after the last token (e.g., after
the final test or describe block) to eliminate the trailing blank-space.

In `@test/install-codex-auth.test.ts`:
- Around line 28-32: The current test "uses collision-resistant backup suffix"
only checks for the presence of process.pid and Math.random in the script text;
replace it with a behavioral concurrency test that actually spawns the installer
twice in parallel (using the same temporary HOME) and asserts the produced
backup filenames do not collide. Use the same pattern as the dry-run test around
lines 34-49: create a shared temp HOME, run the installer script (scriptPath)
concurrently twice (e.g., Promise.all on two child process runs), capture the
backup paths from stdout or by inspecting the target dir, and assert the two
backup names are distinct to verify collision resistance of the backup suffix
generation that relies on process.pid and Math.random().

In `@test/paths.test.ts`:
- Around line 68-85: Add a windows-specific regression test for getConfigDir
that mirrors the existing fallback-with-accounts test but uses a Windows-style
homedir (e.g., "C:\\Users\\test") and win32 path semantics; in the new test set
homedir() to the Windows value, use path.join with path.win32 where appropriate,
mock mockedExistsSync to return true only for primary/settings.json and for
fallback/openai-codex-accounts.json (and false for primary account files and
other signals) and assert getConfigDir() returns the fallback path; follow the
pattern used in the getProjectConfigDir windows test (test/paths.test.ts:96-100)
and reuse the same mockedExistsSync and homedir stubbing approach so
case-insensitive/dedup behavior is exercised.

In `@test/plugin-config.test.ts`:
- Around line 203-226: The test suffers from module-mocking/isolation: after
vi.resetModules() the hoisted mocks for node:fs (mockExistsSync,
mockReadFileSync) and ../lib/logger.js (logger.logWarn) may not be the same
instances used by the dynamically imported module via
import('../lib/config.js'), so assertions can be against stale mock references;
fix by re-establishing the mocks after resetModules() (or move the mock
implementations into the test body) so mockExistsSync and mockReadFileSync used
by loadPluginConfig() are the same objects you assert on, and ensure you import
the module only after setting those mocks; also add an additional test case for
windows-style CODEX_HOME (e.g., set process.env.CODEX_HOME to a backslash path)
to verify path.join behavior and that expectedPath is built and read correctly,
asserting logger.logWarn is called with the windows-style path.

In `@test/test-model-matrix-script.test.ts`:
- Around line 27-50: The tests create a platformSpy (vi.spyOn(process,
"platform", "get")) and call platformSpy.mockRestore() after assertions, which
can leak the spy if an assertion throws; wrap each test's use of platformSpy in
a try/finally so mockRestore() always runs. Specifically, in the tests that call
resolveCodexExecutable() (refer to the test names "filters non-path where output
on Windows" and "returns fallback command when where has no executable
candidates"), wrap the import, spawnSync.mockReturnValue setup, and expect(...)
assertions in a try block and call platformSpy.mockRestore() in the finally
block to guarantee cleanup. Ensure the same pattern is applied wherever
platformSpy is used in this file.

---

Duplicate comments:
In `@scripts/test-model-matrix.js`:
- Around line 114-139: stopCodexServers currently serializes cleanup via
stopCodexServersQueue but still issues global kills by image/pattern; change it
to only kill tracked PIDs by adding a module-level registry (e.g.,
spawnedCodexPids: number[]) and an exported registerSpawnedCodex(pid) function
that the server-launching code calls, then update stopCodexServersInternal to
iterate that registry and call runQuiet("taskkill"/"kill", [pid]) per PID (and
clear the registry) instead of killing by name/pattern; if tracking spawned PIDs
cannot be implemented now, add an exported boolean option or CLI flag (e.g.,
skipCleanup) checked by stopCodexServers to skip global kills and document the
limitation.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 15abb9a and 5f60971.

📒 Files selected for processing (17)
  • .github/workflows/ci.yml
  • config/codex-legacy.json
  • lib/cli.ts
  • lib/config.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/request/fetch-helpers.ts
  • scripts/bench-format/codex-host.mjs
  • scripts/test-model-matrix.js
  • test/codex-host-resolver.test.ts
  • test/eslint-config.test.ts
  • test/fetch-helpers.test.ts
  • test/host-codex-prompt.test.ts
  • test/install-codex-auth.test.ts
  • test/paths.test.ts
  • test/plugin-config.test.ts
  • test/runtime-paths.test.ts
  • test/test-model-matrix-script.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/fetch-helpers.test.ts
  • test/install-codex-auth.test.ts
  • test/codex-host-resolver.test.ts
  • test/paths.test.ts
  • test/test-model-matrix-script.test.ts
  • test/eslint-config.test.ts
  • test/host-codex-prompt.test.ts
  • test/plugin-config.test.ts
  • test/runtime-paths.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/cli.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/config.ts
  • lib/request/fetch-helpers.ts
🧬 Code graph analysis (8)
test/fetch-helpers.test.ts (1)
lib/request/fetch-helpers.ts (1)
  • refreshAndUpdateToken (328-363)
test/install-codex-auth.test.ts (1)
scripts/install-codex-auth.js (2)
  • content (67-67)
  • configPath (43-43)
test/paths.test.ts (1)
lib/storage/paths.ts (1)
  • getConfigDir (23-25)
scripts/bench-format/codex-host.mjs (1)
scripts/test-model-matrix.js (7)
  • resolveCodexExecutable (20-64)
  • envOverride (21-21)
  • command (23-23)
  • whereResult (31-34)
  • candidates (35-38)
  • exactExe (44-46)
  • exactCmd (51-53)
test/host-codex-prompt.test.ts (1)
lib/prompts/host-codex-prompt.ts (1)
  • getHostCodexPrompt (285-316)
lib/prompts/host-codex-prompt.ts (1)
lib/runtime-paths.ts (1)
  • getCodexCacheDir (162-164)
lib/config.ts (1)
lib/runtime-paths.ts (2)
  • getCodexHomeDir (13-16)
  • getLegacyCodexDir (191-193)
scripts/test-model-matrix.js (1)
scripts/bench-format/codex-host.mjs (7)
  • resolveCodexExecutable (12-52)
  • envOverride (13-13)
  • command (15-15)
  • whereResult (23-26)
  • candidates (27-30)
  • exactExe (36-36)
  • exactCmd (41-41)
🔇 Additional comments (17)
config/codex-legacy.json (2)

571-571: trailing blank lines issue from prior review appears resolved.

file now ends cleanly at line 571. no action needed.


2-2: schema path updated to local reference — correct and verified.

removes the opencode.ai url and points to the new local schema at ./schema/config.schema.json. aligns with the pr objective to eliminate all opencode references. schema file exists at config/schema/config.schema.json and is valid json. relative path resolution is correct from config/codex-legacy.json.

test/eslint-config.test.ts (1)

1-2: good baseline test setup.

this uses vitest correctly and keeps a real assertion path. no concurrency/shared-state risk is introduced here. test/eslint-config.test.ts:1, test/eslint-config.test.ts:2.

lib/cli.ts (1)

20-22: lgtm — case-insensitive TERM_PROGRAM check is now robust.

the .trim().toLowerCase() on line 22 fixes the prior case-sensitivity mismatch flagged in the past review. test coverage for CODEX_TUI, CODEX_DESKTOP, and TERM_PROGRAM exists in test/cli.test.ts:282-303 per the ai summary.

test/runtime-paths.test.ts (2)

53-70: lgtm — platformSpy now properly restored in finally block.

the try/finally at lines 55-69 ensures platformSpy.mockRestore() executes even if assertions throw, addressing the past review concern. test isolation via vi.resetModules() and dynamic imports is correct.


23-51: good coverage for fallback priority and legacy root scenarios.

these tests at test/runtime-paths.test.ts:23-51 verify that account storage signals take precedence over non-account signals, which aligns with the runtime-paths prioritization logic. mocks are deterministic and properly isolated.

test/install-codex-auth.test.ts (1)

34-49: good dry-run behavioral test.

the dry-run test at test/install-codex-auth.test.ts:34-49 properly isolates via temp HOME, checks exit status and output, and verifies no config file is created. this is the right pattern for behavioral testing.

scripts/bench-format/codex-host.mjs (2)

19-21: lgtm — lowercase "codex" command on posix now correct.

lines 20 and 33 now return { command: "codex", shell: false } with lowercase command, fixing the past review concern about posix case-sensitivity. test coverage at test/codex-host-resolver.test.ts:16-21 confirms this.


12-51: windows path resolution logic looks correct.

the where Codex lookup at line 23, path filtering at line 30, and preference ordering (exact exe → exact cmd → any cmd → first candidate) at lines 36-51 is sound. the regex patterns correctly match npm\\Codex.exe and npm\\Codex.cmd case-insensitively.

test/test-model-matrix-script.test.ts (1)

52-69: good concurrency regression test for stopCodexServers.

test/test-model-matrix-script.test.ts:52-69 exercises parallel stopCodexServers() calls and verifies serialization + windows taskkill scoping to current USERNAME. this is exactly the kind of concurrency regression test the coding guidelines require.

lib/config.ts (2)

15-27: lgtm — LEGACY_CODEX_HOME_AUTH_CONFIG_PATH added per past review.

lib/config.ts:16-19 introduces LEGACY_CODEX_HOME_AUTH_CONFIG_PATH derived from getCodexHomeDir(), addressing the past review concern that auth fallback skipped CODEX_HOME overrides. the fallback chain at lines 75-105 now checks in correct order: LEGACY_CODEX_HOME_CONFIG_PATHLEGACY_CODEX_CONFIG_PATHLEGACY_CODEX_HOME_AUTH_CONFIG_PATHLEGACY_CODEX_AUTH_CONFIG_PATH.


91-97: test coverage for the LEGACY_CODEX_HOME_AUTH_CONFIG_PATH branch exists in test/plugin-config.test.ts:204. the test "should detect CODEX_HOME legacy auth config path before global legacy path" properly sets CODEX_HOME to a temp dir and mocks existsSync to validate the branch behavior. no additional test coverage is needed.

test/host-codex-prompt.test.ts (1)

248-281: good concurrency regression coverage.

this test properly validates that concurrent stale-refresh calls deduplicate to a single fetch. the loose assertion at line 271 correctly accounts for timing races between the two callers.

.github/workflows/ci.yml (1)

86-86: test file reference updated correctly.

the codex compatibility smoke test now references test/host-codex-prompt.test.ts which aligns with the renamed test file.

scripts/test-model-matrix.js (2)

219-242: timeout handling implemented correctly.

the spawnSync call now includes timeout and killSignal: "SIGKILL" (lines 224-225), and ETIMEDOUT is caught with exit code 124 (lines 234-242). this addresses the prior review concern about stuck cli calls blocking ci indefinitely.


448-459: good testability pattern.

the isDirectRun guard allows importing resolveCodexExecutable and stopCodexServers for unit tests without triggering main(). this enables the test coverage referenced in test/test-model-matrix-script.test.ts.

lib/request/fetch-helpers.ts (1)

332-345: auth-setter runtime guard is a solid fix.

lib/request/fetch-helpers.ts:332 (Line 332) now validates client.auth.set before refresh-path use, and lib/request/fetch-helpers.ts:344 (Line 344) uses the guarded setter. this removes the unsafe cast-only path in auth rotation. coverage is present in test/fetch-helpers.test.ts:73-85. no windows filesystem edge is introduced here.

Comment thread lib/config.ts Outdated
Comment thread lib/prompts/host-codex-prompt.ts
Comment thread lib/prompts/host-codex-prompt.ts Outdated
Comment thread test/codex-host-resolver.test.ts
Comment thread test/eslint-config.test.ts Outdated
Comment thread test/host-codex-prompt.test.ts Outdated
Comment thread test/install-codex-auth.test.ts Outdated
Comment thread test/paths.test.ts
Comment thread test/plugin-config.test.ts
Comment thread test/test-model-matrix-script.test.ts
@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest open review items in commit 8f17ee5.

Fixed items:

  • lib/prompts/host-codex-prompt.ts: switched legacy cache migration/save to atomic temp-file writes + rename retry, with safe fallback to legacy in-memory cache if migration write fails.
  • scripts/test-model-matrix.js: replaced global image-name kill cleanup with tracked PID cleanup only (
    egisterSpawnedCodex, queued stop, test reset helper).
  • est/codex-host-resolver.test.ts: wrapped platform spies in ry/finally restore.
  • est/test-model-matrix-script.test.ts: wrapped platform spies in ry/finally; updated cleanup assertions for PID-based taskkill.
  • est/eslint-config.test.ts: made config path deterministic and quote-format tolerant regex assertion.
  • est/fetch-helpers.test.ts: strengthened guard tests to assert no refresh path starts when auth setter is invalid.
  • est/host-codex-prompt.test.ts: added partial migration failure regression and updated stale-refresh assertion stability under atomic writes.
  • est/install-codex-auth.test.ts: replaced string-inspection backup test with behavioral concurrent installer backup-collision test.
  • est/paths.test.ts: added Windows-style fallback-with-accounts regression.
  • est/plugin-config.test.ts: hardened CODEX_HOME legacy auth path test isolation via �i.doMock after �i.resetModules, and added Windows-style CODEX_HOME coverage.
  • lib/config.ts: removed trailing blank lines at EOF.

Validation:

pm run lint ✅

pm run typecheck ✅

pm test ✅ (84 files, 1991 tests)

If you re-run CodeRabbit on PR #6 now, it should pick up these fixes.

@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: 4

♻️ Duplicate comments (1)
lib/config.ts (1)

1104-1105: 🧹 Nitpick | 🔵 Trivial

trailing blank lines at eof still present.

lib/config.ts:1104-1105 has extra blank lines at end of file. this was flagged in a past review but remains unaddressed.

proposed fix
 export function getPreemptiveQuotaMaxDeferralMs(pluginConfig: PluginConfig): number {
 	return resolveNumberSetting(
 		"CODEX_AUTH_PREEMPTIVE_QUOTA_MAX_DEFERRAL_MS",
 		pluginConfig.preemptiveQuotaMaxDeferralMs,
 		2 * 60 * 60_000,
 		{ min: 1_000 },
 	);
 }
-
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/config.ts` around lines 1104 - 1105, The file lib/config.ts contains
unwanted trailing blank lines at the end of file; open lib/config.ts, remove the
extra blank lines after the last token so the file ends with a single newline
(no multiple empty lines), and save so the EOF has exactly one newline
character; this is a whitespace-only change — no code logic edits required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/prompts/host-codex-prompt.ts`:
- Around line 150-180: The partial-rename recovery in writeCacheFilesAtomically
can itself fail when rewriting CACHE_FILE after content rename succeeds but meta
rename fails; wrap the call to writeFileWithRetry(CACHE_FILE, content) in a
try/catch inside writeCacheFilesAtomically and ensure any error from that
rewrite is logged (or removed) but does not replace the original rename error
being thrown; keep the original thrown error from the rename block while adding
a safe best-effort catch around writeFileWithRetry(CACHE_FILE, content) (and
optionally call removeFileQuietly or processLogger.error) so we don’t mask the
root failure and avoid leaving an unhandled exception from the recovery path.

In `@scripts/test-model-matrix.js`:
- Around line 224-230: The spawnSync timeout parsing uses
Number.parseInt(process.env.CODEX_MATRIX_TIMEOUT_MS ?? "120000", 10) which can
produce NaN for invalid strings and cause ERR_OUT_OF_RANGE; change this to parse
into a variable (e.g., const parsedTimeout =
Number.parseInt(process.env.CODEX_MATRIX_TIMEOUT_MS ?? "120000", 10)), validate
with Number.isFinite(parsedTimeout) and fall back to 120000 when invalid (const
timeout = Number.isFinite(parsedTimeout) ? parsedTimeout : 120000), pass timeout
into spawnSync's timeout option and use that validated timeout value in any
error/log messages instead of the raw env var; also add a regression test in
test/test-model-matrix-script.test.ts that sets CODEX_MATRIX_TIMEOUT_MS to an
invalid string like "abc" to ensure the script uses the default 120000 and does
not throw.

In `@test/install-codex-auth.test.ts`:
- Around line 76-77: The test is asserting the wrong config filename
(.config/Codex/config.json) which mismatches the test setup that writes/targets
"Codex.json"; update the assertion to check the actual filename used by the
test—replace the hardcoded "config.json" with the same filename/constant used in
the setup (e.g., "Codex.json" or the variable defined at the top of
test/install-codex-auth.test.ts) so configPath = path.join(home, ".config",
"Codex", "<correct-filename>") and expect(existsSync(configPath)).toBe(false).

In `@test/paths.test.ts`:
- Around line 87-116: The test fails on non-Windows CI because mockedExistsSync
compares path.win32.join strings but getCodexMultiAuthDir()/getConfigDir() uses
path.join (which produces mixed separators on POSIX), so update the test to
normalize both sides before comparing: either mock path.join to use
path.win32.join for this test scope, or change the mockedExistsSync comparison
to normalize the candidate and expected paths (e.g., convert candidate and the
expected path to path.win32.normalize or replace separators) so comparisons
match; reference mockedExistsSync, getCodexMultiAuthDir/getConfigDir, and
path.join/path.win32.join when making the change.

---

Duplicate comments:
In `@lib/config.ts`:
- Around line 1104-1105: The file lib/config.ts contains unwanted trailing blank
lines at the end of file; open lib/config.ts, remove the extra blank lines after
the last token so the file ends with a single newline (no multiple empty lines),
and save so the EOF has exactly one newline character; this is a whitespace-only
change — no code logic edits required.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5f60971 and 8f17ee5.

📒 Files selected for processing (11)
  • lib/config.ts
  • lib/prompts/host-codex-prompt.ts
  • scripts/test-model-matrix.js
  • test/codex-host-resolver.test.ts
  • test/eslint-config.test.ts
  • test/fetch-helpers.test.ts
  • test/host-codex-prompt.test.ts
  • test/install-codex-auth.test.ts
  • test/paths.test.ts
  • test/plugin-config.test.ts
  • test/test-model-matrix-script.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/paths.test.ts
  • test/fetch-helpers.test.ts
  • test/install-codex-auth.test.ts
  • test/test-model-matrix-script.test.ts
  • test/eslint-config.test.ts
  • test/host-codex-prompt.test.ts
  • test/plugin-config.test.ts
  • test/codex-host-resolver.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/prompts/host-codex-prompt.ts
  • lib/config.ts
🧬 Code graph analysis (7)
test/paths.test.ts (1)
lib/storage/paths.ts (1)
  • getConfigDir (23-25)
test/fetch-helpers.test.ts (2)
lib/types.ts (1)
  • Auth (143-143)
lib/request/fetch-helpers.ts (1)
  • refreshAndUpdateToken (328-363)
test/install-codex-auth.test.ts (1)
scripts/install-codex-auth.js (3)
  • content (67-67)
  • configDir (42-42)
  • configPath (43-43)
lib/prompts/host-codex-prompt.ts (3)
lib/runtime-paths.ts (1)
  • getCodexCacheDir (162-164)
lib/logger.ts (1)
  • logDebug (325-331)
lib/utils.ts (1)
  • sleep (54-56)
test/eslint-config.test.ts (1)
scripts/install-codex-auth.js (1)
  • content (67-67)
lib/config.ts (1)
lib/runtime-paths.ts (2)
  • getCodexHomeDir (13-16)
  • getLegacyCodexDir (191-193)
scripts/test-model-matrix.js (1)
scripts/bench-format/codex-host.mjs (7)
  • resolveCodexExecutable (12-52)
  • envOverride (13-13)
  • command (15-15)
  • whereResult (23-26)
  • candidates (27-30)
  • exactExe (36-36)
  • exactCmd (41-41)
🔇 Additional comments (18)
test/eslint-config.test.ts (1)

6-7: good fix for determinism and windows path safety.

test/eslint-config.test.ts:6 is now cwd-independent, which removes the windows/ci edge case. test/eslint-config.test.ts:7 is formatting-tolerant and keeps a real assertion. no concurrency risk is introduced in this test scope.

as per coding guidelines, "test/**: 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."

test/install-codex-auth.test.ts (1)

30-61: good behavioral concurrency coverage for backup collision risk.

test/install-codex-auth.test.ts:30-61 runs two installer processes concurrently and verifies unique Codex.json.bak-* artifacts. this is the right regression shape for race/collision behavior.

as per coding guidelines, "test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."

test/fetch-helpers.test.ts (2)

73-89: lgtm, guard tests now verify no refresh work is started.

the updated tests at test/fetch-helpers.test.ts:76-79 and test/fetch-helpers.test.ts:85-88 spy on refreshQueueModule.queuedRefresh and assert it is not called. this correctly addresses the past review concern since lib/request/fetch-helpers.ts:331-333 validates the auth setter before invoking queuedRefresh.


91-124: lgtm, refresh tests correctly stub the queue module.

the failure and success tests at test/fetch-helpers.test.ts:91-124 properly mock refreshQueueModule.queuedRefresh which aligns with the implementation at lib/request/fetch-helpers.ts:337.

test/codex-host-resolver.test.ts (1)

16-35: lgtm, platformSpy restoration now uses try/finally.

the tests at test/codex-host-resolver.test.ts:18-23 and test/codex-host-resolver.test.ts:29-34 wrap assertions in try/finally blocks ensuring platformSpy.mockRestore() always runs even on assertion failure. this addresses the past review concern and prevents spy leakage.

test/paths.test.ts (1)

68-85: lgtm, non-windows fallback test correctly exercises account-storage priority.

the test at test/paths.test.ts:68-85 verifies that getConfigDir() prefers the fallback directory when primary has non-account signals (settings.json) but no account files. this matches the expected behavior in lib/runtime-paths.ts.

test/test-model-matrix-script.test.ts (2)

16-56: lgtm, executable resolution tests cover windows edge cases with proper cleanup.

tests at test/test-model-matrix-script.test.ts:16-56 cover:

  • CODEX_BIN override detection with .cmd shell mode
  • filtering INFO: noise from where output on windows
  • fallback to codex when no candidates found

all platformSpy usages wrapped in try/finally ensuring cleanup on assertion failure.


58-87: serialization is properly implemented—test logic is sound.

the implementation uses a promise queue at scripts/test-model-matrix.js:114 (stopCodexServersQueue) that chains concurrent stopCodexServers() calls via .then(). when the test calls it twice concurrently at test/test-model-matrix-script.test.ts:69, the first call snapshots and clears the tracked pids set then kills both (2 spawnsync calls), and the second call sees an empty set (0 spawnsync calls). total of 2 calls correctly matches the assertion at line 71. the windows taskkill regression case at line 58-87 properly tests pid serialization and cleanup behavior.

lib/config.ts (1)

15-27: lgtm, legacy auth fallback now respects CODEX_HOME.

lib/config.ts:16-19 adds LEGACY_CODEX_HOME_AUTH_CONFIG_PATH and lib/config.ts:91-97 checks it before the global legacy path. this correctly addresses the past review concern about auth migration missing ${CODEX_HOME}/openai-codex-auth-config.json.

test/plugin-config.test.ts (1)

203-264: lgtm, test correctly isolates modules and covers windows paths.

test/plugin-config.test.ts:203-264 uses vi.resetModules() + vi.doMock() pattern for proper module isolation, addressing the past review concern. the test at lines 253-263 exercises windows-style CODEX_HOME paths and verifies the backslash path format at line 256.

lib/prompts/host-codex-prompt.ts (2)

201-237: lgtm, legacy cache migration now uses atomic writes with graceful degradation.

lib/prompts/host-codex-prompt.ts:223-234 attempts migration via writeCacheFilesAtomically, catches failures, logs them, and returns the legacy cache anyway. this addresses the past review concern about partial migration states.

test/host-codex-prompt.test.ts:101-136 covers the partial write failure scenario ensuring the code doesn't crash and still returns legacy content.


340-394: lgtm, api renamed to host-codex terminology.

exports at lib/prompts/host-codex-prompt.ts:340 and lib/prompts/host-codex-prompt.ts:390 renamed to getHostCodexPrompt and prewarmHostCodexPrompt respectively, aligning with the pr's codex-only naming scope.

test/host-codex-prompt.test.ts (3)

101-136: lgtm, partial migration failure test exercises ebusy handling.

test/host-codex-prompt.test.ts:101-136 covers the scenario where legacy migration write partially fails:

  • line 121-127: writeFile throws EBUSY for meta temp file
  • line 129: getHostCodexPrompt() still returns legacy content
  • line 133: rename not called (failed before rename phase)
  • line 134: rm called (temp cleanup)
  • line 135: no fetch attempted

this addresses the past review concern about missing partial failure regression test.


292-325: lgtm, concurrency deduplication test verifies single background refresh.

test/host-codex-prompt.test.ts:313 calls getHostCodexPrompt() twice concurrently via Promise.all. line 317 asserts mockFetch called exactly once, validating the refreshPromise guard in lib/prompts/host-codex-prompt.ts:319-331.

this covers the concurrency regression case as required by coding guidelines.


65-99: lgtm, tests cover legacy migration, env overrides, and ebusy retry.

  • test/host-codex-prompt.test.ts:65-99: verifies legacy opencode-codex-prompt.* files migrate to host-codex-prompt.*
  • test/host-codex-prompt.test.ts:215-232: verifies CODEX_CODEX_PROMPT_URL env override is used first
  • test/host-codex-prompt.test.ts:356-374: verifies ebusy retry on cache write succeeds after transient failure

Also applies to: 215-232, 356-374

scripts/test-model-matrix.js (3)

20-63: windows executable resolution looks solid.

line [31] and line [38] in lib/scripts/test-model-matrix.js:31 and lib/scripts/test-model-matrix.js:38 use strict candidate filtering with case-insensitive matching, and line [41] in lib/scripts/test-model-matrix.js:41 keeps a safe lowercase fallback command. this is a good windows edge-case hardening step.


114-144: pid-scoped cleanup removes the prior cross-run concurrency hazard.

line [127] and line [138] in lib/scripts/test-model-matrix.js:127 and lib/scripts/test-model-matrix.js:138 now stop only tracked pids and serialize shutdown calls. this avoids global process kills and is much safer for parallel matrix jobs.


453-464: direct-run guard is a good import-safety fix.

line [453] in lib/scripts/test-model-matrix.js:453 prevents accidental execution during test imports and keeps main() side effects scoped to direct script runs.

Comment thread lib/prompts/host-codex-prompt.ts
Comment thread scripts/test-model-matrix.js
Comment thread test/install-codex-auth.test.ts Outdated
@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest review (3868830187) in commit 65b3883.

Fixed:

  • lib/prompts/host-codex-prompt.ts
    • Preserved original rename error in writeCacheFilesAtomically by wrapping recovery rewrite in ry/catch and logging recovery failure without masking root error.
  • scripts/test-model-matrix.js
    • Added validated timeout resolver (
      esolveMatrixTimeoutMs) with safe default 120000 when env value is invalid/non-positive.
    • Uses validated timeout for spawnSync and timeout error message text.
  • est/test-model-matrix-script.test.ts
  • Added regression test for invalid CODEX_MATRIX_TIMEOUT_MS (�bc) -> falls back to 120000.
  • est/install-codex-auth.test.ts
  • Fixed dry-run assertion path to Codex.json (was mismatched config.json).
  • est/paths.test.ts
  • Hardened Windows fallback regression by normalizing candidate/expected paths to win32 semantics before comparison.
  • lib/config.ts
    • Removed remaining EOF trailing blank lines.

Validation:

pm run lint ✅

pm run typecheck ✅

pm test ✅ (84 files, 1992 tests)

@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 3

♻️ Duplicate comments (1)
test/paths.test.ts (1)

87-118: ⚠️ Potential issue | 🔴 Critical

windows path assertion will still fail on non-windows ci runners.

the normalizePath helper fixes the mock comparison (lines 99-106), but the assertion at line 111 compares result directly against fallback. on linux, getConfigDir() calls path.join() internally (lib/runtime-paths.ts:122), which produces mixed separators like C:\Users\test\.codex/multi-auth. fallback is built with path.win32.join, giving C:\Users\test\.codex\multi-auth. these won't match.

normalize the assertion:

 try {
   const result = getConfigDir();
-  expect(result).toBe(fallback);
+  expect(normalizePath(result)).toBe(normalizePath(fallback));
 } finally {

alternatively, skip on non-windows if true win32 semantics cannot be mocked deterministically. per coding guidelines, windows filesystem behavior tests are required, but they must stay deterministic across all ci runners.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/paths.test.ts` around lines 87 - 118, The test compares getConfigDir()
result to a windows-built fallback string but on non-Windows runners
getConfigDir() may produce mixed separators; normalize both sides before
assertion. Update the test's final expectation to compare normalizePath(result)
(or path.win32.normalize(result.replace(/\//g,"\\"))) against
normalizePath(fallback) so both use the same win32-normalized form (referencing
getConfigDir, normalizePath, fallback, and result).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/config.ts`:
- Around line 15-27: The two LEGACY_CODEX_HOME_* constants end up equal to the
LEGACY_CODEX_* ones when CODEX_HOME is unset, causing duplicate existsSync
checks in resolvePluginConfigPath; compute codexHome = getCodexHomeDir() and
legacyDir = getLegacyCodexDir(), set isCustomHome = codexHome !== legacyDir, and
then only evaluate/check the LEGACY_CODEX_HOME_CONFIG_PATH and
LEGACY_CODEX_HOME_AUTH_CONFIG_PATH when isCustomHome is true (leave the existing
LEGACY_CODEX_* checks unchanged); update resolvePluginConfigPath to
short-circuit those redundant existsSync calls using this flag so duplicate
filesystem checks are avoided.
- Around line 75-81: Add a regression test in test/plugin-config.test.ts that
verifies the LEGACY_CODEX_HOME_CONFIG_PATH fallback: create a temp directory,
set process.env.CODEX_HOME to that dir, write a legacy file named
codex-multi-auth-config.json into it, call loadPluginConfig() and assert it
returns the legacy path (LEGACY_CODEX_HOME_CONFIG_PATH) and that
logConfigWarnOnce (or the captured logs) contains the migration warning message
referencing CONFIG_PATH; repeat the assertion for both posix and windows-style
paths (e.g., by forcing path normalization or simulating a Windows path) so both
branches are covered.

In `@test/test-model-matrix-script.test.ts`:
- Around line 64-93: Add a parallel test that mocks process.platform to "linux"
and verifies stopCodexServers uses "kill -9" for tracked PIDs: import the
module, call __resetTrackedCodexPidsForTests(), registerSpawnedCodex(3003) (or
another PID), call stopCodexServers(), and assert spawnSync was called with
"kill" and args ["-9","3003"] and an options object containing stdio: "ignore";
restore the platform spy in a finally block. Reference the functions
stopCodexServers, registerSpawnedCodex, and __resetTrackedCodexPidsForTests and
the mocked spawnSync used in the existing Windows test.

---

Duplicate comments:
In `@test/paths.test.ts`:
- Around line 87-118: The test compares getConfigDir() result to a windows-built
fallback string but on non-Windows runners getConfigDir() may produce mixed
separators; normalize both sides before assertion. Update the test's final
expectation to compare normalizePath(result) (or
path.win32.normalize(result.replace(/\//g,"\\"))) against
normalizePath(fallback) so both use the same win32-normalized form (referencing
getConfigDir, normalizePath, fallback, and result).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8f17ee5 and 65b3883.

📒 Files selected for processing (6)
  • lib/config.ts
  • lib/prompts/host-codex-prompt.ts
  • scripts/test-model-matrix.js
  • test/install-codex-auth.test.ts
  • test/paths.test.ts
  • test/test-model-matrix-script.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/install-codex-auth.test.ts
  • test/test-model-matrix-script.test.ts
  • test/paths.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/prompts/host-codex-prompt.ts
  • lib/config.ts
🧬 Code graph analysis (5)
test/install-codex-auth.test.ts (1)
scripts/install-codex-auth.js (3)
  • content (67-67)
  • configDir (42-42)
  • configPath (43-43)
lib/prompts/host-codex-prompt.ts (3)
lib/runtime-paths.ts (1)
  • getCodexCacheDir (162-164)
lib/logger.ts (1)
  • logDebug (325-331)
lib/utils.ts (1)
  • sleep (54-56)
lib/config.ts (1)
lib/runtime-paths.ts (2)
  • getCodexHomeDir (13-16)
  • getLegacyCodexDir (191-193)
scripts/test-model-matrix.js (1)
scripts/bench-format/codex-host.mjs (7)
  • resolveCodexExecutable (12-52)
  • envOverride (13-13)
  • command (15-15)
  • whereResult (23-26)
  • candidates (27-30)
  • exactExe (36-36)
  • exactCmd (41-41)
test/paths.test.ts (1)
lib/storage/paths.ts (1)
  • getConfigDir (23-25)
🔇 Additional comments (24)
test/paths.test.ts (1)

68-85: lgtm - regression test covers the fallback-with-accounts scenario.

this test properly validates that getConfigDir() prefers the fallback directory when primary only has non-account signals (settings.json) but no account files. the mock checks both openai-codex-accounts.json and codex-accounts.json in primary, which aligns with hasAccountsStorage() logic in lib/runtime-paths.ts.

test/install-codex-auth.test.ts (4)

1-20: setup and teardown look solid.

temp-root tracking and afterEach cleanup are deterministic and keep tests isolated in test/install-codex-auth.test.ts:12-19 (Line 12, Line 16).


22-28: template filename guard is clear and focused.

the lowercase/uppercase assertions in test/install-codex-auth.test.ts:22-28 (Line 24, Line 27) are explicit and easy to maintain.


30-61: good behavioral concurrency regression coverage.

running two installer processes in parallel and validating unique backup names in test/install-codex-auth.test.ts:41-60 (Line 41, Line 59) is the right shape for collision-risk detection.
as per coding guidelines, "test/**: 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."


63-78: dry-run filesystem assertion is correct.

the test verifies both dry-run signaling and no disk write at test/install-codex-auth.test.ts:74-77 (Line 75, Line 77), which protects expected installer behavior.

lib/config.ts (2)

6-6: lgtm on import rename.

import correctly switches from getLegacyOpenCodeDir to getLegacyCodexDir, aligning with the codex-only naming scope.


83-89: lgtm on legacy path renames.

LEGACY_OPENCODE_CONFIG_PATHLEGACY_CODEX_CONFIG_PATH and LEGACY_OPENCODE_AUTH_CONFIG_PATHLEGACY_CODEX_AUTH_CONFIG_PATH are consistent with the codex-only migration. log messages updated correctly.

Also applies to: 99-105

lib/prompts/host-codex-prompt.ts (7)

14-42: constants and legacy cache paths look solid.

the LEGACY_CACHE_FILES array at lines 29-38 correctly maps both old naming conventions (opencode-codex-prompt and codex-prompt) for seamless upgrade migration. RETRYABLE_FS_ERROR_CODES at line 40 covers windows EBUSY/EPERM scenarios.


108-148: retry helpers correctly handle windows EBUSY scenarios.

writeFileWithRetry and renameWithRetry both use exponential backoff with 5 attempts, which is appropriate for transient windows filesystem locks. removeFileQuietly at line 142 safely swallows errors during cleanup, avoiding cascade failures.


150-186: atomic write implementation addresses prior review feedback.

the recovery block at lib/prompts/host-codex-prompt.ts:174-180 now wraps writeFileWithRetry in a try/catch as suggested in past reviews. temp file naming with ${Date.now()}-${process.pid}-${Math.random()} at line 153 provides good collision resistance for concurrent processes.


229-240: legacy cache migration handles failures gracefully.

the migration at lib/prompts/host-codex-prompt.ts:232-238 uses writeCacheFilesAtomically and catches errors to log + continue with legacy content. this addresses the prior review concern about upgrade warm-cache compatibility—users with old cache files will still work offline even if migration fails.


291-302: 304 response handling is correct.

on HTTP 304, only the meta file is updated at line 300, which is appropriate since content hasn't changed. if the meta write fails, memoryCache at line 298 is already updated, so the current process continues working—disk staleness will trigger re-fetch on next startup.


346-377: stale-while-revalidate pattern looks good.

getHostCodexPrompt at lib/prompts/host-codex-prompt.ts:346 returns cached content immediately and triggers background refresh at line 362 when stale. updating lastChecked at line 360 before scheduling refresh prevents thundering herd from concurrent callers.


229-240: vitest coverage for legacy migration and atomic writes is already comprehensive.

regression tests exist and pass at test/host-codex-prompt.test.ts:65-99 (legacy migration), test/host-codex-prompt.test.ts:101-136 (EBUSY during migration), and test/host-codex-prompt.test.ts:356-374 (EBUSY retry on fresh fetch). all scenarios mentioned are covered: successful legacy migration, atomic write failures with cleanup, and transient EBUSY recovery.

scripts/test-model-matrix.js (5)

21-65: executable resolver correctly filters windows where noise.

the regex at scripts/test-model-matrix.js:39 properly filters where output to valid paths like C:\Users\...\npm\Codex.exe, rejecting INFO: prefixed noise. this addresses windows edge cases where where returns informational messages mixed with paths.


115-153: pid tracking and serialized cleanup address prior review concerns.

registerSpawnedCodex at scripts/test-model-matrix.js:118 validates pids, and stopCodexServers at line 147 serializes cleanup via promise queue—avoiding concurrent global kills flagged in past reviews. resolveMatrixTimeoutMs at line 127 properly validates timeout to avoid ERR_OUT_OF_RANGE from NaN.


233-247: spawned codex process pid not registered for cleanup tracking.

executeModelCase at scripts/test-model-matrix.js:234 uses spawnSync which doesn't allow pid tracking since it's synchronous. however, if this were changed to async spawn (e.g., for parallelization), you'd need to call registerSpawnedCodex(pid). current implementation is safe because spawnSync blocks until completion or timeout.


463-474: direct-run guard enables safe test imports.

the isDirectRun check at scripts/test-model-matrix.js:463-467 prevents main() from auto-executing when the module is imported for testing, which is the correct pattern for making script helpers testable.


135-145: pid cleanup handles already-dead processes gracefully.

stopCodexServersInternal at scripts/test-model-matrix.js:138-144 uses runQuiet which swallows errors, so killing an already-dead pid won't cause failures. this is correct behavior for cleanup.

test/test-model-matrix-script.test.ts (5)

1-14: test setup properly isolates module state between tests.

beforeEach at test/test-model-matrix-script.test.ts:10-14 uses vi.resetModules() + vi.clearAllMocks() + vi.unstubAllEnvs(), which ensures each test gets fresh module imports and clean mock state. this is the correct pattern for testing module-level singletons like CodexExecutable.


16-25: CODEX_BIN override test covers .cmd shell detection.

the test at test/test-model-matrix-script.test.ts:16-25 verifies that .cmd files trigger shell: true, which is necessary for windows batch scripts to execute correctly through node's spawn.


27-31: invalid timeout regression test added per prior review.

the test at test/test-model-matrix-script.test.ts:27-31 verifies resolveMatrixTimeoutMs() falls back to 120000 when CODEX_MATRIX_TIMEOUT_MS is invalid ("abc"). this addresses the prior review concern about NaN causing ERR_OUT_OF_RANGE.


33-50: windows path filtering test uses proper spy cleanup.

the try/finally at test/test-model-matrix-script.test.ts:35-49 ensures platformSpy.mockRestore() runs even if assertions fail, addressing the prior review concern about spy leakage. test correctly verifies filtering of INFO: noise from where output.


64-93: stopCodexServers concurrency test verifies serialization and no double-kill.

the test at test/test-model-matrix-script.test.ts:64-93 calls stopCodexServers() twice in parallel via Promise.all. since stopCodexServersInternal clears spawnedCodexPids before iterating (at scripts/test-model-matrix.js:137), the second call finds an empty set and does nothing. assertion of exactly 2 spawnSync calls confirms both serialization AND no redundant kills.

Comment thread lib/config.ts Outdated
Comment thread lib/config.ts Outdated
Comment thread test/test-model-matrix-script.test.ts
@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

Latest deep-audit fix batch is pushed on worktree/isolated-20260228-005048.

New commits:

  • d379fa9 Fix latest PR review regressions and coverage gaps
  • 3710069 Normalize Windows path assertion in storage paths test

What this batch fixed:

  1. lib/config.ts: eliminated redundant �xistsSync checks when CODEX_HOME is not custom (IS_CUSTOM_CODEX_HOME gate).
  2. est/plugin-config.test.ts: added regression coverage for ${CODEX_HOME}/codex-multi-auth-config.json fallback (both POSIX and Windows-style paths).
  3. est/test-model-matrix-script.test.ts: added non-Windows cleanup coverage verifying kill -9 for tracked PIDs.
  4. est/eslint-config.test.ts: upgraded vendor ignore guard to behavioral assertion via ESLint isPathIgnored.
  5. est/paths.test.ts: normalized Windows-path assertion to be deterministic on non-Windows CI runners.

Validation run after final commit:

pm run lint ✅

pm run typecheck ✅

pm test ✅ (84 files, 1994 tests)

If you want, I can now walk each still-open thread and mark resolved where the latest commits cover it.

@ndycode

ndycode commented Feb 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 the current code and only fix it if needed.

Inline comments:
In `@lib/config.ts`:
- Around line 78-84: Add a regression test in test/plugin-config.test.ts that
mocks fs.existsSync to return true for both CONFIG_PATH and
LEGACY_CODEX_HOME_CONFIG_PATH, then call the function that resolves the config
path (the code path using CONFIG_PATH / LEGACY_CODEX_HOME_CONFIG_PATH and
IS_CUSTOM_CODEX_HOME) and assert it returns CONFIG_PATH; also spy on
logConfigWarnOnce to assert no migration warning was emitted. Use jest.spyOn or
similar to stub existsSync and to spy on logConfigWarnOnce, restore mocks after
the test, and ensure the test covers the case where both files exist to lock in
CONFIG_PATH priority.

In `@test/paths.test.ts`:
- Around line 87-118: The test sets process.env.CODEX_HOME alongside
process.env.HOME and process.env.USERPROFILE but only restores HOME/USERPROFILE
in the try/finally, which is confusing; update the try/finally in
test/paths.test.ts (the test that uses normalizePath and calls getConfigDir) to
also capture and restore process.env.CODEX_HOME (or explicitly document why
cleanup is delegated to the existing afterEach) so all env vars modified in the
test are restored in the same scope (reference normalizePath, getConfigDir,
CODEX_HOME, process.env.HOME, process.env.USERPROFILE, and the existing
afterEach).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 65b3883 and 3710069.

📒 Files selected for processing (5)
  • lib/config.ts
  • test/eslint-config.test.ts
  • test/paths.test.ts
  • test/plugin-config.test.ts
  • test/test-model-matrix-script.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/paths.test.ts
  • test/eslint-config.test.ts
  • test/plugin-config.test.ts
  • test/test-model-matrix-script.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/config.ts
🧬 Code graph analysis (2)
test/paths.test.ts (1)
lib/storage/paths.ts (1)
  • getConfigDir (23-25)
lib/config.ts (1)
lib/runtime-paths.ts (2)
  • getCodexHomeDir (13-16)
  • getLegacyCodexDir (191-193)
🔇 Additional comments (10)
test/test-model-matrix-script.test.ts (5)

10-14: solid deterministic test isolation setup.

test/test-model-matrix-script.test.ts:10 correctly resets module cache, mocks, and env stubs before each case, which keeps cross-test state leakage down.

As per coding guidelines, "test/**: tests must stay deterministic and use vitest."


16-31: good regression coverage for env override and invalid timeout fallback.

test/test-model-matrix-script.test.ts:16 and test/test-model-matrix-script.test.ts:27 assert both command resolution and default-timeout behavior under bad input. this is the right guardrail for script stability.

As per coding guidelines, "test/**: tests must stay deterministic and use vitest."


33-62: windows executable resolution cases are well covered and cleanup is safe.

test/test-model-matrix-script.test.ts:33 and test/test-model-matrix-script.test.ts:52 cover noisy where output and no-candidate fallback, and the try/finally spy restore pattern prevents platform mock leakage.

As per coding guidelines, "test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."


64-93: strong concurrency regression test on windows stop path.

test/test-model-matrix-script.test.ts:64 explicitly exercises concurrent stopCodexServers() calls and validates only tracked pids are killed once each with expected taskkill args/options.

As per coding guidelines, "test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."


95-124: nice non-windows parity test for kill behavior under concurrency.

test/test-model-matrix-script.test.ts:95 mirrors the windows serialization check and verifies kill -9 is used for tracked pids on linux, which closes a key cross-platform edge case.

As per coding guidelines, "test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."

test/eslint-config.test.ts (1)

1-12: good upgrade from string parsing to behavioral assertion.

the new implementation using ESLint.isPathIgnored at test/eslint-config.test.ts:9-10 is a solid improvement over the prior string-content check. the isPathIgnored API returns Promise<boolean> and is valid for eslint v10.0.0. config path resolution via fileURLToPath(new URL(...)) at line 7 is cwd-independent.

one minor gap per coding guidelines: the test doesn't include an explicit windows-style path regression (e.g., vendor\\fixture.ts). eslint normalizes internally, but a dedicated assertion would confirm deterministic behavior on windows ci runners.

optional: add windows-path regression case
 	it("keeps vendor directory ignored", async () => {
 		const configPath = fileURLToPath(new URL("../eslint.config.js", import.meta.url));
 		const eslint = new ESLint({ overrideConfigFile: configPath });
 		expect(await eslint.isPathIgnored("vendor/fixture.ts")).toBe(true);
+		// windows-style path regression
+		expect(await eslint.isPathIgnored("vendor\\fixture.ts")).toBe(true);
 		expect(await eslint.isPathIgnored("lib/config.ts")).toBe(false);
 	});

as per coding guidelines, test/**: "demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."
[approve_code_changes, suggest_optional_refactor]

lib/config.ts (1)

15-30: path constants properly handle CODEX_HOME override.

the IS_CUSTOM_CODEX_HOME guard at lib/config.ts:17 avoids redundant existsSync calls when CODEX_HOME equals the default ~/.codex. this addresses the prior nitpick about duplicate filesystem checks.

the fallback order looks correct:

  1. CONFIG_PATH (primary)
  2. LEGACY_CODEX_HOME_CONFIG_PATH (if custom home)
  3. LEGACY_CODEX_CONFIG_PATH
  4. LEGACY_CODEX_HOME_AUTH_CONFIG_PATH (if custom home)
  5. LEGACY_CODEX_AUTH_CONFIG_PATH

test coverage in test/plugin-config.test.ts:203-327 exercises both legacy path types with posix and windows-style paths.

test/plugin-config.test.ts (2)

203-264: test isolation pattern looks correct; minor robustness concern on windows path assertion.

the runWithCodexHome helper correctly uses vi.resetModules() before setting CODEX_HOME, ensuring the module re-evaluates IS_CUSTOM_CODEX_HOME and path constants. the vi.doUnmock in the finally block prevents mock leakage.

one edge case: at line 256, expect(windowsResult.expectedPath).toContain('\\') passes on non-windows CI because path.join preserves backslashes from the input string. however, this doesn't actually verify windows-native path handling—it just confirms backslashes survive string concatenation. if you want true windows behavior coverage, you'd need to mock path.sep or run on a windows runner.

that said, for the purpose of verifying the config loader reads the correct path regardless of separator style, this is acceptable.


266-327: test for LEGACY_CODEX_HOME_CONFIG_PATH addresses past review feedback.

this test exercises lib/config.ts:78-84, confirming that when CODEX_HOME is set and codex-multi-auth-config.json exists there, loadPluginConfig uses it and emits the migration warning. both posix and windows-style paths are covered.

addresses the prior major issue: "add regression test for LEGACY_CODEX_HOME_CONFIG_PATH fallback."

test/paths.test.ts (1)

68-85: solid regression test for fallback-with-accounts logic.

test correctly verifies that when primary has only non-account signals (settings.json), the resolver prefers fallback containing account storage (openai-codex-accounts.json). mock implementation at test/paths.test.ts:72-81 explicitly covers all expected file checks.

Comment thread lib/config.ts
Comment thread test/paths.test.ts
@ndycode
ndycode merged commit 7f3c502 into main Feb 27, 2026
1 check passed
@ndycode
ndycode deleted the worktree/isolated-20260228-005048 branch February 27, 2026 21:35
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