anomalyco / anomalyco/opencode

Cold-start default model is stale: substring priority list picks gemini-3-pro-preview over every newer model

Open
#39,125 1 comment 0 reactions 1 assignee View on GitHub

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

  1. Substring pins don't survive model generations. "gemini-3-pro" doesn't match google/gemini-3.1-pro-preview (that id contains gemini-3.1-pro), so the successor can never win. Meanwhile gpt-5.5 and claude-sonnet-4.6 do match their pins, but at lower priority indexes, so they lose to the older gemini pin.
  2. 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.
  3. The final tie-break is reverse-alphabetical id, which is the only reason -preview beats -image (p > i). Nothing checks capabilities at this point. If the catalog gained a -vision or -realtime sibling it would sort above -preview and become the default.
  4. Only status: deprecated/alpha get 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:

  1. google/gemini-3-pro-preview
  2. google/gemini-3-pro-image
  3. anthropic/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_date in the ranking. It's already on every model and getSmallModel already sorts by it - defaultModel could prefer the newest model within a preferred family/capability set.
  • Match on the catalog's family metadata (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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.