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
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@

## Unreleased

## v0.30.0 — 2026-08-19 — the future folds

**The 7d strip stopped drawing the future one cell at a time.** Thirty
hollow cells after the now-marker carried one fact between them — "the
week has room left" — and spent thirty columns saying it (thirty-one
when the forecast painted them all `×`). The live row now keeps two
cells after `▮` and folds the rest into `...▯5hx28`: the count of 5h
slots to the reset, `×` red when the tail projects dry. History still
draws in full — that is the information — and the fold only engages
when it hides at least ten cells, so a closing week still draws to its
edge. The `week` subcommand's wide ledger is untouched.

**The 5h strip reads by the hour.** Ten half-hour cells were more
resolution than a glance uses; five hour cells tell the same story in
half the width, and the freed columns go to the notice beside them.

**The live demo became the instrument it demos.** The GitHub Pages
site dropped the fake macOS terminal window for a meter: a recessed
register carrying the same frame-by-frame simulation (now with the
folded week tail, hour cells, and a dry-forecast frame), a mono
reading number the anatomy table cites (№ 0008), answering-pair
anatomy rows — the left column states the reading, the right says
what to do about it — and an odometer roll on quota digits that
change. Narrow screens get a truthfully compacted line, the same way
the script itself gives up the trace chip first. Stale claims fixed
across the surfaces: 417 tests everywhere (README said 383, the site
384, llms.txt 379), and llms.txt learned the new week-row grammar.

**The forecast refuses a corrupt profile.** A weekday that claims to
average more than the whole pool per day (`145%/day` of a 100-point
week) can only come from a broken accountant — measured live when a
pre-v0.29.0 build sharing the same home wrote `recent_24h: 343` and the
walk called a 2%-used fresh window dry in 30 hours, red, on every
render. Impossible input now earns silence, not a siren; `recent_24h`
alone clamps to 100 (legitimately larger across a reset, but this
window cannot lose more than everything in a day).

## v0.29.0 — 2026-08-19 — the store learns to count

**Fixed: burn was counted wrong, by a factor of three.** The weekday
Expand Down
12 changes: 8 additions & 4 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,15 @@ own signal.
## The ledgers (row 2)

```
5h ▃▄▮▯▯▯▯▯▯▯ 0.6x @04:00 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▅ˍ▂▁▁ ˍˍˍ▃▅ ˍˍˍ▅ ▆▆ˍ▂▮▯▯ 0.7x @Wed 09:00
5h ▃▄▮▯▯ 0.6x @04:00 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▮▯▯...▯5hx14 0.7x @Wed 09:00
```

One grammar, two scales. `5h` = this window as 10 half hours; `7d` = the
period as 34 five-hour windows, oldest left, a gap at each local midnight
*in history only* — the run from `▮` on is contiguous. Each strip ends
One grammar, two scales. `5h` = this window as 5 hours; `7d` = the
period as 34 five-hour slots, oldest left, a gap at each local midnight
*in history only* — the run from `▮` on is contiguous. History draws in
full; the live row folds the 7d future after two kept cells into
`...▯5hx14` — the slots left to the reset, all alike, `×` red when the
tail projects dry (the `week` report still draws every slot). Each strip ends
with its pace (used ÷ elapsed; dim <1x, pressure ≥1x, hidden under 15 min)
and the reset its right edge is — axis labels, not restated badges.

Expand All @@ -97,6 +100,7 @@ and the reset its right edge is — axis labels, not restated badges.
░ unknown — no sample; never drawn as idle
▮ now
▯ ahead — the hollow of ▮
...▯5hx14 the folded future: 14 more 5h slots to the reset (live 7d row)
× pace won't cover it (7d: learned forecast, linear when cold; 5h: linear)
```

Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Every component earns its place:
| 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.9x @23:00 7d ▅▁▂ ▃▅ˍ▃▅ …▮▯▯ 0.7x @Wed 09:00` — this sitting as ten half hours, the week as its 5h windows (day-gapped), height = what each cell burned, `▮` now, `×` where the pool runs dry, each strip ending 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). |
| Week row | **A row of its own, under the badges**: `5h ▅█▃▮▯ 0.9x @23:00 7d ▅▁▂ ▃▅ˍ▃▅ …▮▯▯...▯5hx19 0.7x @Wed 09:00` — this sitting by the hour, the week as its 5h windows (day-gapped, the far future folded to a counted `...▯5hx19`), height = what each cell burned, `▮` now, `×` where the pool runs dry, each strip ending 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. |

Expand All @@ -118,7 +118,7 @@ red, matching its Claude Code TUI color) = model family; everything else is
| OAuth + macOS Keychain | -- | Yes |
| Change flash on every refresh (`+.37` cost, `+9`/`-27` context, `+N` quota) | -- | Yes |
| Model-scoped weekly quota (`fb`/`op`/`sn`) | -- | Yes |
| Week row: the 5h window as half hours, the 7d period as 5h windows, what each cost | -- | Yes |
| Week row: the 5h window by the hour, the 7d period as 5h windows, what each cost | -- | Yes |
| Works behind trusted mitm proxies (NODE_EXTRA_CA_CERTS) | -- | Yes |
| 383 bats tests + CI | -- | Yes |

