Young's Mind
03 Decisions

Model List Must Be Derived

Consolidated. This note's findings now live in ../MIN-STUDIES.md §6. The original is kept for its measurements; do not re-derive from it.

The model list changes constantly — so it must be derived

Young, 2026-10-10: "Updated the list with your find, the GO/ZEN free models keep update all the time."

That observation is a design requirement, not a note. A list that changes all the time is exactly the class of fact V3 exists to handle: if it is written down in two places, it drifts. And I found the drift live while checking.


1. 🔴 The defect, measured — our config already has it

Our opencode.json declares a model under the wrong endpoint. Verified by call, not by reading:

# space-bunny-free, sent to the GO base — where our config puts it
POST https://opencode.ai/zen/go/v1/chat/completions
  → {"error":{"type":"server_error","message":"Upstream request failed: Model is unavailable."}}

# the same model, sent to the ZEN base — where it actually lives
POST https://opencode.ai/zen/v1/chat/completions
  → {"id":"07193e23...","model":"space-bunny-free","usage":{...}}   ✅

The config declares:

providerbaseURLmodelsverdict
lab-go…/zen/go/v1glm-5.3-flash · longcat-2.0 · qwen3.8-flash✅ all live on GO
lab-go…/zen/go/v1space-bunny-free❌ lives on ZEN, not GO

So a model we list is silently unreachable — and the failure is a server_error at call time, not at config time. This is the V2 pattern exactly: a fact restated in a second place, drifting, with the failure surfacing far from the cause.

Why it happened is the interesting part: space-bunny-free IS a real ZEN model. Whoever added it had the right name and the wrong endpoint. A saved list cannot tell you that; a derived one cannot get it wrong.


2. The provider agrees the list is unstable

Measured on both endpoints:

cache-control: no-store

The provider deliberately does not let the list be cached. That is a design statement: do not hold this. And the docs say it in words:

"The list of models may change as we test and add new ones." — opencode.ai/docs/go

Counts change too. GO read 38 models and ZEN 14 in this session; the free sets were:

EndpointFree models live 2026-10-10
GOlongcat-2.5-preview-free · step-5-preview-free
ZENjev-1.13-free · longcat-2.5-preview-free · mimo-v2.6-flash-free · muse-spark-1.2-contributor-free · muse-spark-1.3-contributor-free · nemotron-3.5-lightning-free · space-bunny-free · step-5-preview-free

8 free on ZEN, 2 on GO — and both lists move.


3. The rule this produces

A model list is DERIVED, never saved. It joins the registry's rule:

FactHomeDerived?
agent identity, port, lanecortex/registry/agents.yaml✅ everything derives
which models exist, and whereGET /zen/go/v1/models · GET /zen/v1/models✅ must derive — NEW
which models are freethe same endpoint ('free' in id)✅ derive
a model's price and monthly limitthe published table⚠️ docs — see below

And the endpoint is part of the model's identity. space-bunny-free is not a model name alone — it is (zen, space-bunny-free). A model without its endpoint is half a fact, which is precisely how the defect above happened.


4. What this means for the plan

StageChange
1 — registrythe registry declares which endpoint each model belongs to, and a drift probe refreshes the live list and fails on any model that is not served there
6 — costthe per-model limit is joined at read time: the live list gives the ids, the published table gives price and limit — never a single saved table
8 — observabilitythe readout shows when the model list was last refreshed, and flags a model that has stopped being served

Stage 1's drift probe now has a second subject, and it is a real one: mutate the registry's model, and the probe must fail because the endpoint does not serve it. That is the same shape as the port-drift probe, and it would have caught space-bunny-free on the day it was added.


5. The honest limit of the derivation

The live list gives ids, not prices. /models returns ids; the price and monthly limit come from the published docs table, which is not an API.

So the split is:

FactSourceStability
which models exist, and on which endpoint/models APIderived — authoritative
whether a model is free/models — 'free' in idderived
price, monthly limitthe docs tabledocumented — re-checked, not derived
GO window usage/zen/go/v1/usagederived
ZEN credit balanceconsole onlynot derivable — unknown

That is four derivable facts and two that are not — and the ledger should say which is which rather than blending them into one confident number.


6. Not established

  • The /models endpoints were read, not schema-verified. The shape is {"data":[{"id": …}]}; other fields exist but were not enumerated.
  • The published price table was not cross-checked against a live call. Prices are from opencode.ai/docs/go; a real call's usage object reports tokens, not dollars, so the join is by model id and the docs' table.
  • The space-bunny-free mismatch was proven on one model. Whether other saved models are similarly misplaced was checked — the other three on lab-go are correct — but the openrouter-free provider's 8 models were not verified against OpenRouter's own list.
  • The list will have changed by the time this is read. That is the point.
Copyright © 2026