Part 1 (src): expose two add-only, optional aggregations on the menubar
payload's `current`, computed on the unscoped all-provider path from the
surviving-session parse (carried history cannot contribute, as expected).
- current.pullRequests: aggregateByPr rows (top 20 by cost) plus the
multi-link-safe distinctCost/distinctSessions. Rows are by-reference, so
they are never summed.
- current.byBranch: new aggregateByBranch, per-branch spend (top 15 by cost)
that carries each session's last-seen git branch forward across its turns.
The cache stores a turn's branch only when it changes, so the parser now
resolves the branch at reconstruction (before the date slice) and records
SessionSummary.everHadBranch from the full transcript. That lets the report
keep a branch-bearing session's pre-branch spend in an explicit null row
even when the range clipped the anchor turn, while a provider that never
captures a branch still contributes nothing.
Part 2 (app): new "Pull requests" sidebar entry between Sessions and Spend
(shortcuts reflow to number 8 for Plans). It renders the pullRequests rows as
a refined table with the PR label linking out via openExternal, cost,
sessions, calls and an active-date span, plus a footnote stating the distinct
total and the by-reference attribution. A quiet explanatory line shows when no
PR links exist, never a fake table.
Tests: aggregateByBranch unit tests (carry-forward, null bucket, clipped
anchor, by-reference session counts) and the Pull requests component tests
(table, external link, footnote, both empty states). Nav-reflow assertions in
Sidebar and App tests updated.
Co-authored-by: reviewer <review@local>
The once-daily usage_snapshot now includes each top model's primary
task category (which model for what purpose, one cheap by-model call
per day), plus MCP servers and skills used (names + coarse usage
buckets). Names + buckets only, no prompts/paths/dollars; graceful
degradation if the by-model fetch fails. /telemetry page updated.
Energy (field report: ~3x Chrome drain):
- polls skip entirely while the window is hidden/minimized, with one
catch-up refresh on return when data is stale; visible-but-unfocused
keeps polling (second-monitor case)
- all looping animations pause under html.page-hidden (the sidebar
flame flicker was a perpetual compositor drain)
- default cadence 60s; an explicit stored choice is always honored
Currency (field report: set USD, still saw EUR):
- memo-served payloads re-applied their embedded stale currency; config
mutations now purge the renderer memo and force-refresh, and a
switching payload can never overwrite the applied currency
- USD default verified end to end
Measured: hidden window = 0 new CLI spawns over 5 cadences (was: full
polling forever). App 340/340, build + package green.
- 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.
Same model via different providers (MiniMax M3 x3) was
indistinguishable: rows now carry providerDisplayName in both lenses.
The provider dropdown and Sessions quick-filter no longer hide detected
providers with zero spend in the current period (Hermes was invisible
while Settings listed it); entries sort by cost, zero-spend last.
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.
- Settings/General: Daily budget row (Off / USD amount / Tokens),
positive-finite validation, warns at 80% and alerts at 100%
- app-wide one-line banner above every section: --warn at 80%+, --bad
at 100%+, dismissible for the rest of the day
- USD caps compare raw USD against per-provider daily cost (works under
any filter); token caps evaluate only on the all-providers view since
provider-filtered history zeroes token fields (comparing against a
false zero would always pass)
212/212, build green.
- TopBar picker (shown only when >1 Claude config dir exists) threads
--claude-config-source through getOverview; synthetic 'All Claude
configs' entry returns to the aggregate default
- config id validation accepts the real <kind>:<16hex> id shape while
still rejecting flag-shaped input (leading char anchored)
- both incompatibility directions handled: selecting a config resets a
non-Claude provider filter, and picking a non-Claude provider clears
the config scope, so the CLI rejection is unreachable
- choice persisted (localStorage); active config shown in the scope
line and read-only in Settings/General
Verified live against 2 real config dirs. 205/205, build green.
- 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.
- Plans tab: add/remove budget presets via `plan set`/`plan reset`, lists
configured plans, links to the full Plans screen
- Export tab: format + provider + native folder picker (electron dialog)
→ runs `codeburn export`, reports the result
- Devices: real Remove (`devices rm`) + Refresh; visibility is read-only
from `share status`; pairing shows an honest "pair from terminal" note
instead of a dead Approve button; combine row is read-only status
- Apply the persisted theme at app startup (not only when General mounts)
- Settings gets onNavigate for the "Open Plans" link
Only one-shot, non-interactive CLI commands are wired as buttons; the
interactive share daemon and pairing flow stay honestly read-only.
Sessions: provider-grouped list of every session in range (project,
models, turns, cost, tokens) with a lightweight real detail — no
fabricated titles, no unbacked context-window widget.
Compare: two-model picker (defaults to top two by cost) across
Performance, Efficiency, Category head-to-head, Working style, and a
derived Context panel, all mapped straight from `compare --format json`.
Nav reordered to match the design: Overview, Sessions, Spend, Optimize,
Models, Compare, Plans, Settings (⌘1-7).
- Period seg now uses the 5 real CLI periods (Today/7D/30D/Month/6M);
6M maps to 'all' (matches the menubar).
- Provider control is a real dropdown listing only DETECTED providers
(accumulated from current.providers so it never shrinks) + All.
- New RangeCalendar: month grid with click-and-drag range selection
(+ two-click fallback, future-date disabling) behind a calendar button;
commits a custom {from,to} threaded to Overview/Spend/Models/Optimize
via new --from/--to CLI args (Plans/Settings stay period-only).
- typecheck clean, 87 vitest pass.
Install an app menu without the CmdOrCtrl+R reload accelerator so ⌘R reaches the renderer for
in-place refresh (keeps Edit/Window roles + dev DevTools). Model labels are now family-level
("Opus"/"Sonnet"/"Haiku"/"GPT / Codex") and the Sankey shows the real model id instead of the
wireframe's sample version names. Optimize retains last-good yield across 30s revalidation (no
flicker to "—"). Removed hints for unimplemented ⌘K/⌘E/esc; fixed stale TopBar comment.
Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).