You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
codeburn sessions --by-work-unit groups the sessions report into provider-recorded work units. The new deterministic resolver (src/work-units.ts) folds each session carrying the #1140lineage field under the orchestration root it names, giving one row per unit with the root's title and project, cost/calls summed over root plus children, and a child count, with the children listed indented beneath and standalone sessions rendered exactly as before. A work unit's id is deriveTraceId(rootSessionId), the exact trace-id derivation sync already uses, so a unit's identity matches the root's existing trace identity. Evidence stays strictly provider-recorded: a session without lineage is its own standalone unit with role unknown, a child that names an in-range parent folds even when the parent recorded nothing (one-sided evidence), and anything ambiguous fails closed per the boundary spec's MAIN-02 - a parent id outside the parsed window leaves the child ungrouped, cycles and self-references are broken and marked unknown, and duplicate or cross-provider id collisions never fold. --format json with the flag emits an add-only { sessions, workUnits } envelope: sessions is today's row array unchanged, workUnits is the resolver's full partition (standalone sessions included, so counts and totals reconcile). Default output without the flag is byte-identical, no wire/sync or daily-cache behavior changes, and a pinning test asserts the grouped footer total equals the ungrouped total. Implements slice CB-2 of the CodeBurn main/Teams boundary spec (CODEBURN-MAIN-TEAMS-BOUNDARY-v0.1). (#1140)
codeburn status --format menubar-json can serve from a disk-persisted snapshot. A background poll whose corpus has not changed since the last computation is answered from a per-query snapshot keyed on a corpus fingerprint (ordered source topology, per-provider env fingerprints, five config hashes, currency, pricing generation, cache and package versions - any mismatch is a miss), eliminating the per-poll re-parse on idle machines. A snapshot is only ever written for a complete, non-degraded parse: the save gate requires the payload's own captured markers (stale !== true, no hydration block), so a poll that went read-only under a held refresh lock can never pin its under-reported totals to disk - a regression holds the refresh lock and asserts no snapshot file is written. Serve-side and one-shot outputs are unchanged; the optimization currently engages for --no-optimize polls (the menubar background refresh). Thanks @dgabehar, hardening @ozymandiashh. (#999)
The menubar status snapshot now re-validates persisted records on load, mirroring the save gate. A snapshot record whose payload carries degradation markers (stale: true or a hydration block) is treated as a miss and recomputed instead of served, so a record that should never have been written (older build, hand-edited file) can never pin under-reported totals; the save gate from #999 is unchanged. Extending the snapshot to the default optimize path was evaluated on this branch and reverted in post-build review: scanAndDetect's findings depend on mutable state the corpus fingerprint never observes (~/.claude and project-level settings.json, CLAUDE.md, defined skills, agents, commands, MCP config), and re-deriving them on a snapshot hit requires the parsed corpus, which is the full parse the snapshot exists to avoid on exactly the cold processes it serves. The optimize path therefore still never reads or writes the disk snapshot, and one-shot and serve-child outputs are byte-identical to before for both optimize values. (#1135)
The parser captures provider-recorded session lineage instead of discarding it. Claude agent transcripts carry a provider-written parent session reference (and the parent side records the spawned agent id when compaction has not dropped it); Kimi Code subagent sessions live inside their parent session's own directory. Both now populate an optional lineage field on the parsed session model - parentSessionId, a root/child role, and evidence: 'provider-recorded' - persisted through the session cache, with claude and kimicode parse versions bumped for the one-time re-derive. Strictly provider-recorded evidence only: no inference from time adjacency or shared projects, and a session with no evidence carries no lineage field at all. Purely additive metadata: a pinning test asserts every report total is byte-identical with the field present or absent. Groundwork for spend attribution across delegated-agent families (a measured 5.1% of sessions carry direct lineage but 76% of 90-day spend). (#1140)
Fixed (macOS)
CodeBurnMenubar now launches mise-installed CodeBurn correctly from Spotlight. Spotlight gives GUI apps a minimal PATH, so a persisted mise use -g npm:codeburn launcher could be found but then fail with exit 127 when its shell wrapper tried to resolve node. The menubar child environment now includes mise's stable shim directory (including a custom MISE_DATA_DIR when available), matching the existing Volta/asdf/nvm handling without invoking a shell. (#1124)
Fixed (TUI)
The interactive dashboard stays responsive during the cold-start background fill, and Ctrl+C exits the TUI at any moment after paint. The progressive cold start introduced by #1109 paints the dashboard from recent files, then runs a background fill that parses the deferred history and writes the per-file session cache. On a real 21k-file corpus that fill's cache save publishes one JSON.stringify + fsync per provider shard in a tight loop, and the whole save held the event loop long enough that Ink's stdin reader never ran - a q or Ctrl+C pressed in the post-paint window was swallowed, not buffered, and the user saw a frozen terminal (#1139, #1141). saveCache now yields to the event loop between every shard write, so the Ink input handler fires between publishes. The yield sits BETWEEN shards, never inside one, so each shard's temp+rename atomicity and the lost-fence cleanup that owns the canonical envelope are preserved; nothing marked seen without being parsed, the kill-safe resume invariant from #1109 is intact, and the floored-parse memo key suffix keeps warm reads byte-identical. The fill still parses every deferred file, so money totals are unchanged. The dashboard's useInput now exits on Ctrl+C through the same path as q, so a raw 0x03 from the terminal lands as soon as the loop breathes. Pinning tests: tests/save-cache-yields.test.ts (a setImmediate probe must interleave with saveCache's shard writes) and tests/dashboard-exit.test.ts (a raw \x03 exits the dashboard the same way q does). The #1109 cold-start one-shots - report, sessions, models, json/csv/markdown, the desktop and menubar payloads - are unchanged.
The first q during the cold-start background fill renders feedback instead of a silent drain, and a second q exits the same way Ctrl+C does. With the #1142 input fix in place, q pressed during the post-paint fill landed instantly but the exit path drained the fill first - a deterministic ~16.5s silent wait on a 21k-file corpus (#1143). The fill's indexing signal already flows to the dashboard (the #1109 banner reuses it), so the useInput handler now arms a confirmation on the first q while a fill is active and renders Finishing background index so the next launch starts warm - press q or Ctrl+C again to quit now in the footer area, with the second q taking the abrupt path. Ctrl+C is unchanged: it always exits through the abrupt path, and because #1109 made that path kill-safe (nothing marked seen without being parsed, resume converges) the two are equivalent. When the fill completes, the confirmation clears itself so a later q cannot be trapped by a stale flag. q with no fill active exits immediately as before, with no status-line flicker. Pinning tests added to tests/dashboard-exit.test.ts.