Skip to content

Knowledge Base

Purpose

This is the canonical record of project knowledge that has been verified or is explicitly tracked as unresolved. It is not a transcript and it is not a place to paste raw external research.

Raw Perplexity responses belong in Research/. Local investigation reports belong in Findings/. Promote information here only when its evidence and confidence are clear.

Project status

Area Status
Internet research Complete
Local investigation Complete (Phase 1B findings recorded 2026-08-02)
Architecture Approved (Phase 2 complete 2026-08-02; data sources and failure behavior identified)
Design Approved (Phase 3 complete 2026-08-02; ADR-0009)
Implementation Complete (Phase 4 complete 2026-08-03; committed 5c0973b, deployed to ~/.claude/statusline/)
Validation Complete (Phase 5 complete 2026-08-03; report in Findings/2026-08-03 - Phase 5 - Validation Report.md)
V1.0 release Complete (ADR-0010 recorded 2026-08-03; merged to main, tagged v1.0.0)
Post-V1.0 styling Implemented (2026-08-04; feature/statusline-polish, commit c0792f9)
Context window data source Investigated (2026-08-04; findings in Findings/2026-08-04 - Context Window Data Source and Statusline Styling.md); Model Database sync verified — models.dev sync populated model_capabilities 0 → 7,278 rows with limit_context; V2 implemented & deployed (2026-08-04; feature/statusline-polish, commits e3c03c5/ca4a9ec) — mapping carries contextWindows, statusline shows the resolved model's real window with payload fallback

Confirmed facts

Fact: The statusline payload carries the requested model, not the resolved route

  • Status: Confirmed
  • Evidence: Findings/2026-08-01 - Phase 1B - Statusline Input Contract.md; model.display_name in the payload equals the configured ANTHROPIC_MODEL (auto/cheap) in .claude/settings.local.json.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: This is the documented Claude Code contract, not a defect.

Fact: OmniRoute records the resolved provider/model per request in SQLite

  • Status: Confirmed
  • Evidence: Findings/2026-08-01 - Phase 1B - OmniRoute Topology and Logs.md; call_logs table columns model and provider hold the post-routing values, separate from requested_model. Sample: requested_model=auto/cheapmodel=big-pickle, provider=opencode.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: Data is in the Docker volume omniroute-data (/app/data/storage.sqlite), accessible read-only via docker exec or a throwaway volume-mounted container.

Fact: The session transcript records the resolved model per assistant message

  • Status: Confirmed
  • Evidence: Findings/2026-08-01 - Phase 1B - Statusline Input Contract.md; 101 assistant messages in the current session transcript carry a model field with resolved values (deepseek-v4-flash, mimo-v2.5-free, gemini-3-flash-preview, sonnet).
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: The transcript model is the API response's model field echoed by OmniRoute; it carries the model string but not a separate provider field.

Fact: The statusline contract has no resolved-provider field

  • Status: Confirmed
  • Evidence: Full statusline schema (code.claude.com/docs/en/statusline) and local payload inspection; only model.id and model.display_name exist.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: Any resolved-provider display requires a side channel (SQLite or transcript), not the statusline payload.

Fact: Session-level correlation is not functional in this installation

  • Status: Confirmed
  • Evidence: Findings/2026-08-01 - Phase 1B - OmniRoute Topology and Logs.md; session_tag is NULL on all 3680 call_logs rows; session_model_history has 0 rows.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: A statusline can report the most recent route but cannot reconstruct per-session history from OmniRoute logs alone.

Fact: OmniRoute management APIs require a separate token

  • Status: Confirmed
  • Evidence: Findings/2026-08-01 - Phase 1B - OmniRoute Topology and Logs.md; all /api/usage/*, /api/sessions, /api/analytics/*, and /api/cloud/model/resolve return 403 AUTH_001 with the gateway token.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: A management token (scope ["manage","grammars"]) exists in api_keys but is not in the local configuration.

Fact: The resolved route for an auto combo is not stable across requests

  • Status: Confirmed
  • Evidence: Findings/2026-08-02 - Phase 2 - Read-only Verification.md; combo_name='auto/cheap' (482 rows) resolves to 20+ (provider, model) pairs with mixed success (status 200/400/402/413/429/499/502).
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: A statusline that displays "the resolved route" displays the most recent route, not a stable value. Each statusline event must re-read.

Fact: The transcript carries the resolved model on every assistant message

  • Status: Confirmed
  • Evidence: Phase 2 transcript inspection; 78/78 assistant messages carry message.model, values matching call_logs.model (big-pickle, mimo-v2.5-free).
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: In the current Claude Code version the field is nested at message.model (Phase 1B read it at the top level) — evidence of transcript version drift. No separate provider field exists in the transcript.

Fact: The transcript read is sub-millisecond; SQLite read latency is dominated by Docker startup

  • Status: Confirmed
  • Evidence: Phase 2 measurements: transcript tail 0.00 s; SQLite query <10 ms; docker run startup 0.21–0.29 s; docker run + apk add sqlite 0.73–1.13 s. sqlite3 is not present in the omniroute or plain alpine images.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: The 300 ms statusline debounce is exceeded by the docker run + install path; a pre-baked sqlite3 image is required for any SQLite-based read.

Fact: combo_name is the reliable filter for auto-routed traffic in call_logs

  • Status: Confirmed
  • Evidence: Phase 2 query; requested_model='auto/cheap' matches only 14 rows (all status 499, provider auto — the blocked/classifier-gated requests), while combo_name='auto/cheap' matches 482 rows including all concrete resolutions.
  • Observed on: 2026-08-02
  • Confidence: High
  • Notes: Any SQLite query for auto routes must filter on combo_name, not requested_model.

Fact: The empirical model→provider mapping is currently unambiguous

  • Status: Confirmed (observation about current data)
  • Evidence: Phase 2 query over successful auto-routed rows; no model appears under more than one provider (e.g., big-pickleopencode 864, mimo-v2.5-freeopencode 145).
  • Observed on: 2026-08-02
  • Confidence: Medium — reflects the current catalog; providers can add or remove models, so this is a heuristic, not a contract.

Fact: The statusline never calls OmniRoute at read time

  • Status: Confirmed (verified from code and contract test)
  • Evidence: Statusline/lib/* read only the local transcript (JSONL) and the local mapping file; contract test case 16 asserts the route-path modules load no child_process; Statusline/Design/04-caching-timeout-policy.md.
  • Observed on: 2026-08-03
  • Confidence: High
  • Notes: "OmniRoute unavailable" exercises no statusline code path beyond a frozen transcript; this is why the Phase 5 scenario is opportunistic, not a required induced failure.

Fact: Assistant messages stream into the transcript progressively with write-time timestamps

  • Status: Confirmed
  • Evidence: Transcript lines for one message (id 313c518e): thinking 07:24:04.911Z, text 07:24:05.368Z, tool_use 07:24:07.688Z — separate flushed lines, distinct timestamps, model present on the first (thinking) entry.
  • Observed on: 2026-08-03
  • Confidence: High
  • Notes: A new turn produces a fresh, parseable assistant entry with a fresh timestamp at generation start. This is why the [stale] marker clears before the response completes — correct per the ADR-0009 design.

Fact: Auto-routing is request-weight dependent (observed once)

  • Status: Confirmed (single observation pair)
  • Evidence: call_logs 2026-08-03 07:45–07:46 — a fresh light session (tokens_in ≈ 31K) resolved mimo-v2.5-free/opencode; a heavy session (tokens_in ≈ 152K) stayed big-pickle/opencode; both status 200.
  • Observed on: 2026-08-03
  • Confidence: Medium — a single observed pair; routing heuristics may consider other request characteristics.
  • Notes: The statusline faithfully shows whatever the last assistant message resolved to; it does not explain why a route was chosen.

Fact: The statusline failure invariant holds

  • Status: Confirmed
  • Evidence: 29 contract tests (all 16 acceptance cases + invariants) pass; live failure paths verified — missing transcript → auto/cheap, unparseable stdin → "", empty mapping → model-only. Every failure path degrades to the requested label or empty; never undefined/null/error in output.
  • Observed on: 2026-08-03
  • Confidence: High

Fact: Context window comes from Claude Code's payload, not from an observed source (superseded by V2)

  • Status: Confirmed (V1 behavior); superseded by V2 for the context-window segment
  • Evidence: Under V1, formatContext read ctx.context_window_size from the stdin payload (Claude Code provides this) — no hardcoded context-window value exists in the statusline source. V2 (2026-08-04, feature/statusline-polish) prefers the resolved model's real window from the mapping's contextWindows block and falls back to the payload when no entry exists. The payload still reflects what Claude Code believes the requested model's window is.
  • Observed on: 2026-08-04
  • Confidence: High
  • Notes: This is the same "requested vs resolved" divergence documented for model names. V2 closes it for models covered by model_capabilities.limit_context.

Fact: OmniRoute's model_context_overrides table has real context windows, but coverage is uneven

  • Status: Confirmed
  • Evidence: model_context_overrides has 424 rows (all source = 'auto:discovery'), keyed by (provider, model_id) with a real_context column. Providers with entries: openrouter (362), gemini (47), groq (15). Providers with no entries: opencode, nvidia, local, anthropic. Only 62 resolved calls in call_logs have a direct model_id match to this table. Provider keys differ between call_logs and model_context_overrides (e.g., call_logs records provider = "nvidia" for nemotron models; model_context_overrides stores them under provider = "openrouter").
  • Observed on: 2026-08-04
  • Confidence: High
  • Notes: This is the most promising source for the resolved model's real context window, but it does not cover the internal proxy models (big-pickle, mimo-v2.5-free, deepseek-v4-flash-free) that are the most common resolved models in this setup. Extending the mapping script to pull real_context would improve accuracy for external models; the internal proxy models have no observed context-window data in any OmniRoute table.

Fact: The models.dev Model Database sync populates model_capabilities with real context windows

  • Status: Confirmed
  • Evidence: OmniRoute admin UI "Model Database" section ("Auto-sync pricing, capabilities & specs from models.dev"); after a manual sync, model_capabilities went from 0 to 7,278 rows, all with limit_context populated. The internal proxy models each appear under provider opencode (matching call_logs): big-pickle 200,000, mimo-v2.5-free 200,000, deepseek-v4-flash-free 200,000, deepseek-v4-flash 1,000,000. Provider+model join returns 1,709 matched call rows; 384 distinct resolved models map to a row.
  • Observed on: 2026-08-04
  • Confidence: High
  • Notes: This supersedes the gap recorded in the model_context_overrides fact — internal proxy models now have observed context-window data. For the common auto/cheap case the value (200,000) matches Claude Code's payload guess; the divergence shows for models like deepseek-v4-flash (1,000,000). model_context_overrides is unchanged (424 rows): the sync writes to model_capabilities, not overrides.

Fact: V2 mapping carries contextWindows; the statusline prefers the resolved model's real window

  • Status: Confirmed
  • Evidence: Scripts/update-model-provider-mapping.js LEFT JOINs model_capabilities on model_id and writes MAX(limit_context) under a sibling contextWindows block; Statusline/lib/mapping.js resolveContextWindow reads it; formatContext(ctx, overrideWindow) prefers a positive override over the payload. Regenerated mapping: 8 → 12 models, all 12 with contextWindows (e.g. mistral-large-latest 262,144; deepseek-v4-flash 1,000,000), no existing provider mapping changed. Suite 50/50.
  • Observed on: 2026-08-04
  • Confidence: High
  • Notes: The block is optional — old mappings without it degrade to the payload fallback (provider resolution is untouched). The V2 generation path was fixed in commit ca4a9ec after deployment verification caught that aggregate() dropped the ctx column (see the findings doc Part 4).

External research claims awaiting verification

The Perplexity responses may contain useful claims about Claude Code, OmniRoute, statuslines, source files, routing, or endpoints. Those claims must remain in Research/ until they are independently assessed.

Do not convert a claim into a confirmed fact merely because it appears in an external response.

Open questions

  • Does a management token exist in server.env or the OmniRoute admin UI that could unlock the HTTP API path? (Explicitly out of scope for the approved architecture; the design treats the management API as unavailable.)
  • Is the opencode vs opencode-zen duplication in model_capabilities a stable convention (same model mirrored under multiple aggregator keys), and which key should the mapping script prefer?
  • Does the OmniRoute Model Database sync run on a schedule or only manually? (Relevant to mapping-file freshness.)
  • Does model_capabilities.limit_context reflect the model's native window or a provider-capped window?
  • Is the provider key mismatch (nvidia in call_logs vs openrouter in model_context_overrides) a permanent naming convention, or will OmniRoute reconcile them?

Resolved questions (answered by Phase 1B findings)

  • Which exact fields does Claude Code provide to the statusline? → Documented schema confirmed; model.id and model.display_name are the only model fields; no resolved-provider field exists.
  • Is the resolved provider/model available in the statusline input? → Not in the payload; available via the transcript (model string) and SQLite call_logs (model + provider).
  • Why does the statusline show auto? → It is the configured requested model (ANTHROPIC_MODEL), not a bug.
  • Which local boundary exposes the resolved route? → SQLite call_logs via Docker volume; transcript JSONL per-assistant-message model field.
  • Is a local integration read-only feasible? → Yes: both SQLite (docker exec / docker run read-only) and transcript (JSONL read) are read-only paths.
  • Can the integration be read-only? → Yes; no live inference or config changes required for either path.

Resolved questions (answered by Phase 2 findings, 2026-08-02)

  • How stable is the resolved model label across requests? → Not stable; auto/cheap resolves to 20+ provider/model combos with a large failure tail. The statusline displays the most recent route, re-read on each event.
  • Can the transcript model field be relied upon to always reflect the resolved model? → Yes for the observed providers: present on 78/78 assistant messages, values match call_logs.model. The field reflects the model the API response used; no separate provider field exists.
  • What is the latency difference between transcript and SQLite reads? → Transcript sub-millisecond; SQLite query <10 ms but Docker startup 0.21–1.13 s, over the 300 ms budget unless a pre-baked sqlite3 image is used.
  • What is the safest fallback when SQLite or the transcript is unavailable or stale? → Defined fallback chain (ADR-0005/0006): requested-model from the payload until the first assistant message; model-only display when provider enrichment fails; stale marker past the threshold.
  • Which data may be displayed locally without exposing secrets? → Metadata only (model, provider, combo, status, timestamp); never request bodies, server.env, or key tables.

Resolved questions (answered by the approved Phase 3 design, 2026-08-02)

  • What staleness threshold is acceptable for an interactive statusline? → 5 minutes by default (configurable constant); a [stale] marker is appended past the threshold.
  • How is the empirical model→provider mapping stored and refreshed? → Versioned JSON at ~/.claude/statusline-model-provider.json, refreshed out-of-band by a Scripts/ maintenance script, never at statusline read time.
  • Which provider source does the design use? → The empirical mapping is the default; SQLite enrichment is a documented optional path (off by default). A missing provider degrades to model-only.
  • Is the transcript tail window a fixed architectural size? → No; the architecture requires only a bounded tail read, and the concrete window size is an implementation constant (currently 256 KB) — approved change request in ADR-0009.

Resolved questions (answered by Phase 4/5 validation, 2026-08-03)

  • Does the [stale] marker clear at the right time? → Yes. Staleness keys to the last assistant message's timestamp; transcript entries stream in at generation start, so the marker clears as soon as a new turn begins processing — before response completion. This matches the approved design (ADR-0009); not a bug.
  • Does the statusline track per-session route changes? → Yes. Live model-switch validated: a fresh light session showed opencode/mimo-v2.5-free while a heavy session kept opencode/big-pickle; each session's statusline showed its own resolved route.
  • Does the statusline stay bounded across terminal widths? → Yes. Manual resize test: single line, no wrap, no prompt overlap, readable.
  • Is the OmniRoute-unavailable scenario a required induced failure? → No; opportunistic only. The statusline has no runtime coupling to OmniRoute, and the statusline-visible effect (no new assistant messages → [stale]) is identical to idle and already validated.
  • Is the statusline resilient to the safety-classifier gating seen in Phase 1B? → Yes. The 2026-08-02 classifier outage did not affect the statusline; its route path uses only plain fs reads (contract case 16).
  • Does the deployed statusline still resolve a real route after the Phase 4 install? → Yes. Live smoke returned opencode/big-pickle from the deployed binary with real transcript + mapping, matching call_logs.

Resolved questions (answered by context window investigation, 2026-08-04)

  • Is the "200K ctx" hardcoded in the statusline? → No. formatContext reads ctx.context_window_size from the Claude Code stdin payload. No 200000 literal exists in the statusline source; the window value is whatever Claude Code sends.
  • Is the context window data from OmniRoute, like the provider? → No. The provider is resolved from the transcript + mapping (OmniRoute-observed data). The context window comes from Claude Code's own payload — it reflects the requested model's window, not the resolved model's window. This is the same "requested vs resolved" divergence documented for model names.
  • Does OmniRoute record the real context window anywhere? → Yes: model_context_overrides.real_context (424 rows, all auto:discovery), but coverage is uneven — openrouter (362), gemini (47), groq (15); no entries for opencode, nvidia, local, or anthropic as used in the mapping file. Only 62 resolved calls have a direct model_id match.
  • Is a provider-hue statusline styling feasible without changing the provider-aware logic? → Yes. A presentation layer (lib/style.js) applies ANSI decoration at render time only; all segment-content functions and their tests are unchanged. This architecture was implemented on feature/statusline-polish (commit c0792f9) and passes all35 tests.
  • Could model_capabilities be populated, and by what process? → Yes. The OmniRoute admin "Model Database" section ("Auto-sync pricing, capabilities & specs from models.dev") populates it. After a manual sync: 7,278 rows, all with limit_context. (Recorded 2026-08-04.)
  • Are there OmniRoute tables that record context-window data for the internal proxy models (big-pickle, mimo-v2.5-free, deepseek-v4-flash-free)? → Yes — model_capabilities, populated by the models.dev sync. All three have limit_context = 200000 under provider opencode (matching call_logs); deepseek-v4-flash is 1,000,000. (Recorded 2026-08-04.)

Research sources

Add source entries only when they are tied to a research record or finding:

### <source title>

- URL: <url>
- Used by: `Research/<filename>.md`
- Accessed: <date>
- Relevance: <one or two sentences>
- Verification status: Unassessed | Partially assessed | Verified locally | Not applicable

The bibliography itself belongs in References/; this section is an index of sources that materially support the knowledge base.

Promotion rules

  1. Preserve the original external claim in Research/.
  2. Record local observations and supporting evidence in Findings/.
  3. State the conclusion, scope, confidence, and remaining limitations.
  4. Add the conclusion here only after review.
  5. Update Architecture.md or Decisions.md when the conclusion changes design or project direction.