Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
# Changelog

## v0.40.0 — 2026-09-01 — what a cold cache costs

**The bar's extra color above 200k was a pricing claim, and the price is
gone.** Anthropic's pricing page: every 4.6+ model bills the full 1M window
at standard rates — a 900k request costs the same per token as a 9k one. The
"premium band" the README promised has not existed for any model you can
run. What is still true above 200k is exposure: step away past the cache
TTL and the whole prefix re-bills at the write rate, 20x a read on most
models and 80x on Fable 5.1, where reads dropped to 0.025x. The color
stays — yellow past 200k, red past 800k — and now means that: **rewrite
exposure**, sized at what a cold cache would re-cache.

**The cache badge now reads the CLI's ledger.** Claude Code 2.1.251 added a
`prompt_cache` object to the status line input: misses (its own definition,
the same >5% and >=2k thresholds this script inferred), expected rebuilds,
tokens re-cached, TTL and the exact expiry. It sees every request, including
the ones the 300ms render debounce skipped, so when it is present it replaces
the drop rule, the idle-expiry guess and the TTL default. Two things change
on screen: `≡!Nk` is sized at what the CLI's misses actually wrote back, and
a compaction rebuild — a rewrite the CLI expected — shows `≡~`, never `≡!`.
Same state file, new `pc_*` keys; older CLIs keep the inferred detector
untouched.

**Fable 5.1 shipped as `claude-fable-5-1` and the chip kept saying `fabl5`.**
The abbreviation predates Fable having a minor; the chip now carries it:
`fabl5.1`. And the `[1m]` tag follows the CLI's window in both directions —
`CLAUDE_CODE_DISABLE_1M_CONTEXT=1` makes the CLI report 200k on Fable (and,
since 2.1.223, compact there), so the tag drops with the bar instead of
painting `[1m]` over a 200k window. The name decides only when the CLI sent
no window at all. 486 bats.

## v0.39.0 — 2026-09-01 — a guess may not delete a window

**v0.38.0 unfolded the future and the dry wall walked out with it.** The
Expand Down
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ identity ───────────────────────
## Color: three lanes

