Skip to content

bug(catalog): clamp in b7ce5aad strips max/ultra from all models regardless of Codex binary version #297

Closed

Description

@Wibias

Bug

Commit b7ce5aad ("fix(codex): clamp catalog reasoning efforts to installed binary's ladder") introduced clampCatalogModelsToCodexSupport, which runs as the last step of syncCatalogModels and strips any effort rung not found in the installed Codex binary's bundled catalog.

The problem: even on a current Codex binary, codexSupportedReasoningEfforts reads the binary's bundled native model entries (gpt-5.5, gpt-5.4, etc.), and those entries only list low/medium/high/xhigh. So max and ultra are never present in the derived supported set — and the clamp removes them from every model in the catalog, including routed models and the gpt-5.6 family.

Reproduction

  1. Run opencodex sync with any current Codex binary.
  2. Check opencodex-catalog.json — every model's supported_reasoning_levels tops out at xhigh. max and ultra are gone.
  3. The Codex UI model picker caps effort selection at xhigh for all models, including gpt-5.6-sol, anthropic/claude-sonnet-5, and all other routed providers.

Root cause

codexSupportedReasoningEfforts (src/codex/catalog.ts) scans only bare native slugs (no /) in the bundled catalog for known effort strings. Those slugs ship with supported_reasoning_levels that stop at xhigh in the binary's own bundled catalog — because the binary predates the max/ultra additions to its native ladder. So the derived supported set is {low, medium, high, xhigh}.

The ensureGpt56ReasoningLevels / ensureUltraReasoningLevel calls earlier in the sync correctly add max and ultra, but the clamp at line 2205 undoes all of it before the file is written.

Suggested fixes

Option A — seed the supported set from CODEX_REASONING_LEVELS (simplest)
Treat max and ultra as always-valid in codexSupportedReasoningEfforts regardless of what the bundled native entries happen to declare. They are defined in CODEX_REASONING_LEVELS and the request pipeline already handles wire-clamping to the model's real top rung via nativeEffortClamp/adapters — so letting them through the catalog clamp is safe.

// in codexSupportedReasoningEfforts, seed with the full ladder first
const efforts = new Set(CODEX_REASONING_LEVELS.map(l => l.effort));
// then union in whatever the binary confirms

Option B — version-gate the clamp
Only apply the clamp when the Codex binary is old enough to actually crash on unknown rungs. The commit message cites 0.133.0 as the problematic version. Read the binary version and skip the clamp for binaries >= the first version that accepts max/ultra.

Option C — run ensure-functions after the clamp
ensureGpt56ReasoningLevels / ensureUltraReasoningLevel are explicitly designed to restore max/ultra. Moving them to run after clampCatalogModelsToCodexSupport instead of before would let their intent survive.

Context

  • Regression introduced: b7ce5aad (landed 2026-07-22)
  • Affects: every model in the synced catalog — anthropic/claude-sonnet-5, gpt-5.6-sol, all routed providers, etc.
  • CODEX_REASONING_LEVELS in src/reasoning-effort.ts correctly defines all six rungs (low → ultra); the issue is purely in the clamp's source-set derivation from native binary entries that predate max/ultra.

