2026-05-08-anthropic-catalog-ingestor
2026-05-08 — Anthropic catalog ingestor (first concrete ModelIngestor)
Summary
Closed the multi-round "0 ingestors registered" gap that surfaced in R17, R18, and R19. The IngestorScheduler scaffolding has existed since R15; what was missing was a concrete ModelIngestor impl. This entry adds the first one — Anthropic's /v1/models — registered hourly when ANTHROPIC_API_KEY is set.
Ships paired with the mTLS strict-mode cutover runbook (R5 in the R19 risk register) so the runbook is documented with rollback signals before anyone flips the flag.
Lockstep checklist
- [x] Source —
src/router/intelligence/anthropic-ingestor.ts(115 lines) implementsModelIngestor, polls Anthropic/v1/modelswithx-api-key+anthropic-version: 2023-06-01, returns liveness +displayName+createdAtpatches. Pricing intentionally NOT supplied (Anthropic's models endpoint omits it;provider-catalog-pricing.tsremains the curated source). - [x] Tests —
src/router/intelligence/anthropic-ingestor.test.ts(6 regression tests): liveness patch shape, no-API-key skip, non-2xx HTTP error path, network throw, no-pricing assertion, key-rotation safety (resolver called fresh each cycle). - [x] Wiring —
src/router/model-router-init.tsregisters the ingestor with60 60 1000ms (hourly) whenprocess.env.ANTHROPIC_API_KEYis set. LogsAnthropic catalog ingestor registered (hourly)on bootstrap; logs the disabled state otherwise. - [x] SDK — None needed.
ModelIngestoris internal infrastructure; no API surface change. - [x] MCP — None needed. Same reason.
- [x] Runbook —
docs/runbooks/mtls-strict-cutover.md: gates strict-mode cutover on zero advisory bypasses over 24h, documents Option A (config redeploy) + Option B (ChangeSet+restart), and a rollback procedure tied to legitimate-caller 403 detection. - [x] Ship log — this file.
Why this matters
R17/R18/R19 each flagged "0 ingestors registered" as drift surface against LiteLLM's 100+ auto-discovered catalog. The static catalog (provider-catalog-pricing.ts) is hand-maintained and silently goes stale when Anthropic adds/removes a model. The ingestor doesn't replace pricing curation; it just makes model existence drift detectable through the deprecation detector and intelligence store.
Hourly is conservative. The Anthropic /v1/models payload is small; rate limits are not a concern at this cadence. We can drop to 15-minute or 5-minute intervals after observing real drift cadence in production.
Caller-id discipline
The ingestor's getApiKey: () => string | undefined is called fresh on every cycle, so key rotation propagates without process restart. Verified by anthropic-ingestor.test.ts:rotation-safety.
Verification
Local:
pnpm tsgo # 0 errors
pnpm lint # 0 warnings, 0 errors
pnpm test:fast -- src/router/intelligence/anthropic-ingestor.test.ts # 6/6 pass
pnpm check # All gates pass
Production:
After deploy, the bootstrap log line will confirm registration:
Anthropic catalog ingestor registered (hourly)
If ANTHROPIC_API_KEY is missing in production env, the alternate path logs:
Anthropic catalog ingestor not registered: ANTHROPIC_API_KEY not set
The ingestor's run output appears in IngestorScheduler logs as anthropic ingest: N models reported alive once an hour.
Follow-ups (not in this PR)
- OpenAI
/v1/modelsingestor (same shape, easier — OpenAI returns just IDs) - Google
models.listingestor (different auth, different shape) - DeepSeek, x.ai, Groq, Perplexity, Moonshot ingestors
- Wire ingestor output to the deprecation detector so model removal triggers an alert (currently the patches just update the intelligence store)
- Drop to 15-min cadence after one week of hourly observation