Commit graph

20 commits

Author SHA1 Message Date
Resham Joshi
4d2bcbac7f
app: Workflow card on Overview and session titles in the Sessions view (#784)
Two Phase-1 desktop additions over the renderer's existing card grammar.

Overview workflow card: a panel below the daily chart with two stat tiles
(correction rate with count, median time to first edit) plus a top-rework
line and one coaching caption. The caption is derived locally with the CLI's
buildCoachingNotes thresholds (corrections, then file churn, then time to
first edit; first that fires). The card hides entirely when no real signal is
present, never renders zeros, and shows a "N% priced" chip only when pricing
coverage is a number below 1. Payload types gain optional workflow,
topReworkedFiles, and pricingCoverage fields so older payloads render as before.

Sessions view: the captured session title becomes the row headline with the id
demoted to the mono secondary line; untitled rows keep the project as the
headline (unchanged). Title is also searchable and added as an optional
SessionRow field so older CLIs render unchanged.

Co-authored-by: reviewer <review@local>
2026-07-20 13:55:33 -07:00
Resham Joshi
dbd93fee51
app,dash: render pre-history days as no data, not a currency zero (#765)
* fix(app,dash): render pre-history days as no data, not a currency zero

Days before the first recorded day in history.daily were painted as a real
0.00/0 calls in the daily activity heatmap and the daily spend charts. That
zero is unknown, not measured, so those cells and bars now read "No data
recorded" instead of a currency zero. Genuinely idle days within recorded
history stay as real zeros.

- app: heatmap cells, the daily spend bars, and the daily-by-model columns
  get a distinct no-data style plus a "No data recorded" hover for days before
  the first recorded day.
- dash: the granular line chart drops buckets before the first recorded day so
  no flat zero line is drawn before any history exists.
- data-start is derived in the UI layer from history.daily; payload shape is
  unchanged.

Verified: app vitest 418 pass (7 new for the no-data behavior); app, dash, and
root typecheck clean; dash and app renderer vite builds succeed.

* no-data: cap guard, timezone-free dash trim, StackedBars aria

Three hardening fixes from adversarial review of the no-data rendering:

dataStartKey returns null at the payload's 365-entry history cap, where
the oldest retained entry is no longer the true data start; classifying
past it would label real aged-out history as no data on long custom
ranges. Documented that the install-to-first-use gap reading as no data
is literally accurate: nothing was recorded then either.

The dash granular chart now trims leading zero-only buckets by value
instead of comparing bucket timestamps against date keys, so producer
and viewer timezone skew can never drop a real first-day bucket, and an
all-zero series lands in the established empty state.

StackedBars no-data columns expose their state via aria-label, not only
a title on a non-focusable div.
2026-07-20 08:03:24 -07:00
iamtoruk
e9655fa03c fix: hydration completeness markers, cold-only splash, honest switching, prefetch
P0: an interrupted cold hydration left partial caches that the
emptiness check read as warm — the daily backfill froze (missing older
days) while session parsing healed gradually (drifting totals),
poisoning every surface sharing the cache. Both caches now carry an
explicit complete marker: partial resumes cold under the hydration
lock; unmarked caches self-heal with one re-hydration. Regression test
seeds the exact frozen artifact and asserts bit-for-bit convergence
with a never-interrupted run.

- splash indexing reveals only on genuinely cold scans (explicit cold
  flag in the progress protocol), never on warm incremental re-parses
- uncached filter switches clear to skeleton instead of showing the
  previous filter's numbers; cached switches paint same-commit;
  background prefetch warms every detected provider after idle
- build stamp (git sha + date) in splash and About ends build ambiguity
- payload parity test: identical payloads on identical cache, and
  payload totals equal the CLI report path
- Sessions empty state keeps the provider filter row; zero savings
  line hidden; TCC usage-description strings + Full Disk Access docs
- warm profile documented: optimize scan ~1.47s dominant (safe
  refactor deferred), parse ~1s, aggregation ~30ms

Root 1848, app 320, typechecks clean.
2026-07-16 14:05:11 -07:00
iamtoruk
300e949951 feat(app): refresh cadence, instant switching, cache coherence, telemetry v1
Performance and coherence:
- Settings > General 'Refresh every' (Manual/30s/1m/3m/5m/10m), live via
  RefreshCadenceContext; Manual polls only on demand
- usePolled memoKey LRU: provider/period switches paint the last-good
  result instantly with a switching hairline while refreshing quietly
- quota TTL 5min + honest rate-limited copy on 429 backoff
- version-suffixed cache files (session-cache.v5.json, daily-cache.v12,
  auto-minted on future bumps); legacy files never written or deleted,
  adopted once when versions match: old and new binaries coexist
  without clobbering (field-observed menubar-vs-desktop ping-pong)
- advisory hydration lock: concurrent cold starts share one scan
  (wait-then-read-warm), stale/dead locks self-heal, never a
  correctness gate

Telemetry v1 + onboarding (desktop only, per owner decisions):
- first-launch onboarding (3 feature screens + consent); region-split
  default (EU/EEA/UK/CH off, elsewhere on, unknown off); nothing sends
  before consent completion or while off; dev builds never send
- anonymous install UUID, rotated on opt-out; day-granularity events,
  cost buckets only; whitelisted names; 200-event queue, 5min flush
- Settings > Privacy live toggle replaces the static claim

Zero computed-number changes. App 316/316, root 1805.

Wire contract targets api.codeburn.app/v1/telemetry (Worker follows).
2026-07-16 11:47:05 -07:00
iamtoruk
09ac453fcc feat(app): coaching signals card (menubar rule parity)
Replaces the three-rule anomaly card with a Wins/Improvements/Risks
Signals card mirroring the menubar's rule set and thresholds
(FindingsSection.swift): cache >=80% and one-shot >=0.75 and spend-down
>10% and streak >=5d as wins; top-3 optimize findings, low cache <50%,
low one-shot <0.5, retry tax >=25% of spend as improvements; weekday
spike >1.8x, week-over-week >+25%, projected month >1.3x prior month as
risks. Time-comparison rules suppressed under a custom range. Renders
nothing when empty; icons per group, never color-alone. Anomaly test
coverage ported, not deleted.

221/221, build green.
2026-07-16 03:30:51 -07:00
iamtoruk
8a3fa69fe9 fix(app): real currency conversion, provider ids in picker, yield provider scoping
- 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.
2026-07-16 01:36:59 -07:00
iamtoruk
cbddd3a9d7 fix(app): accuracy batch A — provider scoping, honest chart axes, range threading, stale-error banner
- Provider filter: Models-this-period sources current.topModels (history
  days carry empty topModels per provider); StackedBars falls back to a
  cost-proportional single segment like the Swift menubar
- Chart windows align to the CLI (week -7, 30days -30, all = 6 months)
  and zero-fill a contiguous calendar window; history.daily is sparse and
  both sides key local YYYY-MM-DD, so lookups are safe (verified on real
  CLI output)
- Custom range threads into Overview panels (chart, models table) and
  suppresses MTD/projected and vs-last-week comparisons; Compare states
  that custom dates fall back to the period
- usePolled clears errors per attempt and sections show a StaleBanner
  instead of silently rendering last-good data after a failed refresh
- Cache-hit denominator matches the CLI; hero savings relabeled 'Saved by
  applied fixes' + new 'Saved via local models' line; formatCompact
  everywhere; Optimize 'Fixes' count matches the rendered list

App suite 163/163 (was 147), root 1613/1613.
2026-07-16 01:14:41 -07:00
iamtoruk
a8351bfd1a fix(app): daily charts empty/flat on real data — use real last-N days
contiguousDailyWindow rebuilt a calendar window from the client clock and looked
up days by date key; on real CLI data every lookup missed and zero-filled, so the
Spend chart went empty and the Overview chart went flat. Use the real last-N
backfilled entries of history.daily directly (Spend last-15, Overview
last-max(30, periodDaily)) and remove the dead helper.

typecheck clean; 147/147 tests pass.
2026-07-12 17:06:46 -07:00
iamtoruk
642a12b7ff feat(app): remove the Nearest-limit quota ring from Overview
Drop the FuelRing (and its now-unused plans polling) from the ov-stats3 card;
the stats row is now Month-to-date + Projected month (2 equal columns).

typecheck clean; 128/128 tests pass.
2026-07-12 15:25:00 -07:00
iamtoruk
76a5db4f55 feat(app): Optimize organization + gear icon, more logos, drop relative-cost column
- Optimize: colored impact badges (High/Medium/Low) + fixed-column layout;
  fix the dead "Fixes" tab; findings no longer look falsely clickable
- Optimize nav icon: gear + magnifying glass (was a lightning bolt)
- remove the "Relative cost" bar column from the Overview models table
- provider logos: Goose / Pi / Warp + Cursor Agent (own logo) from repo assets

typecheck clean; 128/128 tests pass.
2026-07-12 13:56:07 -07:00
iamtoruk
34333dc2c5 feat(app): more provider logos + drop redundant session dot
- ProviderLogo: add Copilot/Gemini/Qwen/OpenCode marks; reuse the Cursor logo
  for Cursor Agent; single-color logos (Claude, Gemini) via a SINGLE_LOGOS map
- remove the redundant model-series dot from Overview "Most expensive sessions"
  (the row already names the model in its sub text)

typecheck clean; 128/128 tests pass.
2026-07-12 13:09:53 -07:00
iamtoruk
b0299c57ac feat(app): design reskin — token unification, a11y, and chart color + shading
Design-consistency pass (from the audit):
- unify the indigo→plain grey/border token families (alias --t1/2/3, --hair/2 onto --ink/mut/mut2, --line/2); fix a leftover hardcoded chevron color
- accent :focus-visible rings on SegTabs, chart bars, calendar, buttons, Settings toggles; ov-link hover
- theme-aware Spend legend swatches + Sankey (fixes a light-mode contrast failure)
- wire Models "Compare…" → Compare and Plans "Add plan…" → Settings pane; drop the fake-affordance Plans label; Settings loading captions
- Compare card radius/title, set-input, alias CTA color, outer-border tokens, session mono numbers, frame spacing to match the mockup
- scope a date-flaky Overview test assertion to its card

Reskin batch 1 — color + shade (current tone):
- validated CVD-safe model-series palette via dedicated --s-* tokens (light+dark), applied to the daily bars, Sankey, and series dots
- subtle soft card elevation via --card-shadow on the main card containers

typecheck clean; 128/128 tests pass.
2026-07-12 12:36:50 -07:00
iamtoruk
a50389b076 style(app): Overview into divided 3-column cards + metallic grade medal
- Top card: one card, three equal columns split by dividers —
  current spend (with Saved-to-date) | daily activity | efficiency
  scorecard. Removed the duplicate one-shot/cache ratings column and the
  separate efficiency card (EfficiencyScorecard + FuelRing gain a 'bare'
  mode).
- Stats: one card, three equal divided columns — Month-to-date |
  Projected | Nearest-limit fuel ring.
- Efficiency grade is now a circular brushed-metal medal (one subtle
  gradient + inset highlight + depth shadow), tiered A/B-C/D/F, tokenized
  for light+dark — premium, not the flat AI-looking pill.
- Tightened spacing/dead space; 1-column reflow under 900px.
typecheck clean, 88 tests pass.
2026-07-11 11:47:30 -07:00
iamtoruk
0ba3a853d8 feat(app): Overview v2 phase 2 — efficiency scorecard, cost-per-outcome, anomalies, routing what-if
Unique widgets neither the TUI nor menubar has, all from real data:
- Efficiency scorecard: A+..F composite (45% one-shot / 30% cache /
  25% retry-health), with the source component rows + retry share of
  spend; null one-shot → neutral input + 'partial' disclosure.
- Cost per outcome (new getYield poll): $/commit, $/productive session,
  productive/reverted/abandoned split; honest — for zero/empty/error.
- Anomaly callouts: today-vs-weekday (1.8x), w/w spend (25%), low cache
  (<50%); strip omitted when nothing fires.
- Routing what-if from current.routingWaste (omitted unless positive).
typecheck clean, 88 tests pass.
2026-07-11 11:24:47 -07:00
iamtoruk
9d906832f6 feat(app): Overview v2 phase 1 — dashboard layout, KPI strip, activity heatmap
- Full-width responsive dashboard grid (2-col body, reflows <900px) so the
  Overview uses the window instead of a narrow column.
- KPI strip: Spend/Calls/Sessions + one-shot rate (current.oneShotRate,
  the previously-missing metric; null → em dash) + cache-hit % + Saved.
- New ActivityHeatmap: GitHub-style 26-week contribution grid from
  history.daily (5 cost levels, active-day count, portal tooltip), ported
  from the menubar's ContributionHeatmapInsight.
- Top activities rail: name · cost · turns · one-shot % (current.topActivities).
- All real payload data, no new bridge calls. typecheck clean, 87 tests pass.
2026-07-11 11:12:41 -07:00
iamtoruk
dce18fff83 feat(app): Overview redesign — models table, legible chart, period-aware hero
- Hero now reflects the SELECTED period (current.cost + current.label +
  calls/sessions), not just today — 30D no longer shows today's number.
- New Models table: per-model Input/Output tokens + Cost + Calls,
  aggregated from history.daily[].topModels[] (real per-day data),
  rebuilt with a robust bar → left-name → right-numbers layout (fixes
  the offset/misaligned columns).
- Daily chart: weekly date ticks (MMM d), Avg/Peak/Yesterday chips, and
  the hover tooltip now portals to document.body (position:fixed,
  edge-clamped) so it is never clipped.
- Removed the meaningless decorative sparklines. Empty fuel-ring card no
  longer reserves a tall empty box.
- typecheck clean, 87 vitest pass.
2026-07-11 09:33:51 -07:00
iamtoruk
51e47112ca fix(app): Overview daily chart min 30-day window, internal scroll, native chrome
- Daily-spend chart always shows a contiguous >=30 calendar-day trend
  (backfilling gaps with zero bars) instead of collapsing to the period
  selector (e.g. a single bar on Today).
- Real app shell: viewport-height window with an internally scrolling
  .body (was clipped/unscrollable).
- Drop the wireframe's fake traffic-light dots; rely on the real OS
  window controls. Window background follows the system theme.
- Update Overview test to assert the 30-day chart window.
2026-07-11 08:48:35 -07:00
iamtoruk
95cf876999 feat(app): plain theme + Overview reskin wired to real data
New plain.css token layer (light+dark) repaints the whole app shell;
Overview.tsx rebuilt to the locked mockup: hero burn with count-up +
streak, budget fuel ring (getPlans), coach insight, Saved-to-date
(act report), area sparklines, gridline + hover-tooltip daily chart,
sessions. Adds getActReport bridge plumbing. All data real/derived;
per-day tooltip shows calls (no per-day sessions field). typecheck
clean, 82/82 vitest pass.

Note: Saved-to-date needs a codeburn build with 'act report --json'
(source-only today; installed 0.9.15 lacks it) — falls back to $0.
2026-07-11 08:32:11 -07:00
iamtoruk
343156618f test(app): assert Overview period-window slicing, waste-rate, and model joins
Payload now spans a 30-day backfill so slicing is provable. Adds: chart slices
to the week window (7 bars, 23 excluded); which bar is peak/runner-up (idx
10/20); corrected Waste/wk ($5.51 @30d, $23.60 @week); deterministic MTD/
Projected/rest-of-month and "-3% vs June pace" (May days prepended to prove the
prev-calendar-month comparator); matched model-join dot+sub-line and a
same-project/day/calls collision resolving by cost ($10 opus / $2 haiku).
2026-07-10 16:32:54 -07:00
iamtoruk
055e0d07e4 feat(app): Overview section wired to getOverview
Real Overview driven by usePolled(getOverview): 4 stat cards (Today, Month
to date +% vs pace, Projected month est, Waste found /wk), the daily-spend
CapsuleChart, and the most-expensive-sessions list. Every rendered number is
derived from the single MenubarPayload (formulas documented in-code, mirroring
plan-usage projectMonthEnd); topSessions' missing model is recovered from
topProjects sessionDetails. App renders <Overview/> for the overview section,
replacing the T0 smoke view; first-run/error/loading states retained. TDD
spec added (test written first, red→green).
2026-07-10 16:10:51 -07:00