Activity

  1. lidge-jun commented on Jul 23, 2026

    @lidge-jun
    Owner

    Verified the mechanism against current dev and a live binary — and the picture is more nuanced than the report.

    The mechanism is real. clampCatalogModelsToCodexSupport runs as the last step of sync (src/codex/catalog.ts:2205), and codexSupportedReasoningEfforts derives the supported set only from bare native slugs in the installed binary's bundled catalog. If that union stops at xhigh, the clamp strips max/ultra from every model, including routed ones.

    The claimed trigger does not reproduce on a current binary. On codex-cli 0.144.5:

    gpt-5.6-sol   low medium high xhigh max ultra
    gpt-5.6-terra low medium high xhigh max ultra
    gpt-5.6-luna  low medium high xhigh max
    gpt-5.5       low medium high xhigh
    

    The derived set is the full six-rung ladder, and the synced opencodex-catalog.json on this machine keeps max/ultra on all models — the ensure-functions survive the clamp. For strict-enum binaries like 0.133.0 (the case b7ce5aa fixed), stripping is the intended behavior, since that parser rejects unknown rungs.

    So the open question is which binary you synced with. Could you share:

    1. codex --version
    2. the bare-slug effort rows from codex debug models --bundled
    3. supported_reasoning_levels for one affected model in your synced opencodex-catalog.json

    If there is a binary whose parser accepts max/ultra but whose bundled catalog omits them, we'll version-gate the clamp (your Option B) — that's the only option that preserves the 0.133.0 deserialization guard. Options A and C both bypass that guard (A unconditionally, C only for entries the ensure-functions cover, so routed models would still lose rungs on strict binaries).

    Closing as not-reproducible on current binaries; will reopen immediately with the version details.

  2. Wibias commented on Jul 23, 2026

    @Wibias
    ContributorAuthor

    @lidge-jun

    Summary

    The issue is reproducible and appears to be caused by inconsistent Codex binary selection during catalog sync.

    On the affected system, the default codex command resolved to an old Codex CLI:

    %APPDATA%\npm\codex.cmd
    codex-cli 0.133.0
    

    A newer Codex binary was also installed:

    %LOCALAPPDATA%\OpenAI\Codex\bin\<versioned-folder>\codex.exe
    codex-cli 0.145.0-alpha.30
    

    The newer binary's bundled catalog explicitly included max and ultra.

    When CODEX_CLI_PATH pointed to the newer binary, both ocx restart and ocx sync preserved max and ultra.

    When the override was missing or invalid, ocx sync fell back to the old 0.133.0 binary and removed them again.

    What We Tested

    1. Confirmed PowerShell resolved codex to version 0.133.0.
    2. Found a newer desktop Codex binary running 0.145.0-alpha.30.
    3. Confirmed the newer bundled catalog advertised:
      • gpt-5.6-sol: low, medium, high, xhigh, max, ultra
      • gpt-5.6-terra: low, medium, high, xhigh, max, ultra
      • gpt-5.6-luna: low, medium, high, xhigh, max
    4. Set CODEX_CLI_PATH to the newer binary.
    5. Ran ocx restart and confirmed max and ultra reappeared.
    6. Later ran ocx sync without a valid override and confirmed they disappeared again.
    7. Restored the valid CODEX_CLI_PATH, ran ocx sync, and confirmed max and ultra were preserved.

    Conclusion

    The confirmed failure chain is:

    ocx sync selected Codex 0.133.0
            ↓
    Its bundled catalog did not advertise max or ultra
            ↓
    The catalog clamp removed those efforts
            ↓
    They disappeared from the UI
    

    The successful control test was:

    ocx sync selected Codex 0.145.0-alpha.30
            ↓
    Its bundled catalog advertised max and ultra
            ↓
    The generated catalog preserved them
            ↓
    They remained visible in the UI
    

    The strongest conclusion is:

    Catalog sync can independently resolve an outdated Codex binary and silently regenerate a reduced catalog, even when OpenCodex previously ran successfully with a newer binary.

    Correction to the Original Theory

    This change is not sufficiently justified:

    if (entry.slug?.includes("/")) return;

    Reasons:

    • Routed models may still need to be deserialized by the Codex catalog parser.
    • It could reintroduce compatibility problems on older Codex versions.
    • It would not fix native models such as gpt-5.6-sol.

    The request-time nativeEffortClamp and the catalog clamp operate at different stages and should not be treated as equivalent.

    Recommended Fix

    1. Resolve the Codex runtime once and persist that selection.
    2. Use the same binary for:
      • Codex startup
      • --version
      • debug models --bundled
      • ocx restart
      • ocx sync
    3. Respect CODEX_CLI_PATH as the highest-priority override.
    4. Validate that the override exists and is executable.
    5. Reject missing or stale stored shim targets.
    6. Do not silently fall back to an older binary when a newer runtime was previously selected.
    7. Log the selected binary path, version, and source during every sync.
    8. Warn when reasoning efforts are removed.
    9. Expose the mismatch and recovery steps in:
      • ocx status
      • ocx doctor
      • the dashboard
    10. Keep the global clamp for Codex versions proven unable to parse unknown effort values.
    11. Do not add a routed-model exemption without a separate parser-compatibility test.

    Example diagnostic output:

    [opencodex] Codex runtime: <path> (version=0.133.0, source=PATH)
    [opencodex] Removed unsupported efforts: max, ultra
    [opencodex] A newer Codex installation may be available.
    Run `ocx doctor` or set CODEX_CLI_PATH, then run `ocx sync`.
    

    Required Tests

    • CODEX_CLI_PATH overrides PATH and stored shim targets.
    • An invalid CODEX_CLI_PATH produces a clear warning.
    • A stale stored shim is ignored.
    • Startup, restart, and sync use the same resolved binary.
    • A previously selected runtime is not silently replaced by an older fallback.
    • A new binary preserves max and ultra.
    • An old strict binary removes unsupported efforts.
    • The default effort is repaired when its original value is removed.
    • ocx status and ocx doctor report runtime mismatches and recovery steps.
  3. added a commit that references this issue on Jul 23, 2026
  4. added a commit that references this issue on Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions