Skip to content

Context Window Data Source Investigation and Statusline Styling

Date: 2026-08-04 Branch: feature/statusline-polish Session: Post-V1.0 statusline polish and context window trace


Part 1: Statusline Provider-Hue Styling (implementation)

What changed

A presentation layer was added to the statusline to apply per-provider color and usage-health coloring. The existing provider-aware logic (composeRoute, resolveProvider, truncateRoute) was not modified — color is applied at render time only, after the segment-content functions return plain text.

Architecture

  • New file: Statusline/lib/style.js — ANSI decoration layer, called only from render() in statusline.js.
  • Config: Statusline/lib/config.js gained a STYLE block (provider hue map, usage-health thresholds, segment weights) and a shared STALE_MARKER constant.
  • route.js: Changed one literal (" [stale]"STALE_MARKER) — same string, no behavioral change.
  • statusline.js: render() derives the provider from resolveProvider(transcriptInfo.model, mapping) and passes the plain route string + provider to style.route().
  • Tests: 35 tests all pass; width-bound assertion updated to strip ANSI before measuring;7 style-layer unit tests added.

Design principle

The presentation layer is additive decoration. Every segment-content function (composeRoute, formatContext, truncateRoute) still returns plain text. This preserves byte-for-byte test assertions on the provider-aware logic while allowing styling to be tuned or disabled (NO_COLOR) without touching content.

Color scheme deployed

Segment Color
Provider prefix (known) Dim + provider hue (opencode=blue, nvidia=cyan, gemini=magenta, local=green)
Provider prefix (unknown) Dim
Model Bold
Git branch Green
% used (context) Green <50%, yellow <80%, red ≥80%
N ctx Dim
Token deltas (+in/−out) Dim
[stale] marker Yellow

Test artifact

Commit: c0792f9 on feature/statusline-polish.


Part 2: Context Window Data Source Investigation (research)

Question investigated

Is the "200K ctx" shown by the statusline hardcoded, or is it pulled from an observed source (like the provider)? If not from an observed source, is such a source available in OmniRoute?

Finding 1: "200K ctx" comes from Claude Code's payload, not OmniRoute

Status: Verified fact Confidence: High Evidence: Statusline/lib/format.js:26 reads ctx.context_window_size from the stdin payload. The payload is generated by Claude Code (Phase 1B input contract). No hardcoded 200000 exists anywhere in the Statusline source. The input-contract finding confirms: "The script contains no fallback to read OmniRoute data, the transcript, or any external source. It renders only what Claude Code provides in the statusline payload."

Implication: The context window reflects what Claude Code believes the model's window is (based on the requested model, e.g. auto/cheap), not necessarily the true window of the model OmniRoute actually resolved. This is the same "requested vs resolved" divergence documented for model names.

Finding 2: call_logs does not record context window

Status: Verified fact Confidence: High Evidence: call_logs schema has model, provider, requested_model, token counts (tokens_in, tokens_out, tokens_cache_read, etc.), but no context_window, max_tokens, or window_size column. The table records usage, not the model's capacity.

Finding 3: OmniRoute's model_capabilities table is empty

Status: Verified fact Confidence: High Evidence: SELECT COUNT(*) FROM model_capabilities returns0 rows. The table exists (schema includes limit_context, limit_input, limit_output) but is unpopulated in this installation.

Finding 4: model_context_overrides has real context windows, with uneven coverage

Status: Verified fact Confidence: High Evidence: SELECT COUNT(*) FROM model_context_overrides returns 424 rows. Table schema:

CREATE TABLE model_context_overrides (
  provider TEXT NOT NULL,
  model_id TEXT NOT NULL,
  real_context INTEGER NOT NULL,          -- the corrected context window (tokens)
  source TEXT NOT NULL DEFAULT 'manual',  -- 'manual' | 'auto:discovery'
  refreshed_at TEXT NOT NULL,
  PRIMARY KEY (provider, model_id)
);
  • All 424 entries have source = 'auto:discovery'.
  • Providers with entries: openrouter (362), gemini (47), groq (15).
  • Providers with no entries: opencode, nvidia, local, anthropic, sambanova, deepseek, etc.

Finding 5: Only 62 resolved calls have a direct model_id match to the overrides

Status: Verified fact Confidence: High Evidence: SELECT COUNT(*) FROM call_logs c JOIN model_context_overrides o ON c.model = o.model_id WHERE c.status=200 AND c.model IS NOT NULL returns 62.

These are predominantly openrouter models (nvidia/nemotron, gemini, cohere, etc.). The internal proxy models (big-pickle, mimo-v2.5-free, deepseek-v4-flash-free — the ones most commonly seen in this setup) have no entries in model_context_overrides.

Finding 6: Provider key mismatch between call_logs and model_context_overrides

Status: Verified fact Confidence: High Evidence: call_logs records provider = "nvidia" for nvidia/nemotron-3-super-120b-a12b; model_context_overrides stores the same model under provider = "openrouter". A direct (provider, model_id) join fails; a model_id-only join works for the 62 matches but is fragile (4 models appear under multiple providers, though with identical real_context values in observed data).

Summary: where context-window data exists

Source Has real data? Covers the common models? Latency
Payload context_window_size No (Claude Code's guess for the requested model) Always present Free (already in payload)
call_logs No (records usage, not the window) N/A ~0.25–1.13s via Docker
model_context_overrides.real_context Yes (424 models, auto-discovered) Only for openrouter/gemini/groq models; no coverage for internal proxy models Fast (SQLite read)
model_capabilities Schema exists (limit_context) but unpopulated (0 rows) N/A N/A

Implication for V2

To show the resolved model's real context window (instead of Claude Code's guess), the statusline would need to:

  1. Extend the update-model-provider-mapping.js script to also query model_context_overrides.real_context for matching model IDs.
  2. Add real_context to the mapping file JSON.
  3. Fall back to the payload's context_window_size when no match exists.

Coverage would be good for external models (nvidia/nemotron, gemini, claude-on-openrouter) but absent for the internal proxy models (big-pickle, mimo-v2.5-free). This is an honest limitation, not a bug — the data simply does not exist for those models in OmniRoute's current tables.


Part 3: Post-sync verification — Model Database sync from models.dev (same day)

After Part 2 was recorded, the OmniRoute admin UI's Model Database section ("Auto-sync pricing, capabilities & specs from models.dev") was manually synced. Re-verification found this populates the previously-empty model_capabilities table.

Finding 7: model_capabilities went from 0 to 7,278 rows, all with limit_context

  • Status: Verified fact
  • Confidence: High
  • Evidence: SELECT COUNT(*) FROM model_capabilities → 7,278 (was 0 before the sync). SELECT COUNT(*) FROM model_capabilities WHERE limit_context IS NOT NULL → 7,278 (100%). Schema unchanged (limit_context, limit_input, limit_output).
  • Provider distribution (top rows): nano-gpt (615), openrouter (368), kc/kilo-gateway/kilocode (344 each), vercel (315), llmgateway (189), merge-gateway (152), poe (137), zenmux (120), amazon-bedrock (116), novita-ai (107), nvidia (98), and many more aggregators.

Finding 8: The internal proxy models now have real context windows

  • Status: Verified fact
  • Confidence: High
  • Evidence: model_capabilities rows for the models most commonly resolved in call_logs:
  • big-picklelimit_context 200,000 (providers: opencode, opencode-zen)
  • mimo-v2.5-free → 200,000 (opencode, opencode-zen)
  • deepseek-v4-flash-free → 200,000 (opencode, opencode-zen)
  • deepseek-v4-flash → 1,000,000 (opencode, opencode-zen)
  • This directly resolves the Part 2 gap: internal proxy models now have observed context-window data in OmniRoute.

Finding 9: Provider keys align between call_logs and model_capabilities for the common models

  • Status: Verified fact
  • Confidence: High
  • Evidence: provider+model join returns 1,709 matched call rows (JOIN ... ON l.model = c.model_id AND l.provider = c.provider WHERE l.status = 200). call_logs records provider = 'opencode' for big-pickle/mimo/deepseek-flash; model_capabilities also stores them under opencode (plus a separate opencode-zen mirror with identical values). The provider-key mismatch that affects model_context_overrides (nvidia vs openrouter) does not affect this table for the observed models.
  • Note: table-specific observation; the model_context_overrides mismatch remains unresolved.

Finding 10: model_context_overrides is unchanged by the sync

  • Status: Verified fact
  • Confidence: High
  • Evidence: still 424 rows after the sync. The Model Database sync writes to model_capabilities, not to model_context_overrides.

Coverage summary after sync

  • 384 distinct successfully-resolved model IDs map to a model_capabilities row.
  • 1,709 resolved calls match on (provider, model).
  • For the common internal proxy models, limit_context = 200,000 — matching what Claude Code already reports for the requested auto/cheap (payload context_window_size). The divergence is only visible for models like deepseek-v4-flash (real 1,000,000) and nvidia/nemotron (real 1,000,000), where Claude Code's payload guess understates the real window.

Implication for V2 (updated)

model_capabilities.limit_context, joined on (provider, model_id), is now the preferred observed source for the resolved model's context window (superseding the model_context_overrides path). V2 design should:

  1. Extend Scripts/update-model-provider-mapping.js to also query model_capabilities.limit_context (and optionally limit_input / limit_output).
  2. Add a contextWindow (or similar) field to the mapping file JSON.
  3. Have the statusline fall back to the payload's context_window_size when no match exists.
  4. Use opencode (not opencode-zen) as the canonical provider key for the internal proxies, matching call_logs.

Open questions carried forward (updated)

Resolved by the sync: - ~~Are there other OmniRoute tables that record model capacity for the internal proxy models?~~ → Yes: model_capabilities, populated by the models.dev sync. - ~~Could the model_capabilities table be populated, and by what process?~~ → Yes: the admin "Model Database" sync populates it (7,278 rows).

Remaining: 1. Is the opencode vs opencode-zen duplication a stable convention (same model mirrored under multiple aggregator keys), and which key should the mapping script prefer? 2. Does the Model Database sync run on a schedule or only manually? (Relevant to mapping-file freshness.) 3. Does limit_context reflect the model's native window or a provider-capped window? 4. Is the model_context_overrides provider-key mismatch (nvidia in call_logs vs openrouter in overrides) a permanent naming convention, or will a future OmniRoute version reconcile them?


Part 4: V2 — real context window from model_capabilities (implementation, deployment, and the artifact defect)

Date: 2026-08-04 (same session) Branch: feature/statusline-polish Commits: e3c03c5 (V2 implementation), ca4a9ec (generation fix + regression tests)

Implementation summary

V2 makes the context-window segment show the resolved model's real window from model_capabilities.limit_context instead of Claude Code's payload guess — the path outlined in "Implication for V2 (updated)" above, with these decisions fixed at the planning gate:

  • Schema shape (a) — a sibling top-level block, never a change to the models shape (see Statusline/Design/03-provider-model-naming.md):
{
  "version": 1,
  "generated_at": "2026-08-04T00:00:00Z",
  "models": { "big-pickle": "opencode" },
  "contextWindows": { "big-pickle": 200000 }
}
  • Join key: model_id only (not (provider, model_id)), matching the internal proxy models and sidestepping the nvidia/openrouter provider-key mismatch; mirror duplicates are collapsed with MAX(limit_context).
  • Scope: limit_context only (limit_input/limit_output deferred — no consumer).
File Change
Scripts/update-model-provider-mapping.js SQL gains LEFT JOIN model_capabilities c ON l.model = c.model_id and MAX(c.limit_context) AS ctx; aggregate() threads ctx into a contextWindows block
Statusline/lib/mapping.js new resolveContextWindow(model, mapping) accessor; resolveProvider byte-for-byte unchanged
Statusline/lib/format.js formatContext(ctx, overrideWindow) — a positive override wins over ctx.context_window_size
Statusline/statusline.js render() resolves the window alongside the provider and passes it as the override
Statusline/test/statusline.test.js 11 V2 runtime tests (accessor + override/fallback)

Fallback chain: mapping contextWindows match → payload context_window_size → segment omitted (unchanged). 45/45 tests passed at commit e3c03c5.

Deployment outcome

  • Deployed V2 to ~/.claude/statusline/ via Scripts/deploy-statusline.js (V1 backed up as ~/.claude/statusline.js.pre-phase4.*).
  • Regenerated the mapping via Scripts/update-model-provider-mapping.js: 8 → 12 models, all 12 with contextWindows; no existing provider mapping changed (the earlier mistral-large-latest model-only observation was a stale-mapping miss — the model had been routed since the previous regeneration, which predates it).
  • Live smoke through the deployed binary (NO_COLOR):
  • mistral-large-latest (mapped) → mistral/mistral-large-latest 262K ctx — real window (262,144), not the payload's 200,000.
  • some-unknown-model (unmapped) → model-only, 200K ctx — payload fallback intact.
  • big-pickle (mapped, pre-existing) → opencode/big-pickle 200K ctx — no provider-display regression.
  • Suite: 50/50 pass (45 + 5 pipeline regression tests).

Defect discovered during deployment verification

The first regeneration after deploy produced a mapping without contextWindows. V2's runtime logic (resolveContextWindow, the formatContext override) was correct and fully tested — but it always received null because the generated artifact never contained the block, so every model silently fell back to the payload guess. Until the mapping was correct, the feature was a silent no-op.

Root cause

aggregate() in the committed V2 script dropped the new column: - The SQL selected MAX(c.limit_context) AS ctx (4th column), but aggregate() parsed only [model, provider, nRaw] and the returned JSON had no contextWindows key. - The consumer side was unit-tested (45/45) but the producer side (SQL output → mapping JSON) had no coverage, so the defect passed the suite.

The fix (commit ca4a9ec)

  • aggregate() now parses the 4th field, builds a windows map (max positive ctx per model), and writes contextWindows when non-empty.
  • The block is omitted when nothing resolves, so old artifacts and empty outputs degrade to the payload fallback (schema stays backward-compatible).
  • main() is guarded by require.main === module and aggregate() is exported, so the generation pipeline is unit-testable without docker.

New regression tests

5 tests in Statusline/test/statusline.test.js pin the generated artifact, not just the runtime accessor: contextWindows written from sqlite output; missing/non-numeric ctx skipped; mirror duplicates keep the max ctx; block omitted when no model has ctx; provider selection unchanged. Suite 45 → 50, all passing.

Engineering lesson learned

When introducing a generated artifact, test both the producer and the consumer. A passing consumer test does not prove that the producer generated the expected artifact. The V2 runtime was fully tested and correct; the mapping the producer wrote was not, and the feature silently degraded until the producer was fixed and covered.