codeburn/docs/design/desktop-data-lifecycle.md
2026-09-21 18:33:28 +02:00

105 lines
5.7 KiB
Markdown

# Desktop data lifecycle
Status: implementation contract for the quality/performance epic. This document
describes product behavior; it does not change the meaning of CodeBurn totals.
## User contract
CodeBurn is useful before historical classification is complete. On every launch:
1. Paint the last exact snapshot for the selected view immediately, when one
exists for the same period, provider, range, configuration and calendar
boundary.
2. Revalidate that snapshot in the background. Do not replace usable content
with a skeleton while refreshing.
3. On a true first run, make Today the first useful result. Continue historical
work in this order: Today, 7 days, 30 days, month, 6 months, lifetime.
4. Warm the first-click reports for the selected period only, once, and only
while the window is visible. Interactive requests always run ahead of that
queue, and a finished warm is kept across poll cycles rather than redone.
5. Stop polling while the window is hidden, and catch up immediately when it
comes back. Only the headline runs at the live cadence: `act report` refreshes
every 10 minutes, `yield` every 5, and the optimize scan at most once a day
per query scope, shown with the time it was computed. On battery the live
interval doubles.
Historical results are durable. A renderer restart is not a cache miss. Today
and Month snapshot identities include the local calendar boundary, so yesterday's
answer is never painted beneath today's label. Custom ranges are exact-keyed.
## Visible states and copy
| State | Content | Status copy |
| --- | --- | --- |
| Exact snapshot, refreshing | Keep the full snapshot visible | No banner: the hairline under the top bar lights and the icon beside `refreshed Ns ago` spins |
| Progressive first index | Show the exact indexed subset | `Indexing history · X/Y files · You can keep using CodeBurn; totals update as indexing completes.` |
| Source refresh failed | Keep indexed content visible | `Some sources could not be refreshed. Showing indexed data; recent activity may be missing.` |
| First run, no usable subset yet | Skeleton for that destination only | Specific work copy such as `Scanning sessions…` |
| Refresh failed after success | Keep last-good content | Existing stale/error affordance beside the content |
`X/X` is not indexing progress. A stale payload and an active progressive index
are different states even when both carry `hydration.complete = false`.
## Snapshot custody
Renderer snapshots contain report output only, never credentials. They are:
- versioned and generation-invalidated after pricing, currency, alias or plan
mutations;
- synchronously compressed before browser storage;
- bounded per entry and by a least-recently-written eviction cap;
- treated as stale-while-revalidate after every renderer restart;
- cleared when the snapshot generation is invalidated.
The compact Overview headline remains a separate fallback. It must never be
written under a newly selected period until that exact period resolves.
## Plans and Menu Bar capability contract
Provider visibility is not provider capability. The icon/catalog work may know
dozens of agent brands; a live quota card is allowed only when a tested adapter
exists.
The currently implemented live quota set is six providers on both surfaces:
Claude, Codex, Gemini, Copilot, Antigravity and Kimi. Desktop and Menu Bar use
the same underlying CLI/local sources. For CodeBurn-owned Keychain caches:
- Codex Desktop already reads the native Menu Bar cache
`org.agentseal.codeburn.menubar.codex.oauth.v1` / `default`.
- Claude Desktop reads the native Menu Bar cache
`org.agentseal.codeburn.menubar.claude.oauth.v1` / `default`, then falls back
to Claude Code's own credential store.
- Tokens never cross renderer IPC and are never included in durable snapshots.
The native app deliberately requires a user-initiated Connect before reading a
credential source that can raise a macOS Keychain prompt. Therefore "automatic
bidirectional authentication" is not a truthful claim yet: Desktop can consume
the native CodeBurn caches, while a first native import still requires that one
explicit consent action. Removing it requires a shared native credential broker
and a security/privacy decision; it must not be implemented by putting secrets
in process arguments, renderer storage or a new plaintext file.
## Architectural follow-up
This change fixes the user-visible lifecycle without replacing the aggregation
engine. The broader constraint remains: Electron orchestrates several expensive
report commands and stores their rendered results. The durable end-state is one
incremental local index with explicit coverage watermarks, followed by cheap
queries for period/provider/view slices. That work is justified when measurements
show background warming still consumes material CPU or a selected uncached query
misses the interaction budget; it is not required to preserve the snapshot UX.
## Acceptance gates
- Restart with a heavy corpus: selected last-known content paints without a
skeleton and a fresh request runs behind it.
- Leave the app covered longer than one configured cadence: return to a snapshot
no older than the cadence plus one request duration.
- First-run queue order is deterministic and background-priority; a click jumps
ahead of queued work.
- Today and Month never reuse a prior calendar-boundary snapshot.
- Every major destination has a warm snapshot after the background pass.
- Genuine progressive and stale-source payloads show different, accurate copy.
- No credential or bearer token appears in renderer storage or logs.
- Claude and Codex native Keychain cache reads are exact-service, read-only and
covered by malformed/access-denied/fallback tests.