Commit graph

1015 commits

Author SHA1 Message Date
Aditya Vikram Singh
f2864ff964
yield: single-owner commit attribution, ambiguous bucket, heuristic caveat (#692)
Some checks are pending
CI / semgrep (push) Waiting to run
* yield: single-owner commit attribution, ambiguous bucket, heuristic caveat (#641)

* yield: repo-level single-owner attribution, sharper ambiguous rule, determinism pins

---------

Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 13:09:26 -07:00
Aditya Vikram Singh
1b69c83165
parser: dedupe the twin string/Buffer large-JSONL scanners (#698)
* parser: dedupe string/Buffer JSONL scanners into one implementation (#643)

* Remove dead findJsonValueBounds wrapper

---------

Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 12:23:06 -07:00
Resham Joshi
584340d8bd
Merge pull request #700 from getagentseal/fix/telemetry-flush-4xx
fix: telemetry flush drops 4xx-rejected batches
2026-07-16 12:11:22 -07:00
iamtoruk
fd48f6c57f fix(app): telemetry flush drops permanently-rejected batches
A 4xx response means the server will never accept that payload;
retrying it wedges the queue at its cap and blocks all newer events.
Drop on 4xx, keep retrying on 5xx and network failures. Matches the
server's validation contract (batch cap 200).
2026-07-16 12:10:38 -07:00
Aditya Vikram Singh
594bc994d0
copilot: extract skill invocations so Skills & Agents populates (#654) (#697)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 12:02:34 -07:00
Aditya Vikram Singh
35b8f0519e
Extract shared token estimation helper, migrate all CHARS_PER_TOKEN sites (#644) (#677)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 11:58:22 -07:00
Resham Joshi
4d8d81a714
Merge pull request #699 from getagentseal/feat/cadence-and-coherence
Desktop: refresh cadence, instant switching, cache coherence, telemetry v1
2026-07-16 11:47:48 -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
ozymandiashh
057de97360
feat(budget): spend budgets with overview display and scriptable --check (#562)
Ported onto current main after the locale-grouping change in overview.ts;
review follow-ups included:
- explicit note: budgets count the full would-be API cost including
  subscription-proxied spend, consistent with the overview Cost total
- percent display floors the raw value on both surfaces, so 99.6 percent
  renders as 99 percent in warn state and 100 percent appears only when the
  state is over; regression tests cover the boundary on overview and --check
- overview keeps main's en-US locale-grouped formatting and the
  unpriced-model warning block; the budget line renders after it
2026-07-16 11:10:16 -07:00
ozymandiashh
491c9cbfc8
feat: implement act apply-model and report baseline tripwire (#607) (#616)
* feat: implement act apply-model and report baseline tripwire (#607)

* feat: implement act apply-model and report baseline tripwire (#607)

* fix(act): scope model-default tripwire to the applied project

Review fixes for #616:
- modelDefaultRow and the under-20-edit-turns gate now aggregate only the
  target project's sessions (derived from changes[0].path, separators
  normalized before dirname), matching the per-project baseline captured at
  apply time instead of comparing against all projects.
- baseline.candidateModel labels the candidate explicitly, with a
  backward-compatible fallback to metrics key order for existing journals.
- measured model-default rows render a correlation marker instead of a
  formatTokens(0) token claim.
- zero-matching-projects now reports an honest project-not-found note; clean
  rows route through confidenceFor like every other kind.
- tests: tripwire fires on a same-project regression that global aggregation
  would mask (fails on pre-fix code), clean and not-measurable cases,
  Windows-separator journal paths with an excluded masking project.
2026-07-16 11:10:08 -07:00
ozymandiashh
42509f89ab
fix(menubar): route Codex plan retry to refreshCodex and fix loading copy (#687)
The Codex tab's Plan section reused two Claude-hardcoded views:
PlanLoadingView always showed "Reading Claude credentials..." and
PlanFailedView's Retry always called store.refreshSubscription(), so a
Codex user retried the wrong provider.

Extends the 61209bd parameterization to the two remaining shared views:
- PlanLoadingView(message:) - Claude sites keep the current copy, Codex
  sites show "Reading Codex CLI credentials..."
- PlanFailedView(error:onRetry:) - drops the internal AppStore refresh
  dependency; Claude sites pass refreshSubscription(), Codex sites pass
  refreshCodex()

The failed-state accent stays Theme.brandAccent, matching every sibling
plan view and the section's single-palette convention.
2026-07-16 11:09:58 -07:00
Aditya Vikram Singh
6cdbf560dc
optimize: bound zero-edit low-worth savings estimate to a recovery fraction (#640) (#691)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 11:09:44 -07:00
Resham Joshi
7a91a86dcb
Merge pull request #696 from getagentseal/fix/splash-compact
polish: compact first-run splash progress
2026-07-16 11:00:03 -07:00
iamtoruk
d5b18ff3b4 polish(app): compact first-run splash
The per-provider ingest list rendered a dozen stacked text rows on
machines with many detected providers. Now one status line with live
tabular-numeral counts (Indexing Claude · 240/699), a single strip of
15px provider logos (dim pending, pulse active, settled done), and a
muted one-time-scan note. ~3 lines of vertical footprint, 340px max
width; reduced motion drops the pulse; warm launches never see it.
2026-07-16 10:59:20 -07:00
Resham Joshi
ad4057a0dc
Merge pull request #695 from getagentseal/fix/cold-start
Desktop: cold-start fix — single warmup, incremental cache saves, first-run progress
2026-07-16 10:51:51 -07:00
iamtoruk
55bc53e6be fix(app): cold start survives, first-run shows per-provider progress
Root cause of the field-reported 45s timeout + perpetual slowness: on
a cold cache all six sections spawned different CLI subcommands at
once, each running its own full-history parse (~11-31s, ~3GB RSS),
contending past the 45s kill so the rebuilt cache never persisted and
every poll restarted from zero.

- the first overview fetch runs as a warmup: 10-minute timeout,
  re-arms until it succeeds, reverts to 45s after; section polls gate
  on that first resolution (usePolled gains enabled), so cold
  hydration happens exactly once
- warmup streams the CLI's progress protocol; the splash shows 'First
  run: indexing your usage history' with a per-provider ingest list
  (logo, live counts, check on done), static under reduced motion,
  generic fallback without events; warm launches unchanged
- overview spawns pass --no-timeline (desktop never renders it)

Measured on real data: cold 31s once (was: killed at 45s forever),
warm 2.6s. 295/295 app, 1797 root.
2026-07-16 10:51:18 -07:00
iamtoruk
ded4e4e844 feat(cli): scan progress protocol, incremental cache saves, --no-timeline
- CODEBURN_PROGRESS=1 emits newline-JSON progress on stderr (provider
  list, per-provider start/done, per-file ticks) for GUI first-run
  loaders; plain CLI output untouched
- parseAllSessions saves the session cache atomically every 5s during
  hydration and after each provider group, so an interrupted cold scan
  leaves a warm partial cache instead of nothing
- status --no-timeline skips the granular timeline computation
  (~300-450ms per call) for clients that never render it; default
  unchanged for the menubar
2026-07-16 10:51:18 -07:00
Resham Joshi
3163ccb5c4
fix(report): surface estimated costs distinctly (#688)
* fix(report): surface estimated costs distinctly

Providers that estimate tokens or price (kiro, cursor, warp, copilot,
grok, hermes, codewhale, and the codex proxy path) set costIsEstimated
on their parsed calls, but the flag died at the parser boundary: it was
never carried onto ParsedApiCall or into the session aggregates, so a
figure whose tokens were synthesized from content length rendered with
the same authority as a metered one.

Plumb the truth through, mirroring the savingsUSD/isLocalSavings pattern:
a call-level boolean (ParsedApiCall.isEstimated, CachedCall.isEstimated,
persisted so it survives the session-cache round trip) and an additive
aggregate amount (estimatedCostUSD on the model breakdown, session, and
project totals, plus PeriodData and the menubar payload). The amount is
carried rather than a bare boolean so a row that is mostly metered with a
small estimated slice is not indistinguishable from a fully guessed one.

Display: report (TUI) and overview per-model rows prefix the cost with a
tilde and print one legend line; the MCP tables carry the same marker and
legend, and the machine surfaces (report --json, MCP get_usage, menubar /
web payload) expose estimatedCostUSD. Totals math is unchanged; the flag
is display/metadata only.

Bump PROVIDER_PARSE_VERSIONS for every provider that sets the flag so
already-cached sessions reparse once and pick it up. Copilot is excluded:
it is a durable provider, so changing its env fingerprint would discard
OTel cache entries whose source rows may already be pruned.

Also fix the cross-provider project merge, which summed totalCostUSD but
dropped merged-in projects' totalEstimatedCostUSD, undercounting the
project/period estimated total (the same latent gap still affects
totalSavingsUSD, left untouched here).

* test(parser): pin estimated dollars through the cross-provider merge

The merge fix for dropped totalEstimatedCostUSD was not covered: deleting
the summing line left every test green. Extract the merge into an exported
mergeProjectsByCrossProviderKey (no behavior change) and pin both the
measured-plus-estimated and both-estimated merge cases.

* docs(parser): honest merge-comment scope and load-bearing overwrite note

Re-review nits: the merge doc claimed all additive totals are summed there
while totalSavingsUSD still is not (pre-existing gap, tracked separately)
and totalProxiedCostUSD is re-derived post-merge; say so. Mark the
buildPeriodData overwrite in usage-aggregator as load-bearing for the
estimated marker so nobody optimizes it away trusting the daily cache.
2026-07-16 10:46:21 -07:00
Resham Joshi
3dd1d1c793
feat(doctor): per-provider detection diagnostics (#685)
* feat(doctor): add per-provider detection diagnostics command

When a provider reports zero (or a number that looks wrong) there was no way
to see why: whether the tool is not installed, an env override points at the
wrong directory, the data dir is empty, or parsing failed. Users had to file
issues like "OpenCode does not work" with nothing to go on.

`codeburn doctor` closes that gap. For every provider (or one via --provider)
it shows the exact directories/dbs probed with any env override and whether the
path exists, how many session files were discovered, how many of a bounded
sample parsed cleanly, the cached file count and parser version, and a one-line
verdict: OK (n sessions), NOTHING FOUND with a concrete likely cause, or
ERRORS (n parse failures). Output is a human table by default or --json.

The collect logic is a pure function separated from rendering so tests exercise
it without a TTY. It runs fully offline and read-only (never writes caches or
config, and skips the one network provider's parse), and isolates each provider
in its own try/catch so a single thrower becomes an error row instead of
crashing the report. Providers expose their scan roots via an optional
probeRoots() so the exact paths are shown even when zero sessions are found,
reusing each provider's own path resolution rather than duplicating it.

* fix(doctor): make the inert promise actually true

Adversarial re-review reproduced two false guarantees. Cursor's parser
writes its results cache to disk before its first yield, so a doctor run
created ~/.cache/codeburn/cursor-results.json on a clean machine; the
collect pass now sets CODEBURN_SUPPRESS_CACHE_WRITES (restored after) and
the cursor writer honors it. Antigravity's parse probes for a live
language server (spawns ps and lsof, RPCs the IDE when found); doctor now
skips its parse sample the way network providers are skipped, keeping
discovery counts meaningful.

Also stop blaming CODEBURN_CACHE_DIR in NOTHING FOUND hints (it is our
cache location, not a discovery path) and correct the sample-cap comment:
the cap truncates yields, eager parsers still do whole-file work.
2026-07-16 10:38:08 -07:00
Resham Joshi
98051b4263
fix(copilot): bill CLI input and cache tokens from session.shutdown (#684)
* fix(copilot): bill CLI input and cache tokens from session.shutdown

Copilot CLI sessions (~/.copilot/session-state/*/events.jsonl) recorded
only outputTokens, because the parser read assistant.message events and
ignored the session.shutdown rollup. That rollup is the sole on-disk
source of a CLI session's input, cache-read and cache-write tokens, so
cost was underreported (in the reported case 35k+ cache-read tokens were
dropped entirely).

Handle session.shutdown in the CLI (non-transcript) JSONL path and emit
one supplementary call per model from modelMetrics.<model>.usage. The
per-turn assistant.message events keep owning output tokens, tools and
turn structure; the shutdown call contributes only the input and cache
tokens they lack, with output excluded, so no dimension double-counts and
the combined cost equals the full CLI-measured cost. usage.inputTokens is
cache-inclusive (input + cache_read + cache_write), so the cache
components are subtracted to recover the uncached input calculateCost
expects. These are real counts written by the CLI, so costIsEstimated is
false. Sessions without session.shutdown (crashed or still running) keep
the exact previous output-only behavior, and the VS Code, JetBrains and
OTel paths are untouched.

Bump the copilot entry in PROVIDER_PARSE_VERSIONS so already-cached CLI
sessions re-parse and pick up the corrected tokens and cost.

* fix(copilot): never emit an empty-timestamp shutdown call

A session.shutdown without its own timestamp and without sessionStartTime
produced a call with an empty timestamp, which the date-range filters in
parser.ts drop silently, erasing exactly the tokens this feature bills.
Fall back to the last stamped event in the session.

* fix(cache): durable orphans survive provider fingerprint changes

Bumping a durable provider's PROVIDER_PARSE_VERSIONS changed its env
fingerprint, and getOrCreateProviderSection replaced the whole section on
mismatch. For copilot OTel orphans the discarded cache entry is the only
remaining record of pruned spans, so the first post-upgrade run silently
and permanently erased month-to-date history. Carry forward exactly the
entries whose source no longer exists; present sources still drop and
re-parse under the new fingerprint, which is the point of a bump.

Regression test forges a pre-upgrade fingerprint on a pruned-source
orphan and asserts it stays counted and cached across the transition.
2026-07-16 10:37:58 -07:00
Resham Joshi
a2e95b2310
Merge pull request #686 from getagentseal/fix/provider-visibility
Desktop: bundled CLI, provider logos, provider visibility
2026-07-16 09:28:44 -07:00
iamtoruk
4a100d9d06 feat(app): bundle the CLI inside the packaged app
The packaged app resolved whatever codeburn was on PATH — for real
users the published npm release, which predates every JSON surface the
app calls. The app now ships its own CLI copy under resources/cli
(staged production node_modules tree; tsup output is not
self-contained) and spawns it with Electron's own binary via
ELECTRON_RUN_AS_NODE. No install prerequisite remains.

- resolution order: CODEBURN_BIN, dev repo CLI, bundled, persisted
  path, PATH search
- launch.js shim strips the extra argv element commander mis-slices
  under packaged Electron-as-node (process.versions.electron is set),
  which reproduced the 'too many arguments' class of error
- afterPack hook copies the staged tree (extraResources runs
  node_modules through the production-dep filter and ships it empty);
  lands before signing, signature verified intact
- packaging always restages from src via root build:cli (tsup only,
  no network); ~12MB compressed per artifact

Verified on the built app: Electron-as-node CLI emits current JSON
(providerDetails, currency), and a minimal-PATH GUI launch spawns the
bundled CLI with zero external dependencies. 290/290.
2026-07-16 09:27:46 -07:00
iamtoruk
61dddf57a0 fix(app): models rows name their provider; picker shows all detected
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.
2026-07-16 09:27:46 -07:00
iamtoruk
28d294cde5 fix(app): provider logos key on internal ids + monogram fallback
Settings Providers pane keyed logos by lowercased display names
('grok build' never matched THEMED_LOGOS 'grok'); rows now come from
providerDetails ids like the rest of the app. 20 official marks added
from the repo's own assets (hermes, openclaw, antigravity, kiro, devin,
cline, roo-code, ...) and unknown providers render a first-letter
monogram badge so no detected provider is ever logoless.
2026-07-16 09:27:46 -07:00
Resham Joshi
48a924181f
Merge pull request #683 from getagentseal/fix/packaged-app-node-path
fix: packaged desktop app cannot find node to run the CLI
2026-07-16 08:40:18 -07:00
iamtoruk
700a52cd95 fix(app): packaged app finds node when spawning the CLI
GUI-launched apps inherit a minimal PATH without the user's node
install, so spawning the codeburn npm shim (#!/usr/bin/env node) failed
with 'env: node: No such file or directory' in packaged builds. Spawn
now augments PATH with the resolved binary's own directory (node sits
beside the shim in nvm, Homebrew, and npm-prefix layouts) plus the
resolver's search dirs. Dev never hit this because the terminal PATH
was inherited.
2026-07-16 08:39:30 -07:00
Resham Joshi
4abf5050b4
Merge pull request #682 from getagentseal/docs/macos-first-open
docs: macOS first-open is xattr, not right-click Open
2026-07-16 08:33:07 -07:00
iamtoruk
b5ceee93c6 docs(app): macOS first-open is xattr, not right-click Open; fix exe filename
Field-verified on macOS 15+: an ad-hoc-signed quarantined download gets
the damaged/move-to-Trash dialog and right-click Open does not bypass
it; the quarantine strip is the reliable path. Also updates the
Releases section to the hyphenated CodeBurn-Setup-<version>.exe
artifact name set via nsis.artifactName.
2026-07-16 08:32:25 -07:00
Resham Joshi
5869cad545
Merge pull request #680 from getagentseal/feat/desktop-win-linux-packaging
Desktop: Windows and Linux packaging targets
2026-07-16 08:06:44 -07:00
iamtoruk
69c1946c1c feat(app): Windows NSIS and Linux AppImage packaging targets
Cross-built from macOS alongside the existing mac config: nsis x64
(oneClick false, clean CodeBurn-Setup-<version>.exe artifact name) and
AppImage x64. DISTRIBUTION.md covers the new build commands, the
unsigned-build first-run steps per platform, and the desktop-v<version>
release tag convention the website pins.
2026-07-16 08:06:03 -07:00
Resham Joshi
b7e3bac263
Merge pull request #679 from getagentseal/feat/desktop-app
CodeBurn Desktop: standalone Electron app
2026-07-16 07:54:05 -07:00
iamtoruk
1b600d1c82 Merge origin/main into feat/desktop-app
Resolves menubar-json conflicts: main's granular-history timeline
param unions cleanly with this branch's providerDetails and currency
payload additions; both new tests kept.
2026-07-16 07:52:28 -07:00
kronos
68ad0be18b
fix(antigravity): stamp mtime fallback at emission, tighten path classification (#612)
* feat(antigravity): add support for Antigravity IDE storage on Windows

CodeBurn previously only detected Antigravity CLI usage (.pb files under
.gemini/antigravity/). Antigravity IDE on Windows stores session state in
VSCode-style storage at %APPDATA%\Antigravity IDE\User\globalStorage\state.vscdb,
which was not detected.

Add support for reading Antigravity IDE sessions from the VSCode-style storage:
- Extend CONVERSATION_ROOTS to include APPDATA Antigravity IDE path
- Refine path classification to properly identify IDE vs CLI sessions
- Handle missing per-call timestamps by stamping file mtime as fallback
- Bump CACHE_VERSION to 4 for cache invalidation

Fixes: CodeBurn reports zero usage when Antigravity IDE is actively used.

* fix(antigravity): stamp mtime fallback at emission, tighten path classification

- Apply the mtime fallback to a copy at emission instead of mutating and
  persisting it into the cache, so a later file rewrite can't retro-date a
  session's history to the new mtime. SQLite gen_metadata rows have no real
  per-call time; the RPC path still uses chatStartMetadata.createdAt.
- Drop the unreachable APPDATA classifier branch (discovery only walks the
  ~/.gemini roots; APPDATA state.vscdb holds no token usage). This also removes
  the misroute of base-antigravity paths under an "Antigravity IDE" profile dir.
- Bump CACHE_VERSION to invalidate caches that persisted a synthesized mtime.

* fix(antigravity): stabilize untimestamped call times across DB rewrites

Extract ChatStartMetadata.created_at from proto-encoded data and decode multiple timestamp formats (ISO string, Timestamp submessage, unix varint). Implement assignStableTimestamps() to preserve first-seen timestamps across file rewrites, preventing retro-dating of sessions when .db files are modified. Make conversation roots dynamic (computed per call) to honor environment overrides in tests. Add comprehensive timestamp stability tests verifying that timestamps remain fixed across file mtime changes while respecting date-range filters.

* test(antigravity): assert today range excludes first-seen timestamps

Adds a test case to verify that the 'today' date range filter correctly excludes sessions based on their first-seen timestamp, not file modification time. This ensures that even if a database file is rewritten with a later mtime, sessions with earlier first-seen dates are properly filtered out.

Refs #411
2026-07-16 07:49:54 -07:00
Resham Joshi
4307542b62
feat(claude): account for /advisor escalations under the advisor model (#659)
* feat(claude): account for /advisor escalations under the advisor model

Claude Code's advisor tool (/advisor) escalates hard decisions mid-turn
to a stronger model. Those tokens are recorded inside
message.usage.iterations as advisor_message entries with their own model
and are excluded from the top-level message.usage totals. The parser read
only the top-level totals, so advisor spend was silently dropped and never
attributed to the advisor model.

Emit a separate call per advisor_message iteration priced under the
advisor model, preserve those iterations through entry compaction and the
large-line (>32KB) byte-scanner path, and fold advisor cost into the guard
budget. Bump the claude parser and guard cache versions so existing caches
re-parse and pick up the advisor spend.

* test(guard): pin advisor cost fold in the live budget

The guard's advisor fold in computeSessionUsage had no test; dropping
advisorCost from the line cost left all 30 guard tests green. Pins the
fold (advisor line costs more than the same line without iterations)
and the per-message replace semantics under streaming rewrites.
2026-07-16 07:42:49 -07:00
iamtoruk
3ea43ea5a5 chore(app): version tracks the CLI (0.9.15)
One CodeBurn version across CLI, menubar, and desktop; splash, About,
and artifact names all read app/package.json. Documented in
DISTRIBUTION.md.
2026-07-16 07:30:49 -07:00
iamtoruk
5ceaf6bd52 docs(app): DISTRIBUTION.md states the codeburn CLI prerequisite
A packaged DMG on a machine without the CLI opens into the 'CLI not
found' state; the distribution doc now says so up front, with the
install command and the future bundle-the-CLI path.
2026-07-16 07:21:22 -07:00
iamtoruk
2c66601b47 feat(app): package the desktop app for unsigned macOS distribution
electron-builder config with ad-hoc signing (identity "-", not null,
which skips signing and breaks Apple Silicon), dmg+zip for arm64 and
x64, org.agentseal.codeburn-desktop bundle id matching the menubar
convention, icon reused from build/icon.png. DISTRIBUTION.md documents
the build, verification, the Gatekeeper first-open steps for
unnotarized builds, and the later paid-notarization upgrade path.
2026-07-16 07:16:34 -07:00
iamtoruk
9517d63ba3 fix(app): loader video floats with real alpha (no background)
The h264 loader baked a flat dark background that read as a rectangle
against the splash's warm radial tint. Re-encoded from the original as
VP9 WebM with an alpha channel (checker keyed to transparency), so the
burning flame composites directly onto the gradient. Verified over a
contrasting test canvas.

273/273, typecheck + build green.
2026-07-16 06:22:32 -07:00
iamtoruk
8d1a10369f fix(app): quota connects via macOS keychain + Connect affordance
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.
2026-07-16 06:20:44 -07:00
iamtoruk
000e39a034 feat(app): binary-flame brand art + animated video loader on the splash
New owner-approved art: the binary-digit flame. Checkerboard keyed out,
largest-component masking to drop glow-zone checker fringe, clean
synthetic ember glow rebuilt; composited to the Big Sur squircle as
app/build/icon.png and squared with alpha as the in-app FlameMark
raster (sidebar, About, static splash).

The supplied burn animation becomes the splash loader: checker keyed
per-frame and the splash's fixed dark canvas (#171310) baked in (h264
has no alpha), audio stripped, 944KB looping video shown while the
first scan runs. Splash canvas and text colors are now theme-fixed so
video and page never diverge; reduced motion/tests keep the static
mark, verified by test.

Flame/splash/motion suites green in isolation (full suite re-verified
when the in-flight quota batch commits); typecheck + build green.
2026-07-16 06:19:12 -07:00
iamtoruk
38eef9b298 chore(app): drop superseded icon.svg trace source
The raster master (app/build/icon.png) from the approved art is now
canonical; the hand-traced SVG would only drift from it.
2026-07-16 06:10:54 -07:00
iamtoruk
0fa202b110 feat(app): exact approved flame art everywhere
The owner supplied the final flame render; the hand-retrace is retired.
Checkerboard keyed out to real alpha, composited to the Big Sur squircle
(824/1024, r185, charcoal #231613) as app/build/icon.png (the
electron-builder master, verified legible at 16px), and the flame-only
alpha PNG becomes the in-app mark: FlameMark now renders the raster so
splash, sidebar, About and dock are pixel-identical. Flicker applies to
the whole mark (raster has no separable core). Stale icon.svg removed.

Flame suites green in isolation; full suite re-verified before the
in-flight quota batch commits.
2026-07-16 06:10:22 -07:00
iamtoruk
3a0f4b2fb9 feat(app): retraced brand flame — one mark for icon, splash, sidebar, About
- app/build/icon.svg + icon.png: hand-retraced vector of the approved
  reference (three flat layers: coral #e86c39, amber #f7943c, cream
  #fecb8b on charcoal #231613), Big Sur squircle proportions (824/1024,
  r185, transparent corners, no glow), flame ~72% optically centered;
  8 render-and-compare iterations against the reference; legible at 16px
- FlameMark retraced to the same three paths (flat fills, no gradients);
  flicker now animates the cream core only
- AboutModal renders FlameMark instead of the old gradient flame.png;
  orphaned 70KB asset removed

253/253, typecheck + build green.
2026-07-16 05:54:40 -07:00
iamtoruk
1320807de2 polish(app): splash wordmark in brand orange + version line
CodeBurn wordmark on the splash now uses --accent (matching the
website/sidebar brand treatment) with the app version (v0.1.0, from
package.json at build time) in muted small text below. App version
bumped 0.0.0 -> 0.1.0.

253/253, build green.
2026-07-16 05:15:35 -07:00
iamtoruk
4b1426eab2 polish(app): consistency review nits
Loading quota label matches sibling skeleton labels (Sentence case);
matchMedia test stub deduped into lib/testMatchMedia.

253/253, typecheck + build green.
2026-07-16 05:11:10 -07:00
iamtoruk
cee0f3501d feat(app): Plans settings detect subscriptions automatically
Detected subscriptions card (Claude/Codex): auto-detected tier from the
live quota with a connect hint when logged out — like the menubar,
never a fixed preset. Manual budget presets now only for providers
without OAuth quota (Cursor/SuperGrok); configured claude/codex manual
plans stay listed for removal with a superseded note. The Plans
dashboard already excludes them next to the authoritative live quota.
2026-07-16 05:11:10 -07:00
iamtoruk
0f836db72f fix(app): flame mark is the real menubar flame
Replaces the rejected hand-traced silhouette with Bootstrap Icons'
fire path (MIT, embedded with license note) — the flame.fill-style
teardrop with the inner tongue — filled with the menubar BurnFlame's
actual Ember gradient from the Swift theme (#f0a070 > #e8774a >
#c9521d > #8b3e13, bottom to top), not the brighter accent ramp.
Flicker hook moves to the whole mark (single compound path).
Also: .splash-word snapped to var(--fs-kpi).
2026-07-16 05:11:10 -07:00
iamtoruk
76bc80ca67 polish(app): skeleton loaders for Compare and the Audit lens
The motion batch gave five sections shimmer skeletons on first load but
missed Compare and Models' Audit lens (both still showed bare loading
text in an empty panel). Same SectionSkeleton treatment now.

250/250.
2026-07-16 05:04:50 -07:00
iamtoruk
6c548c9883 fix(app): fallback provider ids must round-trip as --provider
Old-CLI fallback derived picker ids from providers-map keys (lowercased
display names); a key with a space ('grok build') passed to --provider
was rejected by the app's own validator, erroring the fetch and leaving
stale data under the new filter's header. Exclude keys that cannot
round-trip. (Primary fix is running the current CLI: rebuilt dist.)
2026-07-16 05:01:11 -07:00
iamtoruk
77f791376a feat(app): flame splash startup loader + living sidebar flame
- FlameMark: hand-traced inline SVG of the brand flame (~1.1KB, warm
  3-stop gradients, useId-scoped), used at splash and sidebar sizes
- Splash: full-window first-load overlay echoing the menubar's
  BurnLoadingOverlay ignite moment; 600ms floor so warm cache never
  blinks, 250ms crossfade on first data, instant yield on error or
  reduced motion, done-latch so it never reappears
- Sidebar logo replaced with the live FlameMark: 1-2% inner-tongue
  flicker, 4.5s period, random phase; static under reduced motion

250/250, build green (+2.4KB JS, +1.8KB CSS).
2026-07-16 04:51:30 -07:00