Skip to content

Provider/Model Naming Rules

  • Date: 2026-08-02
  • Status: Approved (2026-08-02, ADR-0009)

Principles

  • Model strings and provider names are display data, not parsed truth. They come from OmniRoute (an untrusted label source per the architecture trust boundary) and are rendered verbatim, never rewritten, executed, or used as instructions (05-security-privacy.md).
  • Provider display is best-effort (ADR-0006): a missing or wrong provider degrades to model-only, never crashes and never fabricates.

Resolved model

  • Source: the transcript's last assistant message, message.model (fallback top-level model), verbatim.
  • Examples observed: big-pickle, mimo-v2.5-free, deepseek-v4-flash, deepseek-ai/deepseek-v4-pro, gemini-3-flash-preview.
  • Model strings are never parsed. A model such as deepseek-ai/deepseek-v4-pro contains a / that is part of the model id, not a provider separator — so no inference is made from the string itself.

Provider

  • Default source: the empirical model→provider mapping (04-caching-timeout-policy.md), keyed by the verbatim model string.
  • Authoritative alternative (optional, off by default): SQLite call_logs.provider, correlated by timestamp and filtered to auto-routed successful rows (combo_name non-empty, requested_model != 'connection-test', status = 200). See ADR-0006 and the Phase 2 verification finding.
  • If the model is absent from the mapping and SQLite enrichment is disabled/unavailable, the provider is unknown → model-only display.

Empirical mapping file

  • Path: ~/.claude/statusline-model-provider.json (stable absolute path; the statusline must work without the repo present).
  • Format:
{
  "version": 1,
  "generated_at": "2026-08-02T00:00:00Z",
  "models": {
    "big-pickle": "opencode",
    "mimo-v2.5-free": "opencode",
    "deepseek-ai/deepseek-v4-pro": "nvidia"
  }
}
  • Refresh: out-of-band only, via a Scripts/ maintenance script that reads call_logs (successful auto-routed rows) and regenerates the file. The statusline never queries call_logs to build the mapping at read time — that would exceed the latency budget.
  • Confidence: the mapping is observed data (Phase 2 finding 7), not a contract. Providers can add or remove models at any time; a stale entry yields a wrong provider on screen, which is why provider is display-only and subordinate to the model core.

Context window (contextWindows) — V2

Since V2 (2026-08-04, feature/statusline-polish), the mapping may also carry the resolved model's real context window as a sibling top-level block — never a change to the models shape:

{
  "version": 1,
  "generated_at": "2026-08-04T00:00:00Z",
  "models": { "big-pickle": "opencode", "mistral-large-latest": "mistral" },
  "contextWindows": { "big-pickle": 200000, "mistral-large-latest": 262144 }
}
  • Source: model_capabilities.limit_context (populated by the models.dev Model Database sync), joined on model_id only and collapsed with MAX(limit_context) for mirror duplicates. The join is model_id-only deliberately: it matches the internal proxy models and sidesteps the provider-key mismatch documented for model_context_overrides. Scope is limit_context only (limit_input/limit_output have no consumer yet).
  • Why a sibling, not a change to models: the models block's values are provider strings consumed by resolveProvider; contextWindows values are numbers consumed by resolveContextWindow. Keeping them separate means (a) the provider logic and all its tests are byte-for-byte unchanged, (b) old mapping files without the block degrade to the payload fallback with no code-path change, and (c) the block is optional and additive — a future window source can replace it without touching provider resolution.
  • Runtime fallback chain: mapping contextWindows[model] (positive number) → payload context_window_size → segment omitted. resolveContextWindow returns null on any miss; formatContext treats a non-positive/absent override as "use the payload". Provider resolution is unaffected by the window path.
  • Refresh: same out-of-band maintenance as the provider mapping — regenerated by the same Scripts/ script, at the same time, from the same read-only Docker volume query. The statusline never writes it (read-only runtime dependency).

Display composition

Case Render
Provider known provider/modelopencode/big-pickle
Provider unknown modelbig-pickle
No resolved route yet requested label verbatim — auto/cheap

Requested label

  • Source: model.display_name || model.id from the payload.
  • Displayed only when no resolved route exists (pre-first-assistant-message) or as the final fallback when the transcript is unreadable.
  • Shown verbatim; it is the configured request alias, not a resolved value.

Case handling

  • Providers and models are displayed in the case the source provides (observed data is lowercase). No case normalization is performed — normalization would be a rewrite of label data and could mask schema drift (ADR-0008).
  • Truncation applies after composition per 01-display-requirements.md.