Enhancement. A code sketch already exists in `api/app/routes/models.py` (`refresh_catalog`).
Context
`refresh_catalog` upserts the provider's fetched models but never disables catalog rows that are absent from the live fetch. So a retired dated snapshot stays `enabled=TRUE` and `prefer_cheapest`/default resolution can still pick a dead model — exactly how `claude-3-haiku-20240307` (retired 2026-04-19) caused a 404 storm in code-block translation. We patched the seed (migration 059), but the durable fix is reconciliation.
To look into (caveats already noted in the code sketch)
After upsert, disable enabled rows for the provider whose `model_id` is not in the live set, but:
- Only when the fetch is known-complete (not paginated/partial) — else valid models get wrongly disabled.
- Scope by category (an extraction refresh mustn't disable embedding/vision rows).
- Never auto-disable the active extraction model or `is_default` — warn instead.
- Distinguish operator-intent disables from auto-disables (a `disabled_reason` column) so a manual enable isn't clobbered.
Enhancement. A code sketch already exists in `api/app/routes/models.py` (`refresh_catalog`).
Context
`refresh_catalog` upserts the provider's fetched models but never disables catalog rows that are absent from the live fetch. So a retired dated snapshot stays `enabled=TRUE` and `prefer_cheapest`/default resolution can still pick a dead model — exactly how `claude-3-haiku-20240307` (retired 2026-04-19) caused a 404 storm in code-block translation. We patched the seed (migration 059), but the durable fix is reconciliation.
To look into (caveats already noted in the code sketch)
After upsert, disable enabled rows for the provider whose `model_id` is not in the live set, but: