Phase 4 Implementation Plan — Approval Record¶
- Date: 2026-08-02
- Status: Approved (2026-08-02, by repository owner, no ADR — this is implementation of approved architecture, not a new architectural decision)
- Based on: the approved Phase 3 design (
Statusline/Design/01–06,07-design-handoff.md) and the approved architecture (Architecture.md, ADR-0005–0009)
Scope¶
Implement the approved statusline design as a zero-dependency modular Node.js application with a contract test suite covering all16 acceptance cases from Statusline/Design/06-test-cases-acceptance.md. SQLite enrichment at read time is explicitly out of scope (off by default per ADR-0009).
Change requests incorporated¶
The following change requests were made during the approval review and are incorporated into the implementation:
-
Single named constant for tail window. All implementation constants (transcript tail window, staleness threshold, truncation max, read timeouts) are defined as named constants in a single
lib/config.jsmodule — no hardcoded values scattered across files. -
Modular implementation. The CLI entry point (
statusline.js) is small. Parsing, transcript handling, mapping, staleness, route composition, and formatting are implemented as separatelib/modules, each independently testable. -
Mapping is a read-only runtime dependency. The statusline reads
~/.claude/statusline-model-provider.jsonat runtime. It never regenerates or modifies this file. Generation is an explicit user-initiated action viaScripts/update-model-provider-mapping.js. -
Failure invariant: degrade toward requested model. Every failure path degrades toward the requested-model display (
model.display_name || model.idfrom the payload). No error, exception,undefined, ornullappears in output. A catch-all in the CLI entry enforces this invariant. -
No ADR-0010. This plan is recorded in
Findings/only. NoDecisions.mdchange — this is implementation of an already-approved architecture, not a new architectural decision.
Deliverables¶
| File | Purpose |
|---|---|
Statusline/statusline.js |
CLI entry point (small; reads stdin, wires modules, writes single output line) |
Statusline/lib/config.js |
Named constants: TRANSCRIPT_TAIL_WINDOW_BYTES, STALENESS_THRESHOLD_MS, ROUTE_MAX_CHARS, read timeouts, mapping file path |
Statusline/lib/timeout.js |
withTimeout(promise, ms) — Promise.race hard timeout |
Statusline/lib/parse-stdin.js |
parseStdin(raw) — JSON parse with null-on-failure |
Statusline/lib/transcript.js |
readTranscriptTail(path, windowBytes) + findLastAssistantModel(tailText) — bounded read + backward line walk |
Statusline/lib/mapping.js |
loadMapping(path, timeoutMs) + resolveProvider(model, mapping) — read-only; never writes |
Statusline/lib/staleness.js |
isStale(timestamp, nowMs, thresholdMs) — ISO timestamp comparison |
Statusline/lib/route.js |
requestedLabel(payload), truncateRoute(provider, model, maxChars), composeRoute(...) |
Statusline/lib/format.js |
formatBranch(cwd), formatContext(ctx) — preserved baseline |
Statusline/test/statusline.test.js |
Contract tests for all16 cases + invariants |
Statusline/test/fixtures/transcripts/*.jsonl |
Transcript fixtures for each case |
Scripts/update-model-provider-mapping.js |
Maintenance: aggregate call_logs, write mapping atomically |
Statusline/README.md |
Usage and configuration docs |
Deployment¶
- Backup current
~/.claude/statusline.js→~/.claude/statusline.js.pre-phase4.<timestamp> - Install
statusline.js+lib/to~/.claude/statusline/(self-contained; works without the repo) - No
settings.jsonchange required (the wrapper at~/.claude/statusline.jskeeps the command path unchanged) - Generate initial mapping via
Scripts/update-model-provider-mapping.js