Release notes

FEATURE

Antigravity quota now notifies and warns ahead

The agy_session and agy_weekly rows now send notifications instead of being omitted from the notification allow-list and running down silently. Each row also has its own usage tracker, so it can warn before quota reaches zero rather than stay quiet until then. Antigravity data comes from a network endpoint behind a local cache, which stores a previously fetched result for reuse. When that cache is stale, its snapshot is marked unavailable, so it cannot announce that quota has run out. Forecasting uses the snapshot's fetched_at time instead of the polling clock, preventing repeated reads of a payload that changes only every five minutes from inventing a trend. Both rows use a 3600-second window because the default ten-minute window cannot gather MIN_FORECAST_SAMPLES. A stale snapshot also suppresses forecasting, matching agy_window_keeper.

FEATURE

usage doctor catches Codex history migrations that zero counts

usage doctor, a command that checks for problems in usage data, now looks for a Codex history migration. After Codex began writing tables such as rollout_migration_state, a completed migration could stop the session .jsonl files from being written while loaders/codex_loader.py silently returned zero token counts. It now warns when .jsonl files contain no entry from the last seven days while thread_history_1.sqlite has thread_turns from that same window. That exposes a mismatch that used to zero counts without an error.

FEATURE

usage doctor compares Claude costs with the official figure

When a bill does not match, the problem may be an outdated offline price table rather than you.

It compares cost.total_cost_usd in usage-status.json with the amount usage calculates itself, then warns when the gap is both over 20% and over $1. On the machine where this feature was written, the figures differed by 46.4% because the price table listed claude-opus-5 at half its real rate. That is the kind of price drift an offline fallback table can hide.

BUGFIX

WIN95 and Newspaper project cards no longer clip rows

Project usage cards on the WIN95 and Newspaper panels no longer show only their title bar while clipping the rows below. To measure natural height, a panel temporarily releases the height: 100% chain to auto. Yet both panels write the project card as flex: 1, whose flex basis is zero, while the card's overflow: hidden, needed by retro window chrome, suppresses the automatic content-based minimum size that would keep it open. During measurement the card therefore collapsed to its title bar: WIN95 reported 877 pixels and Newspaper 876, versus 923 for structurally identical Classic.

Measurement now temporarily pins stretching children to flex: 0 0 auto and restores them afterward, leaving each panel's visual design unchanged.

FEATURE

Eight modules move into packages, clearing repository root

The repository root has eight fewer loose modules, while behavior stays unchanged. burn_rate, usage_rate, window_keeper, and agy_window_keeper moved into quota/; time_utils, usage_logging, usage_lang, and usage_dir_sweeper moved into usage_common/.

The shared utility package is called usage_common, not common, because the uvx usage-cli entry point installs top-level packages into shared site-packages, where common can easily collide with another package. No compatibility shims remain: every repository import was updated in one pass, pyproject.toml dropped eight py-modules entries and added the two packages, and setup_app.py followed.

Original changelog

Original changelogAntigravity quota now notifies and warns ahead
**Antigravity quota now raises notifications, the way Claude Code and Codex already did.** The `agy_session` and `agy_weekly` channels were missing from the notification allow-list, so the two Antigravity rows could run to the floor without ever saying so. Antigravity quota is the one source that comes from a network endpoint behind a local cache rather than a file on disk, so `QuotaRowState.available` is hard-coded to `True`; a stale snapshot now passes `available=False` instead, so an expired cache can no longer announce that the quota has run out.
**The Antigravity rows now warn before the quota is gone, not after.** They had never been wired to the burn-rate forecast, so their `warning` flag was permanently `False` — Claude and Codex warned ahead of a reset and Antigravity stayed silent until it hit the floor. Both rows now register their own tracker. Samples are timestamped with the snapshot's own `fetched_at` rather than the wall clock, because the Antigravity snapshot only changes every five minutes and polling would otherwise read the same payload again and invent a slope out of it. Both rows use a 3600-second window: at one sample per five minutes the default ten-minute window never collects `MIN_FORECAST_SAMPLES`, which is the same as not forecasting at all. A stale snapshot suppresses the forecast entirely, matching what `agy_window_keeper` already does.
Original changelogusage doctor catches Codex history migrations that zero counts
**`usage doctor` now catches a Codex history migration that would silently zero the token counts.** Codex has begun writing tables such as `rollout_migration_state`, and if that migration lands and the `.jsonl` files stop being written, `loaders/codex_loader.py` returns zero without an error anywhere. The check warns when the session `.jsonl` files hold no entry from the last seven days while `thread_history_1.sqlite` does hold `thread_turns` from that window.
Original changelogusage doctor compares Claude costs with the official figure
**`usage doctor` now reconciles its own Claude cost against the official one.** It compares `cost.total_cost_usd` from `usage-status.json` with the figure usage calculates itself and warns when they differ by more than 20% and more than $1. On the machine this was written on they differed by 46.4%, because the price table listed `claude-opus-5` at half its real rate — exactly the kind of drift the offline fallback table is known to hide.
Original changelogWIN95 and Newspaper project cards no longer clip rows
**The project usage card no longer clips its rows on the WIN95 and Newspaper panels.** A panel measures its own natural height by releasing the whole `height: 100%` chain to `auto`. Both panels write their project card as `flex: 1`, which means a flex basis of zero, and the card's own `overflow: hidden` — the retro window chrome depends on it — suppresses the automatic content-based minimum size that would otherwise hold the card open. The card therefore collapsed to its title bar during the measurement, and the reported height was short by exactly the project rows: 877 pixels for WIN95 and 876 for Newspaper against 923 for the structurally identical Classic. The measurement now pins such stretching children to `flex: 0 0 auto` while it measures and restores them afterwards, so no panel's visual design changes.
Original changelogEight modules move into packages, clearing repository root
**Eight more modules moved out of the repository root.** `quota/` took `burn_rate`, `usage_rate`, `window_keeper`, and `agy_window_keeper`; `usage_common/` took `time_utils`, `usage_logging`, `usage_lang`, and `usage_dir_sweeper`. The shared-utility package is named `usage_common` rather than `common` because the `uvx usage-cli` entry point installs top-level packages into a shared `site-packages`, where `common` is about the most collision-prone name available. No compatibility shims were left behind — every import in the repository was updated in one pass, `pyproject.toml` dropped the eight `py-modules` entries and added the two packages, and `setup_app.py` followed. Behavior is unchanged.