Preserve Opus fallback on older Claude Code#4480
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
This is a focused follow-up to #4472, not another Opus 5 integration. It resolves the unresolved review finding at #4472 (comment): after The fix keeps the bare Scope is two files in |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 29a898e. Configure here.
| // cannot change which fallback is selected. | ||
| if (provider === ProviderDriverKind.make("claudeAgent") && trimmed === "opus") { | ||
| const availableSlugs = new Set(options.map((option) => option.slug)); | ||
| return CLAUDE_OPUS_FALLBACK_MODELS.find((slug) => availableSlugs.has(slug)) ?? null; |
There was a problem hiding this comment.
Fallback picks custom Opus slugs
Medium Severity
For bare opus, the new fallback treats any matching option slug in CLAUDE_OPUS_FALLBACK_MODELS as eligible, including custom models added via getAppModelOptionsForInstance. That can resolve opus to a user-defined claude-opus-* custom slug when no built-in Opus is in the catalog, instead of ignoring those slugs as described for this change.
Reviewed by Cursor Bugbot for commit 29a898e. Configure here.
There was a problem hiding this comment.
Thanks — the mechanism is real, but the trigger is narrower than the description suggests, so I have left the behavior as-is for now.
The fallback compares slug strings, so it cannot distinguish a built-in entry from a custom one. I confirmed with a throwaway test that a catalog containing no built-in Opus plus a custom model whose slug is literally claude-opus-4-8 does resolve opus to that custom slug.
Two things bound it:
normalizeCustomModelSlugs()inapps/web/src/modelSelection.tsalready drops any custom slug that collides with a built-in model, so this requires a catalog where the colliding name is not currently built-in (an older Claude Code, on a custom instance, with a deliberately colliding name).- Only the five canonical slugs in
CLAUDE_OPUS_FALLBACK_MODELSare eligible. Arbitrary custom names such asclaude-opus-customare ignored, which is the case covered by the new test.
In that narrow scenario the result is a model the user explicitly named after a real Opus release, which seems closer to intent than falling through to the Sonnet default. An exact custom model named opus is also still preserved by the direct-slug match before any alias handling.
Fair hit on the PR wording though: "ignore custom claude-opus-* slugs" was looser than the code. I have corrected the description.
If maintainers would rather have this enforced strictly, the fix is to thread the existing isCustom flag into SelectableModelOption and skip custom entries in the fallback. That touches every call site that builds model options, so I kept it out of this two-file change — happy to add it here or as a follow-up if you prefer.
ApprovabilityVerdict: Needs human review An unresolved review comment identifies a potential logic bug where the fallback may incorrectly select custom Opus model slugs instead of only built-in ones, contradicting the stated intent. This model selection behavior change warrants human verification. You can customize Macroscope's approvability policy. Learn more. |
|
Live verification update: the earlier caveat in this description is now resolved. Claude Code 2.1.219 on Windows, real Opus 5 responses confirmed.
The focused resolver coverage still passes: One unrelated finding worth flagging separately from this PR. The five tests in |


Summary
opusselection when Claude Code's live catalog does not include Opus 5claude-opus-customare not selected, while an exact custom model namedopusis still preserved by the direct-slug matchRoot cause
#4472 updated the bare
opusalias toclaude-opus-5. Claude Code versions below 2.1.219 omit Opus 5 from their live catalog, soresolveSelectableModel()returnednulland T3 fell back to the provider default, currently Sonnet, instead of the newest available Opus. This resolves the unresolved review finding in #4472 (comment).Validation
vp test run packages/shared/src/model.test.ts apps/web/src/modelSelection.test.ts— 16 tests passedpnpm --filter @t3tools/shared typecheck— passedpnpm --filter @t3tools/web typecheck— passedgit diff --check— passedAn isolated desktop build also completed, ran migrations, started its backend, and opened a responsive
T3 Code (Dev)window. A successful live Claude response was not available locally because the account hit its session limit and 1M-context usage requires credits; those external limits do not affect the resolver coverage in this PR.Note
Preserve Opus model fallback in
resolveSelectableModelfor older Claude CodeWhen the provider is
claudeAgentand the requested value is'opus',resolveSelectableModelnow iterates a fixed priority list (CLAUDE_OPUS_FALLBACK_MODELS) and returns the newest available Claude Opus model slug instead of returning null. This ensures older Claude Code clients that send the bare'opus'alias still resolve to a valid model when the normalized target slug is not present in the available options.Macroscope summarized 29a898e.
Note
Low Risk
Scoped change to Claude model resolution in shared model selection logic, with unit tests; no auth or data-path changes.
Overview
Fixes bare
opuson Claude so it no longer fails resolution when the live catalog does not include the newest Opus (e.g. older Claude Code without Opus 5), which previously caused T3 to fall back to the provider default (often Sonnet).resolveSelectableModelnow, forclaudeAgentand input exactlyopus, picks the newest available slug from a fixed built-in list (claude-opus-5down through4-5). That list is independent of picker order and does not treat customclaude-opus-*slugs as fallbacks; explicit aliases likeopus-5still resolve only if that slug is in the catalog.Tests cover current catalogs, older catalogs, unavailable explicit Opus 5, and custom-only catalogs.
Reviewed by Cursor Bugbot for commit 29a898e. Bugbot is set up for automated code reviews on this repo. Configure here.