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-levelmodel), 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-procontains 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_namenon-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 readscall_logs(successful auto-routed rows) and regenerates the file. The statusline never queriescall_logsto 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 onmodel_idonly and collapsed withMAX(limit_context)for mirror duplicates. The join ismodel_id-only deliberately: it matches the internal proxy models and sidesteps the provider-key mismatch documented formodel_context_overrides. Scope islimit_contextonly (limit_input/limit_outputhave no consumer yet). - Why a sibling, not a change to
models: themodelsblock's values are provider strings consumed byresolveProvider;contextWindowsvalues are numbers consumed byresolveContextWindow. 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) → payloadcontext_window_size→ segment omitted.resolveContextWindowreturnsnullon any miss;formatContexttreats 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/model — opencode/big-pickle |
| Provider unknown | model — big-pickle |
| No resolved route yet | requested label verbatim — auto/cheap |
Requested label¶
- Source:
model.display_name || model.idfrom 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.