Expand Down Expand Up @@ -255,7 +255,7 @@ right edge shared with line 1:

```text
proj (main*) +84/-14 8m $6.72 fabl5[1m][██░░42%] fb[66%] [MAX|@work] 5h[38%@23:00] 7d[39%]
- 3x5h left · 20%/win 5h ▅█▃▮▯▯▯▯▯ 0.9x 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▅ˍ▂▁▁ ˍˍˍ▃▅ ˍˍˍ▅ ▆▆ˍ▂▮▯▯ 0.7x @Wed 09:00
- 3x5h left · 20%/win 5h ▅█▃▮▯ 0.9x 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▅ˍ▂▁▁ ˍˍˍ▃▅ ˍˍˍ▅ ▆▆ˍ▂▮▯▯ 0.7x @Wed 09:00
```

The 5h strip prints no reset: line 1's `5h[38%@23:00]` already carries
Expand All @@ -272,7 +272,7 @@ edge and the full sentence sits flush-left beneath them:

```text
proj (main*) fabl5[1m][██░░42%] fb[66%] [MAX|@work] 5h[38%@23:00] 7d[39%]
5h ▅█▃▮▯▯▯▯▯ 0.9x @23:00 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▅ˍ▂▁▁ ˍˍˍ▃▅ ˍˍˍ▅ ▆▆ˍ▂▮▯▯ 0.7x @Wed 09:00
5h ▅█▃▮▯ 0.9x @23:00 7d ▅▁▂ ▃▅ˍ▃▅ ▃▃▁▂▁ ▅ˍ▂▁▁ ˍˍˍ▃▅ ˍˍˍ▅ ▆▆ˍ▂▮▯▯ 0.7x @Wed 09:00
- budget ~3x5h left · even 20%/win · heading ~52%
```

Expand All @@ -285,13 +285,16 @@ budget line shows with it (windows left, what even looks like, where
you land); pressure and surplus clauses still take its place when they
fire.

- **`5h`** — this sitting: the current 5h window as 10 half-hour cells,
height = the 5h points that half hour added (each positive step between
consecutive samples credited to the half hour the later sample fell in).
- **`7d`** — the week: the 7d period as its 5h windows (34 cells, oldest
- **`5h`** — this sitting: the current 5h window as 5 hour cells,
height = the 5h points that hour added (each positive step between
consecutive samples credited to the hour the later sample fell in).
- **`7d`** — the week: the 7d period as its 5h windows (34 slots, oldest
left, the last a 3h stub), height = the 7d points that window burned,
with a thin gap at each local midnight so days read as clusters — and a
day that held five windows shows it — without a ruler.
day that held five windows shows it — without a ruler. History draws in
full; the future folds: two hollow cells after `▮`, then `...▯5hx28` —
28 more 5h slots to the reset, all alike (`×` red when the tail projects
dry). The `week` subcommand's wide ledger still draws every slot.

| Cell | Meaning |
|------|---------|
Expand All @@ -301,6 +304,7 @@ fire.
| `▮` | the cell you are in now |
| `▯` | a cell still ahead of you — the hollow of `▮`, an empty slot waiting |
| `×` | a cell the pool will not cover at the current pace (7d: the learned forecast's dry point, linear when untrained; 5h: linear, the same projection as the badge) |
| `...▯5hx28` | the folded 7d future: 28 more 5h slots to the reset, one token instead of 28 hollow cells (`×` red when the tail ends dry) |

Burn cells take their badge's pressure color; everything else is neutral,
so the row never adds an alarm channel of its own. Both strips are
Expand All @@ -327,7 +331,7 @@ long one:

```text
proj (main*) +84/-14 8m $6.72 fabl5[1m][██░░42%] fb[91%] [MAX|@work] 5h[38%@23:00] 7d[55%]
+ fb 91% vs 7d 55% · go op 5h ▅█▃▮▯▯▯▯▯ 0.9x 7d ▅▁▂ ▃▅ˍ▃▅ … ▆▆ˍ▂▮▯▯ 0.6x @Wed 09:00
+ fb 91% vs 7d 55% · go op 5h ▅█▃▮▯ 0.9x 7d ▅▁▂ ▃▅ˍ▃▅ … ▆▆ˍ▂▮▯▯ 0.6x @Wed 09:00
+ fb weekly 91% against 7d 55% · the model caps first, not the account; op sits at 33%, so run it for the bulk
```

Expand Down Expand Up @@ -504,7 +508,7 @@ run. Setting only `CLAUDE_CACHE_DIR` keeps the legacy single-dir behavior.
npm exec --yes bats -- t/
```

383 tests across `t/statusline.bats` (371 statusline + integration) and
417 tests across `t/statusline.bats` (405 statusline + integration) and
`t/install.bats` (12 installer). CI runs on push and PR to `main`.

## Project Structure
Expand Down
Loading
Loading