When a models.dev refresh removes a model id from a provider's fallback list, existing users keep it. ensureConnection (packages/runtime-host/src/server/bootstrap-runtime-policy.ts) returns an existing connection unchanged, so enabledModelIds is never reconciled against the current registry. A persisted default target stays valid while the old id is still enabled (packages/storage/src/runtime-policy/connection-catalog-document.ts), and Host admission trusts the persisted models[] row (packages/runtime-host/src/server/connection-model-admission.ts). The id is still shown as available and default-capable, and still admitted for execution, after the provider has retired it.
Providers with modelDiscovery: { kind: 'protocol' } self-heal on the next successful discovery. Two cases do not:
opencode-free is { kind: 'fallback' } — its own reason string states the anonymous endpoint has no model-list contract, so no discovery pass ever repairs the state.
google preserves the old catalog when discovery fails.
Why this is not tied to one refresh
This is standing behaviour, not a regression. #2050 removed hy3-free from the same OpenCode Free list for the same reason — an upstream deprecation — and touched no persistence: git show 41a983804 --name-only matches zero files under packages/storage/ or packages/runtime-host/. There is no model-catalog migration or tombstone mechanism; the tombstones in sqlite-session-metadata-store.ts serve session metadata only.
Reverting a refresh does not fix it either: the ids return to the fallback lists but remain retired upstream, so a persisted default still fails at call time.
Scope
A targeted upgrade migration that, for providers without a self-healing discovery contract, removes retired ids from persisted inventories and enabled sets and clears or repairs a matching default target, with base-state upgrade tests.
Found during review of #3324 by @hqhq1025.
When a models.dev refresh removes a model id from a provider's fallback list, existing users keep it.
ensureConnection(packages/runtime-host/src/server/bootstrap-runtime-policy.ts) returns an existing connection unchanged, soenabledModelIdsis never reconciled against the current registry. A persisted default target stays valid while the old id is still enabled (packages/storage/src/runtime-policy/connection-catalog-document.ts), and Host admission trusts the persistedmodels[]row (packages/runtime-host/src/server/connection-model-admission.ts). The id is still shown as available and default-capable, and still admitted for execution, after the provider has retired it.Providers with
modelDiscovery: { kind: 'protocol' }self-heal on the next successful discovery. Two cases do not:opencode-freeis{ kind: 'fallback' }— its own reason string states the anonymous endpoint has no model-list contract, so no discovery pass ever repairs the state.googlepreserves the old catalog when discovery fails.Why this is not tied to one refresh
This is standing behaviour, not a regression. #2050 removed
hy3-freefrom the same OpenCode Free list for the same reason — an upstream deprecation — and touched no persistence:git show 41a983804 --name-onlymatches zero files underpackages/storage/orpackages/runtime-host/. There is no model-catalog migration or tombstone mechanism; the tombstones insqlite-session-metadata-store.tsserve session metadata only.Reverting a refresh does not fix it either: the ids return to the fallback lists but remain retired upstream, so a persisted default still fails at call time.
Scope
A targeted upgrade migration that, for providers without a self-healing discovery contract, removes retired ids from persisted inventories and enabled sets and clears or repairs a matching default target, with base-state upgrade tests.
Found during review of #3324 by @hqhq1025.