Skip to content

fix(codex): stable Codex runtime selection for sync/clamp (#297)#359

Merged
Wibias merged 8 commits into
lidge-jun:devfrom
Wibias:fix/stable-codex-runtime-selection
Jul 23, 2026
Merged

fix(codex): stable Codex runtime selection for sync/clamp (#297)#359
Wibias merged 8 commits into
lidge-jun:devfrom
Wibias:fix/stable-codex-runtime-selection

Conversation

@Wibias

@Wibias Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Introduce one canonical ResolvedCodexRuntime resolver (env → configured → shim → PATH → fallback) with --version validation and persistence in codex-runtime.json.
  • Make bundled-catalog probing, effort clamping, and ocx v2 features use that same runtime so sync cannot silently pick an older PATH binary and strip max/ultra.
  • Surface diagnostics in ocx status, ocx doctor (including optional --fix-codex-runtime), sync logs, and the Startup dashboard warning.

Test plan

  • bun test tests/codex-runtime.test.ts
  • bun test tests/cli-status-json.test.ts
  • bun test tests/doctor.test.ts
  • bun test tests/codex-catalog.test.ts -t reasoning-effort
  • bun run typecheck
  • Full bun test + bun run lint:gui / build on CI
  • Manual Windows dual-install: PATH 0.133.0 vs CODEX_CLI_PATH 0.145.0-alpha.30 (clamp warning vs preserve max/ultra; restart/sync keep runtime)

Closes / related: #297

Summary by CodeRabbit

  • New Features
    • Added Codex runtime discovery in settings, startup UI, and CLI diagnostics (including version/source, warnings, and “newer available”).
    • Added ocx doctor --fix-codex-runtime and expanded ocx doctor output with Codex runtime selection diagnostics.
    • Enhanced ocx status (including --json) to report Codex runtime and catalog clamp state.
    • Startup now shows localized runtime clamp warnings and offers a copy-to-clipboard fix command when applicable.
  • Improvements
    • Improved Windows/POSIX Codex command launching behavior and added i18n performance optimizations (memoized translations).

Keep catalog probing and effort clamping on one persisted runtime so PATH cannot silently downgrade away max/ultra.
@github-actions github-actions Bot added the bug Something isn't working label Jul 23, 2026
@coderabbitai

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Wibias added 2 commits July 23, 2026 21:13
…lean

Reject unparseable --version output, stop v2 from bypassing validation, avoid false replacedConfigured when env matches persisted, and keep settings probes off the hot PATH scan.
Numeric prerelease compare, redact probe failures, mkdir before persist, safer doctor --fix when CODEX_CLI_PATH wins, and localized Startup runtime warnings with complete sync remediation.
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up after CodeRabbit / Codex review

Pushed 4f49a950 and 3f3ea0c7 addressing the actionable findings:

  • Reject unparseable --version output
  • ocx v2 always goes through the canonical resolver
  • No false replacedConfigured when env path equals persisted
  • Numeric prerelease compare (alpha.9 < alpha.30)
  • Redact probe failure messages; mkdir before persist
  • ocx doctor --fix-codex-runtime refuses to claim success when CODEX_CLI_PATH still wins
  • Startup warning uses i18n + copyCommand; fix command is doctor --fix && sync / sync as appropriate
  • Settings hot path uses discoverAlternatives: false; sync logs stay on stderr so stdout JSON stays parseable

Skipped / deferred:

  • Reworking Windows .cmd shell launching in codexExecInvocation (pre-existing platform requirement for npm shims; out of scope for this runtime-selection fix)
  • Full locale-neutral API prose removal for CLI warning strings (dashboard now builds copy from structured fields + i18n; CLI status still uses English diagnostics like the rest of ocx status)

coderabbitai[bot]

This comment was marked as resolved.

Remove the implementation plan from the PR, restore newerAvailable on /api/settings via memoized full resolve, correlate clamp diagnostics to the active runtime, and isolate v2 invocation test state.
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Removed docs/superpowers/plans/... from the PR.

Addressed the latest unresolved CodeRabbit threads:

  • /api/settings now does memoized full alternative discovery so newerAvailable works
  • clamp warnings only apply when the persisted clamp matches the current runtime
  • unresolved fallback with probe failures now warns
  • routine runtime log line goes to stderr (not console.warn)
  • v2 invocation cases each use an isolated configDir

coderabbitai[bot]

This comment was marked as resolved.

…er compare

Add first-write directory regression tests, return persistError from resolveAndPersistCodexRuntime, and compare hyphenated prerelease identifiers with deterministic code-unit ordering.
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Dual review (security + bugs/edge cases)

Bot review threads are already cleared (0 unresolved). CI is green. Independent second look below — several earlier bot findings were fixed; a few concrete leftovers remain.

Security categories checked

No malware, backdoor, hidden C2, admission bypass, or classic XSS. Runtime selection persists under OPENCODEX_HOME (codex-runtime.json / clamp state). Paths in diagnostics go through redactUserPath / redactSecretString. Version probes require parseable Codex --version output (null versions rejected).

Blockers / should-fix

  1. Windows .cmd/.bat probes still use shell: true (High, security)src/codex/exec-invocation.ts
    codexExecInvocation quotes the path and sets shell: true. On Windows, Node does not safely escape cmd metacharacters in that mode, so CODEX_CLI_PATH / shim / PATH .cmd values can inject via the --version probe and codex debug models --bundled. The repo already has the hardened helper: commandInvocation() in src/lib/win-exec.ts (ComSpec /d /s /c + cross-spawn escaping + windowsVerbatimArguments). Route Codex exec through that (as ocx v2 already does) and drop shell: true here.

  2. Bundled catalog / clamp resolve does not forward isolatable deps (Medium)src/codex/catalog/bundled.ts, effort.ts
    resolveAndPersistCodexRuntime({ execFileSync }) drops configDir / env / platform / existsSync / readFileSync. Stubbing only execFileSync in tests (or unusual callers) can still resolve against and write the real OS config dir. Extend BundledCatalogDeps and forward those fields; remove the as never cast.

Non-blocking / already in good shape

  • SemVer prerelease numeric compare, null-version rejection, env-vs-configured dedup, doctor CODEX_CLI_PATH honesty, mkdir-on-persist, Startup i18n + complete fix commands, resolve memoization, clamp↔runtime correlation — look addressed.
  • English warning strings on /api/settings / ocx status are fine for CLI; the dashboard correctly localizes from structured fields.
  • mkdir + persistError coverage exists in tests/codex-runtime.test.ts.

Suggested fix order

  1. Rewrite codexExecInvocation (or its call sites in runtime.ts / bundled.ts) to use commandInvocation.
  2. Thread resolver deps through BundledCatalogDeps.
  3. Add a focused /api/settings codexRuntime shape regression near tests/settings-stream-mode.test.ts.

Route .cmd/.bat probes through commandInvocation instead of shell:true, forward BundledCatalogDeps into the runtime resolver, and cover /api/settings codexRuntime shape.
coderabbitai[bot]

This comment was marked as resolved.

Pass configDir into persistEffortClamp, expose catalogClamp.runtimeVersion
in status JSON, memoize i18n t to avoid Startup refresh loops, and harden
settings clamp-correlation coverage.
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the latest CodeRabbit batch (2 inline + 2 outside-diff) in 6879d15:

  1. effort.tspersistEffortClamp(..., { configDir: deps.configDir }) on all write/clear paths + nested configDir regression test
  2. settings-stream-mode.test.ts — real clamp correlation via resolvable fake Codex returning 0.133.0
  3. status.ts (outside diff) — catalogClamp.runtimeVersion aligned with /api/settings
  4. i18n/provider.tsx (outside diff) — memoize t / provider value so Startup refresh does not loop

Tests: bun test on the touched suites + bun run typecheck green.

coderabbitai[bot]

This comment was marked as outdated.

@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Dual review (security + bugs/edge cases) — re-review @ 6879d15

Prior High blockers from the last dual review look fixed (.cmd/.bat via commandInvocation, no shell: true; BundledCatalogDeps forwarded; CodeRabbit batch addressed). CI-facing suites for the touched paths are green. Second look on current head:

Security

Verdict: merge-ready for security (no blockers).

Checked:

  • Windows launcher path uses shared commandInvocation (ComSpec /d /s /c + escaping); comment-only mention of shell: true remains.
  • Diagnostics (/api/settings, ocx status --json, probe failures, persist errors) go through displayCodexRuntimePath / redactUserPath / redactSecretString.
  • Persist uses mkdirSync(..., 0o700) + atomicWriteFile; no request-body/API-key logging introduced.
  • doctor --fix-codex-runtime only rewrites runtime selection; does not sync itself. CODEX_CLI_PATH still correctly wins.

Non-blocking: bare / non-absolute CODEX_CLI_PATH values (e.g. codex / codex.cmd) can be validated and persisted as configured. Later resolves then depend on the ambient PATH. Prefer resolving to a canonical absolute path before persist (stability more than classic injection).

Bugs / #297 goal

Verdict: not fully merge-ready — one remaining should-fix for the in-process remediation path.

Should-fix

  1. Bundled catalog cache ignores selected runtime (P1)src/codex/catalog/bundled.ts (~134–162)
    loadBundledCodexCatalog() memoizes for 60s whenever there are no injected deps. The cache key does not include the resolved Codex command/version or codex-runtime.json mtime.
    Failure mode: long-lived proxy caches an old debug models catalog (no max/ultra) → ocx doctor --fix-codex-runtime (other process) updates the persisted runtime → dashboard Sync hits POST /api/sync in the same proxy → loadCatalogForSync + codexSupportedReasoningEfforts reuse the stale catalog and keep clamping. Fresh ocx sync CLI (new process) is fine; GUI//api/sync is not.
    Same class of blind spot: resolveCodexRuntime’s 15s cache (runtime.ts ~344–373) keys on env only, not persisted runtime file — status/settings can lag after doctor fix.
    Fix: include runtime identity (or invalidate) in the bundled-catalog cache; clear both caches when persistCodexRuntime / doctor --fix writes (or on /api/sync).

Residual / follow-ups (non-blocking)

  • src/cli/doctor.ts (~511–514): clamp warning does not use effortClampAppliesToRuntime, so doctor can still complain after status/settings correctly clear catalogClamp.active.
  • gui/src/pages/Startup.tsx: when clamp is active and no newerAvailable, recommending only ocx sync can re-apply the same clamp on a genuinely old runtime.
  • Test gap: no warm-process coverage for doctor-fix → /api/sync without restart.

What looks solid

  • Priority order env → configured → shim → PATH → fallback, with stickiness to persisted configured when still valid.
  • Clamp correlation (effortClampAppliesToRuntime) on settings/status; catalogClamp.runtimeVersion aligned.
  • configDir isolation for runtime + clamp persist (including effort.ts).
  • i18n t memoization (Startup refresh loop addressed).
  • Focused regressions for Windows exec, settings clamp correlation, status JSON shape.

Bottom line: security OK to merge; for #297, fix (or deliberately document + invalidate on sync) the 60s bundled-catalog cache before calling the GUI remediation path done.

…w-ups

Bust resolve/catalog memos when persisted selection changes so dashboard
/api/sync does not keep clamping after doctor --fix. Also track default
effort clamps, forward full resolver deps, and harden related tests.
@Wibias

Wibias commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the re-review P1 + latest CodeRabbit batch in 716f39c:

Cache / #297 remediation

  • Bundled catalog cache is now keyed by selected runtime (command + version)
  • Resolve memo includes on-disk codex-runtime.json stamp (cross-process doctor --fix)
  • persistCodexRuntime clears the process resolve memo
  • Regression: doctor-style persist → same-process loadBundledCodexCatalog picks up max/ultra

CodeRabbit

  • Active catalogClamp.runtimeVersion coverage in status --json
  • Probe-fail clamp test actually exercises execFileSync
  • Cross-platform username redaction assert in settings test
  • Clamp diagnostics include unsupported default_reasoning_level changes
  • Forward now / discoverAlternatives through catalog deps
  • Status replacedConfigured predicate matches catalog; resolver throw surfaces in warning

Tests + typecheck green on the touched suites.

@Wibias
Wibias merged commit adb6615 into lidge-jun:dev Jul 23, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant