Skip to content

Phase 1B — Statusline Input Contract

  • Date: 2026-08-02
  • Workstream: A (Claude Code boundary — statusline invocation and stdin payload)

Statusline command configuration

Verified fact: The active statusline is configured in ~/.claude/settings.json (user-level):

{
  "statusLine": {
    "type": "command",
    "command": "node ~/.claude/statusline.js",
    "padding": 0
  }
}

No statusLine override exists in the project settings (.claude/settings.local.json).

Current statusline script (~/.claude/statusline.js)

The script is a Node.js reader that parses the stdin JSON and renders:

Display element Source field
Model name model.display_name \|\| model.id
Git branch git symbolic-ref against cwd
Context usage % context_window.used_percentage
Context window size context_window.context_window_size
Token usage (last call) context_window.current_usage.input_tokens / .output_tokens

Verified fact: The script uses model.display_name as its primary model label. In this installation that field equals auto/cheap (the configured ANTHROPIC_MODEL), so the statusline currently displays auto/cheap — the requested model, not the resolved provider/model.

Observation: 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.

Transcript reveals the resolved model

The most recent session transcript (abd68897-8f5d-47ca-96b5-815f75779afc.jsonl, 42,650 tokens) was inspected for model metadata (message content was not extracted).

Verified fact: Every type: "assistant" message in the transcript carries a model field containing the resolved model that actually handled the request, because OmniRoute returns it in the API response's model field. Observed values:

Resolved model Occurrences
deepseek-v4-flash 52
mimo-v2.5-free 43
sonnet 4
gemini-3-flash-preview 2

Verified fact: The transcript also contains the value auto (7 occurrences), appearing in combo/step identifiers — not as a model response value. No standalone provider, resolved_provider, or display_name field exists in the transcript JSON structure; the resolved provider is embedded only in the model string returned by the API.

Verified fact: The display_name field does not appear anywhere in the transcript JSONL. Claude Code's statusline payload schema (model.display_name) is populated from a different source than the per-message API response model.

Key divergence: statusline payload vs transcript

Source Model label shown What it represents
Statusline payload (model.display_name) auto/cheap The requested model (what the client configured)
Transcript (per-assistant-message model) deepseek-v4-flash, mimo-v2.5-free, etc. The resolved model (what OmniRoute actually routed to)

Verified fact: This divergence exists because the statusline payload reflects the model Claude Code requests, while the transcript records the model OmniRoute returns in each API response. OmniRoute does echo the resolved model back in the response metadata — Claude Code stores it in the transcript, but the statusline contract does not surface it.

Observed vs documented schema

From the session transcript, the following documented statusline fields were observed or confirmed absent:

Field Status Notes
model.id Present Set to auto/cheap
model.display_name Present Set to auto/cheap (same as id in this config)
cwd Present Working directory path
workspace.current_dir Present Same as cwd
context_window.* Present Populated after first API call
cost.* Present Session cost/duration
session_id Present UUID for current session
transcript_path Present Path to the JSONL transcript file
version Present Claude Code version
effort.level Present (when active) Current reasoning effort
fast_mode Present Boolean
rate_limits.* Present (for subscribers) 5h/7d usage
session_name Conditional Only after AI-generated title exists
prompt_id Conditional Only after first user input
vim.mode Conditional Only when vim mode enabled
pr.* Conditional Only when open PR exists
worktree.* Conditional Only during --worktree sessions
agent.name Conditional Only when --agent flag used
Any resolved-provider field Absent Not in documented schema; not in observed data

Key findings

Finding Tag Evidence
The statusline payload carries the requested model (auto/cheap), not the resolved route Verified fact model.display_name in the payload equals the configured ANTHROPIC_MODEL; script renders this value directly
The transcript stores the resolved model per assistant message, because OmniRoute echoes it in the API response's model field Verified fact 101 assistant messages carry resolved model names; no provider field exists alongside
There is no statusline payload field for resolved provider/model — this is a documented gap, not a bug Verified fact Full schema inspected; model.id/model.display_name are the only model fields
A statusline can read the transcript's last assistant message model field to obtain the resolved model Observation Transcript is JSONL at a known path (transcript_path); last line is low-latency to read; no auth required
The current statusline script does not use the transcript as a data source Observation Script source inspected; only reads stdin JSON payload
Session-level correlation from OmniRoute call_logs is not functional (session_tag is NULL on all rows) Observation SQLite inspection (Workstream B) — cross-referenced

Implications for statusline design

Verified fact: Two viable read-only paths to the resolved model exist:

  1. SQLite path (via Docker volume): call_logs.model + call_logs.provider — resolved per request, no auth needed, low-latency. Requires Docker permissions.
  2. Transcript path (via JSONL): last assistant message model field — resolved per message, no auth/Docker needed. Requires JSONL parsing.

Both are read-only and do not route live traffic. The transcript path is simpler (no Docker dependency) but provides only the model string, not the separate provider name. The SQLite path provides both model and provider separately.

Open questions

  • Does the transcript model field always reflect the resolved model, or only when the provider echoes it? (Likely depends on OmniRoute's response construction; current data confirms it for the providers observed.)
  • Can the transcript model field reliably distinguish between providers (e.g., deepseek-v4-flash vs mimo-v2.5-free), or is it sometimes the requested alias?
  • What is the latency of reading the last line of the transcript JSONL vs querying SQLite?