```
status green / yellow / red pressure ONLY: quota, context, cache, premium band, effort
status green / yellow / red pressure ONLY: quota, context, cache, rewrite exposure, effort
identity magenta / cyan / blue model family (fable = bright red, its TUI color)
neutral grey / white / dim everything else; MAX bold, PRO normal
```
Expand Down
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,19 @@ Every component earns its place:
| Trace chip | `[http://localhost:9317/s/c3a6e0f3]` when the session's wire is being captured by [cctrace](https://github.com/thevibeworks/cctrace) (`deva --trace`, or `cctrace` directly) — a full URL so terminals linkify it, deep-linking `/s/<sid8>` (cctrace >= 0.40) straight to *this* session's conversation scrolled to the newest turn; without a session id the link falls back to the `/trace` live page. `DEVA_TRACE_UI_URL` (exported by deva on traced create/reattach) outranks the container-side port, since only the host side knows the published port. **Line 1 must fit the terminal** (Claude Code hands the script `COLUMNS` and truncates or wraps anything wider, which throws every anchored row beneath the wrong edge), so when the full URL would not fit, the chip collapses to `[cctrace]` carrying the same target as an OSC 8 hyperlink — 9 columns, still one click on iTerm2/kitty/WezTerm — before the path/stats gap gives. Session identity, so it sits on the left with path and branch, dim (red stays reserved for pressure). Detected from the trace env cctrace exports into the traced process (`CCTRACE_SERVER_PORT`), from the capture's CA plumbing (`NODE_EXTRA_CA_CERTS` under a cctrace dir), or from `DEVA_TRACE=1`; when only the plumbing is visible (older cctrace) the port resolves through cctrace's live-instance registry, matched by session id (sid8 prefix — the registry stores ids redacted), then project path, then by being the only live capture, with the fallbacks trusting heartbeat-fresh entries only. A traced session with no resolvable port still shows a bare `[cctrace]` — "recorded" matters even portless. |
| Activity | Session diff without opening git. |
| Time and cost | Track long sessions. Hours format above 60m (`1h30m`). |
| Model | Abbreviated: `claude-opus-4-8` becomes `opus4.8`, `claude-fable-5` becomes `fabl5`, `claude-sonnet-5` becomes `sonnet5`. The `[1m]` tag marks a 1M-context session, detected from the window the CLI reports (`context_window_size`) — not the name — so it shows even when Claude Code strips the `[1m]` suffix (which it does since 2.1.173 whenever 1M is the default; Sonnet 5 joined Fable 5 on that path in 2.1.197). |
| Model | Abbreviated: `claude-opus-4-8` becomes `opus4.8`, `claude-fable-5-1` becomes `fabl5.1` (`claude-fable-5` stays `fabl5`), `claude-sonnet-5` becomes `sonnet5`. The `[1m]` tag marks a 1M-context session, detected from the window the CLI reports (`context_window_size`) — not the name — so it shows even when Claude Code strips the `[1m]` suffix (which it does since 2.1.173 whenever 1M is the default; Sonnet 5 joined Fable 5 on that path in 2.1.197), and it drops when you turn 1M off (`CLAUDE_CODE_DISABLE_1M_CONTEXT=1` makes the CLI report 200k; the bar re-bases to 200k and the tag follows). |
| Effort | Compact lowercase badge: `lo` / `md` / `xh` / `max` / `ultra` / `auto` (`high` is the default and stays hidden). Dim for routine levels; `max` / `ultra` use the pressure color; `fast` shows in fast mode. |
| Context bar | Merged with model. Green / yellow / red by window pressure. On 1M models the bar also carries the premium input-pricing band: yellow past 200k tokens, red past 800k — the % alone looks calm (320k = 32%) while every request bills at the premium rate. A real 0% (e.g. right after `/compact` resets the window) renders a visibly empty bar `[░░░░░░0%]` — the snap to empty *is* the refresh signal; the bar hides only when no context data exists at all. Changes flash for ~60s in reverse video: `[█░░░░░30%]+9` while filling, `[░░░░░░3%]-27` right after a compaction. Cost flashes the same way, cents-precise: `$7.90+.37`. |
| Context bar | Merged with model. Green / yellow / red by window pressure. On 1M sessions the bar also carries **rewrite exposure** — what a cold cache re-bills: yellow past 200k tokens, red past 800k — the % alone looks calm (320k = 32%) while a dead cache rewrites all of it at the write rate. Long context itself is standard-priced on every 4.6+ model, so the color is about the rewrite, never a per-token premium. Sized from the CLI's `prompt_cache.recache_tokens_if_cold` on Claude Code 2.1.251+, else the live context. A real 0% (e.g. right after `/compact` resets the window) renders a visibly empty bar `[░░░░░░0%]` — the snap to empty *is* the refresh signal; the bar hides only when no context data exists at all. Changes flash for ~60s in reverse video: `[█░░░░░30%]+9` while filling, `[░░░░░░3%]-27` right after a compaction. Cost flashes the same way, cents-precise: `$7.90+.37`. |
| User tier | Neutral white-weight (MAX bold, PRO normal, dim otherwise) — identity, never a status color. Truncated display name. |
| Quota | Integer percentages. The 5h badge always carries its reset time while a window is live — `5h[42%@14:30]` reads "42% used, resets at 14:30" — because on a 5h horizon the reset is the number you plan the current sitting around. Wall-clock, not a countdown, on purpose: Claude Code only re-renders the statusline on activity, so a relative "@1h38m" silently decays into a lie during idle gaps, while "@14:30" stays true in a frozen frame. (The 7d badge is hybrid: day-relative `@5d` while the reset is >= 24h out — decays one day per day, mild and narrow — switching to the same wall-clock `@04:00` inside the last day, where an `@6h`/`@<1h` countdown decayed by the hour exactly when pressure keeps the suffix visible.) When a window's utilization climbs between renders, a reverse-video `+N` token appears right after the badge for ~60s: `5h[44%@14:32]+2` means "you just burned 2%". A drop (window reset) stays quiet — the fresh low number is its own signal. **7d is forecast, not leveled**: a learned per-weekday burn profile (EWMA over your own usage history) plus your recent 24h burn project whether the quota outlasts the window — your heavy Tuesday counts more than a generic average. The verdict is color alone; under pressure the badge shows when relief arrives: `7d[44%@5d]` red means "at your pace, dry days before the reset 5 days from now"; inside the last day it reads `7d[92%@04:00]` — resets at 04:00. Cold start (<14 days history) falls back to window-average pacing. Recovery color when reset is imminent. **Model-scoped weekly quota**: when the usage API carries a per-model weekly limit (`limits[]`, `kind=weekly_scoped`) for the model your session is running, it renders right after the model+context block — `fabl5[1m][12%] fb[67%]` on a Fable 5 session, `op[33%]` on Opus — because the quota is a property of the model you're running, not of the account-wide 5h/7d cluster. It's a weekly number (same reset as the 7d badge), scoped to one model. Other models' scoped quotas stay hidden: only the limit constraining *this* session is signal. Supersedes the legacy `seven_day_opus`/`seven_day_sonnet` fields, which the API now sends as null. |
| Extra usage | Monthly spend, limit, prepaid balance. `--extra auto` shows when quota runs out. |
| Week row | **A row of its own, under the badges**: `5h ▅█▄▮▯ 0.9✕ @23:00 7d ▅▂▃ ▄▅▁▄▅ …▮▯▯...▯(✕19) 0.7✕ @Wed 09:00` — this sitting hour by hour on a fixed five-slot axis (the hollow run is the hours left), the week as its 5h windows (day-gapped, the far future folded to a counted `...▯(✕19)`), height = what each cell burned, `▮` = now. `×` marks where the pool runs dry on the week; each strip ends with its pace and reset. Reconstructed from your own usage log; `auto` shows it once there is history to show. See [Week row](#week-row). |
| Deadman | **Invisible until a switch is armed.** Surfaces [deadman](https://github.com/thevibeworks/deadman) — a dead man's switch that hands the session off when you stop responding. `[☠ armed 42m]` (dim) counts down to the auto-handoff; `[☠ warned 3m]` (yellow) means the phone warning went out; `[☠ due]` means the handoff fires imminently. Sits on the left lane next to the path — it describes this session's lifecycle, not a quota. One `command -v` when the tool is absent, one fast file read when present; nothing armed renders nothing. `--deadman off` disables it. |
| Cache health | **Quiet until it bites.** Claude Code never re-renders an idle session, and while you work the prompt cache is always freshly ~1 TTL from expiry — so a proactive "expiring soon" isn't honestly observable, and `auto` spends no width on it. It speaks only when a rewrite actually happens: `≡!419k` the instant you resume onto a dead cache (idle longer than the TTL) or a mid-session prefix collapse — a 419k-token re-cache at ~20x the read rate (and the same burn on your 5h/7d quota on subscriptions). **Bold red past 200k** — the premium-band miss. `≡~` while a large prefix rebuilds. `--cache always` additionally keeps the freeze-safe deadline `≡@15:20` (last request + TTL; a past time in a frozen frame reads "expired at 15:20"). TTL defaults to 1h (claude.ai subscriber sessions) or 5m (API-key auth); an observed usage breakdown overrides it. The `≡` glyph (U+2261) reads as stacked cache layers — one terminal column, quiet and distinct. |
| Cache health | **Quiet until it bites.** Claude Code never re-renders an idle session, and while you work the prompt cache is always freshly ~1 TTL from expiry — so a proactive "expiring soon" isn't honestly observable, and `auto` spends no width on it. It speaks only when a rewrite actually happens: `≡!419k` the instant you resume onto a dead cache (idle longer than the TTL) or a mid-session prefix collapse — a 419k-token re-cache at the write rate (20x a read on most models, 80x on Fable 5.1 where reads cost 0.025x), and a burn on your 5h/7d quota on subscriptions. **Bold red past 200k** — the heavy rewrite. `≡~` while a large prefix rebuilds. On Claude Code 2.1.251+ the badge reads the CLI's own `prompt_cache` ledger: a miss is what the CLI counted as one, a compaction rebuild shows `≡~` rather than `≡!`, and the TTL and deadline are exact; older CLIs keep the inferred detector. `--cache always` additionally keeps the freeze-safe deadline `≡@15:20` (last request + TTL; a past time in a frozen frame reads "expired at 15:20"). Without the ledger, TTL defaults to 1h (claude.ai subscriber sessions) or 5m (API-key auth); an observed usage breakdown overrides it. The `≡` glyph (U+2261) reads as stacked cache layers — one terminal column, quiet and distinct. |

**Color follows three lanes** so a glance is unambiguous: **status**
(green/yellow/red) = pressure *only* — quota, context, cache, the premium
context band, expensive effort; **identity** (magenta/cyan/blue; fable = bright
(green/yellow/red) = pressure *only* — quota, context, cache, the rewrite
exposure past 200k, expensive effort; **identity** (magenta/cyan/blue; fable = bright
red, matching its Claude Code TUI color) = model family; everything else is
**neutral** grey/white. Warm status color always means "near a limit or cost."

Expand Down Expand Up @@ -206,12 +206,17 @@ renders the statusline only on activity — so no warning can appear while you'r
away. The honest moment is when you come back: the first post-idle render sees
activity resume after a gap longer than the TTL and reports the rewrite as
`≡!Nk`, sized at the re-cached prefix (`≡!419k`), **bold red past
200k** — the expensive premium-band miss. This fires even when the 300ms render
debounce skipped the `read=0` turn, because the detector keys off the stale
activity anchor, not the one-frame collapse; breaks are held ~60s so a busy
turn's refresh doesn't erase them. A cache miss re-caches the whole prefix at
~20x the cache-read rate, and on subscription plans the same multiplier lands
on your 5h/7d quota.
200k** — the heavy rewrite. On Claude Code 2.1.251+ the CLI sends its own
`prompt_cache` ledger (misses, expected rebuilds, tokens re-cached, TTL,
expiry) computed from every response, including the turns the 300ms render
debounce skipped — when present it drives the badge: a miss is what the CLI
counted, a compaction rebuild shows `≡~` instead of `≡!`, and the deadline is
exact. Older CLIs keep the inferred detector, which keys off the stale
activity anchor rather than the one-frame collapse. Either way breaks are held
~60s so a busy turn's refresh doesn't erase them. A cache miss re-caches the
whole prefix at the write rate — 20x a cache read on most models, 80x on
Fable 5.1 where reads cost 0.025x — and on subscription plans the rewrite
burns 5h/7d quota too.

The activity anchor is the last observed *usage change*, not the last render —
Claude Code also re-runs the statusline on vim/permission/model changes with
Expand Down
Loading
Loading