Project Retrospective — V1.0¶
Date: 2026-08-03
Release: v1.0.0 (tag 4fff4ba, commit d8b95e6)
Project objective¶
Build a provider-aware Claude Code statusline from observed local behavior, not
assumptions: show the actual provider/model a request resolved to (e.g.
opencode/big-pickle) instead of the requested label (auto/cheap), while never
displaying a value the statusline cannot source.
Major milestones¶
| Milestone | Date | Result |
|---|---|---|
| Phase 1A — Internet research | 2026-08-01 | Raw research archived with provenance |
| Phase 1B — Local investigation | 2026-08-01/02 | Payload + OmniRoute topology + transcript facts verified |
| Phase 2 — Architecture | 2026-08-02 | Transcript-first design, ADR-0004–0008 |
| Phase 3 — Design | 2026-08-02 | 6 deliverables, 16 acceptance cases, ADR-0009 |
| Phase 4 — Implementation | 2026-08-03 | Modular statusline, 29/29 tests, deployed live (5c0973b) |
| Phase 5 — Validation | 2026-08-03 | Live route + model-switch + terminal-width validated |
| Phase 6 — Documentation & maintenance | 2026-08-03 | README, ADR-0010, ops notes, maintenance plan |
| V1.0 release | 2026-08-03 | Merged to main, tagged v1.0.0 |
Key architectural decisions¶
- Transcript-first route read (ADR-0004/0005): read the resolved model from the local session transcript (sub-millisecond, no Docker dependency) rather than querying OmniRoute SQLite at statusline time. Provider enrichment is off by default.
- Empirical model→provider mapping (ADR-0006): a versioned JSON heuristic derived from
call_logs, refreshed out-of-band by a maintenance script — never at statusline read time. - Read-only at runtime (ADR-0005/0008): the statusline only reads files; it never writes, never calls OmniRoute, never makes network requests. OmniRoute-down is therefore non-blocking (ADR-0010).
- Bounded tail read (ADR-0009 change request): the transcript read is bounded; the window size is an implementation constant (256 KB), not an architectural invariant.
- Failure invariant: every failure path degrades toward the requested label or empty
string; no error,
undefined, ornullever reaches output. - 5-minute staleness +
[stale]marker, and a 32-char route bound — both tunable constants.
Biggest discoveries¶
- The statusline payload carries the requested model, not the resolved route.
model.display_nameisauto/cheapeven when the request resolved elsewhere. - The transcript carries the resolved model on every assistant message
(
message.model), matchingcall_logs.model(78/78 in Phase 2) — a reliable, local, per-session source. The field drifted from top-level tomessage.modelbetween phases (schema/version drift). combo_nameis the reliable filter for auto-routed traffic incall_logs;requested_model='auto/cheap'only matches blocked/classifier-gated rows.- Auto-routing is request-weight dependent: a light session resolved to
opencode/mimo-v2.5-freewhile a heavy session stayedopencode/big-pickle. - Assistant messages stream into the transcript progressively with write-time
timestamps (thinking → text → tool_use as separate flushed lines). This is why the
[stale]marker clears at processing start — correct per design, not a bug. - The
auto/cheapsafety classifier gates non-allowlisted Bash in Claude Code, and OmniRoute runs its own classifier probes (the499rows). The statusline's route path uses only plainfsreads, so it is immune.
What worked well¶
- Investigate-first discipline: the engineering principle held — no implementation until data flows, interfaces, and failure modes were locally verified.
- Gate-based roadmap: explicit phase gates with ADRs meant each phase was approved on evidence before code moved forward.
- Read-only verification: Phase 2 proved the transcript was the right source before a line of implementation was written.
- Contract test suite: 29 tests encoding all 16 acceptance cases + invariants caught regressions and made "failure behavior is deliberate" testable.
- Worktree isolation: the implementation ran in a dedicated worktree, keeping the user's working copy untouched until release.
- Graceful degradation as a product value: the requested-label fallback and
[stale]marker turned every failure mode into a readable state instead of a crash.
What we'd do differently next time¶
- Provision the Bash allowlist earlier. The
auto/cheapclassifier outage stalled deployment work; a pre-authorized allowlist for the few commands the project needs would have saved wall-clock time. - Capture the light-vs-heavy routing comparison sooner. It was a live, cheap observation that materially strengthened validation; worth scheduling during Phase 5 rather than discovering it opportunistically.
- Consider pinning the OmniRoute image tag. The image is
latest(unpinned); the transcript schema drift we hit suggests pinning would reduce future surprises.
Lessons learned¶
- Verify local behavior before trusting documentation — the resolved model is not in the statusline payload, and the transcript field moved between versions.
- Heuristic data should degrade gracefully: a missing mapping entry renders model-only, never a guess.
- A read-only runtime dependency (local files only) is a strong safety and availability property — it made OmniRoute-down and classifier-down scenarios non-events.
- "Most recent route" is the honest contract: the statusline shows the last response's resolution, re-read per event, not a stable per-session history.
- The empirical mapping is a snapshot, not a contract (ADR-0006) — plan for refresh.
- Test both the producer and the consumer of a generated artifact. A passing consumer
test does not prove the producer generated the expected artifact — V2's runtime was
fully tested and correct, but the regeneration script dropped the context-window
column, and the feature silently no-op'd until the producer was fixed and covered
(commit
ca4a9ec; findings doc Part 4).
Why V1.0 is considered complete¶
- Phases 1–6 all closed with recorded gates (ADR-0003, 0004–0008, 0009, 0010).
- The implementation is deployed and live: 29/29 contract tests pass; real transcripts
and the generated mapping produce
opencode/big-pickle; failure paths degrade correctly. - Validation covered 6 of 7 roadmap scenarios live; the 7th (OmniRoute unavailable) is accepted as an opportunistic observation because the statusline has no coupling to OmniRoute (ADR-0010).
- Documentation is complete: README, repository map, ADRs, operational notes, troubleshooting, and a maintenance plan.
Suggested starting point for V2 (visual statusline improvements)¶
Treat V2 as a separate project on the V1.0 baseline (ADR-0010). Starting points:
- Extension surface:
Statusline/lib/— the progress bar and context-display replacement are new segments, so the route core (transcript.js,mapping.js,route.js,staleness.js) should stay intact; add new lib modules and wire them inStatusline/statusline.js. - Start with a design update: revise
Statusline/Design/01-display-requirements.md(line structure) and06-test-cases-acceptance.mdbefore code — the existing contract suite is the regression net. - Config:
Statusline/lib/config.jsholds the tunable constants (staleness, truncation); visual limits belong there. - Re-deploy:
Scripts/deploy-statusline.jsand the mapping maintenance script are reusable as-is. - Baseline guard:
main+v1.0.0are the stable release; branch V2 offmain.