- prefetch fires exactly once per provider per session; memo sized to
detected providers so base keys never evict (was: 12 full-history
parses every 30s forever from LRU thrash re-triggering the effect)
- day-aggregator buckets whole turns by user-message timestamp,
matching the report/headline path: trend bars and provider breakdown
now reconcile exactly (midnight-straddling turns were split);
DAILY_CACHE_VERSION 13 with an IANA tz guard that rehydrates when
the cache crosses timezones
- overview's redundant second all-provider scan collapsed (~11% faster
warm); numbers parity-guarded
- lock takeover cleans stale hydrating.lock; SIGINT/SIGTERM release it
- scanner skips EPERM/EACCES provider dirs (progress shows skipped)
instead of aborting; onboarding hints Full Disk Access
- rateLimited quota copy render tests; export -f json emits valid
empty JSON when no data
Root 2023 (+4), app 327 (+3), typechecks clean.
Root causes (verified live): the Claude keychain branch was never
enabled (allowClaudeKeychain never passed), security -w returns
hex-encoded JSON the parser rejected, and Codex had no keychain
discovery at all.
- shared readKeychainPassword: Apple-signed /usr/bin/security, 90s
window so the user can answer the macOS dialog, hex decode,
notFound vs accessDenied classification; secrets never logged
- Claude: keychain fallback active (gated to explicit Refresh so
launch never ambushes with a dialog); connection retained across
keychain-less background polls
- Codex sources in order: the menubar's own cached OAuth (read-only,
401 = one re-read, never rotate), legacy ~/.codex/auth.json
(writable), com.openai.codex plaintext if usable; Safe Storage
never decrypted
- new accessDenied state (amber, 'locked') + ConnectAffordance on
Plans cards and Settings rows: login command, keychain-Allow note,
force Refresh
Live verification: Claude connected with its real tier and windows;
Codex correctly accessDenied until the keychain dialog is allowed.
273/273 (26 files), typecheck + build green.
- will-navigate/window-open denied; openExternal restricted to http(s);
production CSP drops 'unsafe-inline' scripts (dev-only Vite plugin
re-adds it for the Fast Refresh preamble)
- single-instance lock; before-quit reaps in-flight CLI children; 16MB
output cap rejects with CliError 'too-large'
- read-only CLI spawns coalesce: concurrent identical calls share one
child, a 5s result cache absorbs same-cadence pollers (six sections
polled getOverview independently); actions bypass and flush the cache
on completion so post-action refetches are fresh
- every renderer-supplied argv string validated (period/provider/range/
currency allowlists, no flag-shaped tokens, absolute export path) with
new CliError kind 'bad-args'; envelope + action stderr routed through
sanitizeError
- quota force-refresh: manual refresh (refreshToken) passes force which
invalidates QuotaService's cache; steady polls do not
- POSIX credential mode check gated off on win32
App suite 190/190, build green (production index.html verified strict).
- formatUsd applies the payload currency {code,symbol,rate} once at
display; formatConverted (symbol only) for CLI-preconverted plan
values so nothing converts twice
- provider picker built from providerDetails: label shown, internal id
sent as --provider (fixes filters for providers whose display name
differs, e.g. Grok Build); falls back to map keys on older CLIs
- getYield threads the active provider through preload/main/sections
App suite 170/170, root 1615/1615.
Replace the $-budget model as the primary view: per-provider cards (Claude/Codex)
from getQuota show the real 5-hour/weekly windows (% used, resets-in, severity
colors), the auto-detected subscription tier, connection states, and the credit
footer. Manual budget plans demoted to a secondary section.
typecheck clean; 148/148 tests pass. Needs a full relaunch for live quota.
Review fix: periodEnd is exclusive (next reset), so the cycle end now renders periodEnd-1 day
with totalDays=diff (was Jul 15/31, now Jul 14/30); dates format in local time (not UTC);
test mock re-derived from the real emitter. Minor: en/em dashes, CLI provider order, non-
contradictory near-status copy, +near-status and plan-singular tests.
Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).
One track per plan from getPlans() (StatusJson/JsonPlanSummary): fill = percentUsed, red
`.over` past 100% with overage shown, amber `.pace.hot` when over/near pace vs mint `.ok`.
Cycle caption from the plan period; pay-as-you-go rows use `.mut`. Add plan… is M2 (visual).
Honest empty state when no plan is configured.
Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).