anomalyco / anomalyco/opencode
Cold-start default model is stale: substring priority list picks gemini-3-pro-preview over every newer model
@nexxeln is already working on this.
Since Jul 27, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
What happened
I run opencode headless in fresh sandboxes every day (eval harness, 180 tasks/day) with a single gateway provider configured and no model set. Every single run picks google/gemini-3-pro-preview. That model is a generation behind and it never changes, so I dug into why. Verified on 1.18.7 (current npm latest).
The cause
packages/opencode/src/provider/provider.ts (dev branch):
const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
export function sort<T extends { id: string }>(models: T[]) {
return sortBy(
models,
[(model) => priority.findIndex((filter) => model.id.includes(filter)), "desc"],
[(model) => (model.id.includes("latest") ? 0 : 1), "asc"],
[(model) => model.id, "desc"],
)
}
defaultModel falls through cfg.model -> recents in state/model.json -> sort(provider.models)[0]. With no config and no state, the priority list decides, and it has gone stale in a few compounding ways:
- Substring pins don't survive model generations.
"gemini-3-pro"doesn't matchgoogle/gemini-3.1-pro-preview(that id containsgemini-3.1-pro), so the successor can never win. Meanwhilegpt-5.5andclaude-sonnet-4.6do match their pins, but at lower priority indexes, so they lose to the older gemini pin. findIndex+"desc"means the last array entry is the highest priority. Easy to misread as first-wins. Either way the top preference is whatever was appended last, back when gemini-3-pro was current.- The final tie-break is reverse-alphabetical id, which is the only reason
-previewbeats-image(p > i). Nothing checks capabilities at this point. If the catalog gained a-visionor-realtimesibling it would sort above-previewand become the default. - Only
status: deprecated/alphaget filtered before the sort, so a superseded-but-active preview model stays eligible forever.
Repro
Clean machine (no state/model.json), opencode.json with just a gateway provider and no model:
{
"provider": {
"vercel": { "options": { "apiKey": "{env:AI_GATEWAY_API_KEY}" } }
}
}
opencode run "hello" picks google/gemini-3-pro-preview. Emulating sort() over that provider's catalog (499 models in the 1.18.7 build) ranks:
google/gemini-3-pro-previewgoogle/gemini-3-pro-imageanthropic/claude-sonnet-4
Why nobody notices
The recents fallback shadows this for anyone who has ever picked a model interactively. Ephemeral environments hit the cold-start path every time - CI, sandboxes, containers, eval harnesses - and they're all quietly running on gemini-3-pro-preview today.
Suggested fix
Any of these kills the staleness class rather than today's instance:
- Use
release_datein the ranking. It's already on every model andgetSmallModelalready sorts by it -defaultModelcould prefer the newest model within a preferred family/capability set. - Match on the catalog's
familymetadata (e.g.gemini-pro) instead of raw id substrings, so successor generations inherit the preference. - At minimum, filter for
toolcall+ text output ahead of the id tie-break so an image variant can't become the default by alphabetical accident.
Happy to PR whichever direction you prefer.
Environment: opencode 1.18.7 (npm opencode-ai, also confirmed in 1.17.0), single gateway provider via opencode.json, no model configured, clean state.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.