Repository navigation
feat(charts): session and project analytics charts with compaction tracking (schema v7) - #40
Merged
Merged
Conversation
…, per-turn Capture context compactions in the analyzer (system/compact_boundary events with trigger + preTokens, falling back to isCompactSummary prompts for older Claude Code files, deduping the boundary→summary pair) and expose them as SessionAnalysis.compactions. Add src/core/chart-series.ts, a bun-free module of session chart series builders shared by both frontends: context-window tokens per main-chain API call (the compaction sawtooth), cumulative cost with a sidechain split, and per-turn cost/tokens/calls. Web: new per-session Charts tab (context chart with dashed compaction markers, cumulative cost, per-turn bars with a metric toggle) plus a compactions row in the summary; Seg extracted for reuse. TUI: new charts mode in the session detail screen (braille context chart with a ▼ marker row, cost-per-call and per-turn sparklines) and a compactions summary line. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq
…ext markers Research against real-world session parsers confirmed compact_boundary events also occur inside subagent transcripts (isSidechain: true) — a subagent's compaction compacts its own context window, not the main chain's. Capture the flag on Compaction records, pair boundary→summary per chain kind, skip sidechain compactions when placing context-chart markers, and label them separately in the web and TUI summaries. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq
…hema v7) Slice 2 — project charts: spendByDay/modelMixByDay/sessionScatter/ costDistribution take an optional projectId; projectTrends() bundles them with the new per-project toolUsage() and turnDepthStats() for /api/projects/:id/trends (memoized per project against the index fingerprint). The web project page gains burn (metric + granularity toggles), model mix, cost/duration scatter via chart components extracted from Trends into trend-charts.tsx; the TUI project preview gains a weekly burn sparkline computed live from the index. Slice 3 — distributions: per-project session-cost histogram, turn-depth buckets, and tool mix on the web project page; compact distribution ramps in the TUI project preview. Slice 4 — compactions in the index (schema v7): the analyzer flags boundaries that precede any API call as inherited (continuation files copy the parent's final boundary); the index stores compactions_json plus a compactions INT column counting only own main-chain compactions, so one compaction never counts in two session rows. compactionUsage() rolls up portfolio pressure (auto/manual/subagent/inherited split + per-project share) for /api/analytics, rendered in the web Tools view; project rows and the TUI preview carry the summed count. Existing v6 indexes are dropped and rebuilt on open, as with every schema bump. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq
…and chart series Correctness: - buildBurnSeries no longer mixes epoch-ms with array indices when sorting; a timestamp-less call inherits its predecessor's timestamp and keeps its stored position instead of jumping to the front of the burn series. - The boundary→summary compaction pair now only closes on an assistant line of the same chain kind, so a subagent streaming while the main chain compacts no longer records the compaction twice. - One canonical own-compaction rule (isOwnCompaction/summarizeCompactions in chart-series.ts) is now used by the indexer, the rollup, the markers, and both frontends' labels — inherited continuation-file boundaries no longer count as the session's own, and the false '(some without timestamps)' caption is gone; inherited/subagent records are labeled explicitly. - compactionUsage reports trigger-less legacy compactions as 'unknown' instead of inflating 'auto', and is single-sourced from compactions_json. - The web Tools compaction empty-state accounts for inherited-only portfolios; the TUI project preview no longer hides distribution ramps for single-week projects. - /api/projects/:id/trends 404s unknown ids before touching the memo cache, keeping its keyspace bounded by real projects. - Machine-written compact summaries no longer open phantom turns (isRealPrompt excludes isCompactSummary); transcripts render them as labeled system items. Reuse/simplification: - bucketSeries/metricValue/weeklySeries hoisted into bun-free stats-types.ts and shared by the TUI and the SPA. - One SVG geometry kit (xScale/linePath/areaPath, shared constants) in trend-charts.tsx consumed by LineChart, SessionCharts, and SkillSpark. - toolUsage/turnDepthStats and analyticsRollup now share row-fold helpers. - markerRow arithmetic simplified; derivable index fields dropped from chart-series points; BurnPanel/ScatterPanel memoized; context markers placed by one cursor pass instead of repeated findIndex. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq
yorch
marked this pull request as ready for review
July 23, 2026 13:43
yorch
added a commit
that referenced
this pull request
Jul 23, 2026
Release-prep for **v0.6.0** (minor), covering everything merged since v0.5.0: - **feat(charts):** session and project analytics charts with compaction tracking; index schema bumped to v7 (#40) Bumps `package.json` 0.5.0 → 0.6.0 so the binary's reported version matches the tag. After merge, tagging `v0.6.0` on `main` triggers the release workflow (5 platform binaries + `SHA256SUMS`). Note: the schema v7 change is a rebuild of the disposable local index cache — no user action needed beyond the automatic reindex. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
yorch
added a commit
that referenced
this pull request
Jul 23, 2026
…compaction uuid dedupe (schema v8) (#42) Follow-ups to #40, covering the five items left on the charts roadmap, plus a full 8-angle code-review/simplify pass applied on top. ## 1 — Context-window limit line `ModelPricing.maxInputTokens` (LiteLLM `max_input_tokens`; also added to the bundled snapshot — a 23-line diff, original number notation preserved) flows through `resolveModel` → `ModelUsage.contextLimit` → `ContextSeries.contextLimit`. **Both** frontends now scale the context chart to the window so headroom is the visible signal: the web draws a dashed limit line with "% of window" in header + tooltips, and the TUI braille chart takes the limit as its ceiling with the same percentage in its header — one shared `pctOfLimit()` in bun-free core for all label math. Safeguards from review: the limit is suppressed when the peak meaningfully exceeds it (a 1M-window variant priced by the 200k family-heuristic entry must not render "375% of window"), and the pricing cache is now **format-versioned** so a pre-upgrade cache (which lacks `maxInputTokens`) refreshes instead of silently hiding the feature for up to 7 days. ## 2 — Compacted-session ground truth (fixture + E2E) New `test/fixtures/compacted-session.jsonl` with the research-documented field shapes (`compact_boundary` + `compactMetadata`, adjacent `isCompactSummary` prompt, sidechain burst, real uuids/timestamps) and an end-to-end test across **parse → analyze → chart series → index → portfolio rollup**, including indexing a copied session file to prove the uuid dedupe. The final eyeball against a real `~/.claude` stays a local step. ## 3 — Deferred perf/structure items - `projectTrends()` folds its three JSON-blob series in **one pass** over the project's rows (model mix via a new shared fold); SQL aggregates stay in SQLite. The now-orphaned `toolUsage()` was deleted. - The TUI project preview is db-free: `projectPreviewStats()` is computed at the screen boundary (memoized on the stable `projectId`) and passed as plain props. - **Considered and not taken**: merging `/api/projects/:id/trends` into `/api/trends?project=` — the shapes genuinely differ and both derive from shared `stats-types` rows. ## 4 — Test hygiene `test/tui/{trends,insights,tools}.test.tsx` build rows through the shared `insertSession` helper. The review pass also extracted **`tempClaudeDir()`** (`test/helpers/claude-dir.ts`) and migrated all five copies of the temp-dir/env boilerplate onto it (`mkdtempSync`, leak-proof env restore). ## 5 — Cross-file compaction dedupe (schema v8) `Compaction` records carry the boundary event's `uuid`; `compactionUsage()` filters **every category** (own, subagent, inherited — a copied file duplicates them all) through a shared `dedupeCompactions()` before the canonical `summarizeCompactions()` split, with `ORDER BY path` for deterministic attribution. **Schema bumps to v8**: uuid is load-bearing in `compactions_json`, and without the forced rebuild the incremental indexer would leave v7 rows uuid-less and the dedupe would silently no-op on existing installs. `byProject.compactions` stays the raw SUM-able per-row column and is documented as such. ## Review pass (8 finder angles + verification, all confirmed findings applied) Fixed: missing schema bump (v8); un-versioned pricing cache; partial dedupe (own-only → all categories) + second trigger-classification site + nondeterministic scan order; TUI/web headroom-scaling divergence (`brailleChart` ceiling); ">100% of window" from heuristic limits; three duplicated percent formulas → `pctOfLimit`; duplicated per-point limit representation dropped; `bundled-pricing.json` notation churn reverted to a 23-line diff; orphaned `toolUsage()` deleted (CLAUDE.md updated); preview memo keyed on `projectId` string; uuid conditional-spread ceremony removed; fifth copy of the claude-dir test boilerplate extracted into a helper and all five migrated. ## Gates 293 tests across 34 files, `bun run lint`, both typechecks, and `bun run build:web` all green. **Note for reviewers**: schema v8 means the first `cc-analyzer index` after this lands rebuilds the index (established migration path; required for the uuid dedupe to work on existing installs). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq --------- Co-authored-by: Claude <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Session- and project-level charts across both frontends, with compaction detection end-to-end (parse → analyze → chart → index → rollup), plus a full multi-angle code-review/simplify pass applied.
feat: session charts + compaction capture
events.ts,analyze.ts):system/compact_boundaryevents recorded intoSessionAnalysis.compactionswithtrigger,preTokens,isSidechain,inherited;isCompactSummary-only files (older Claude Code) fall back to timestamp-only records. Boundary→summary pairs dedupe per chain kind, so a subagent streaming through a main-chain compaction can't double-count.chart-series.ts(new, bun-free likestats-types.ts): context-window fill per main-chain call (the sawtooth), cumulative burn with sidechain split (timestamp-less calls keep their stored position), per-turn series — plus the canonicalisOwnCompaction/summarizeCompactionssplit shared by every surface.c): braille context chart with▼marker row, cost-per-call and per-turn sparklines.isRealPromptexcludesisCompactSummary, per community parser guidance); transcripts render them as labeled system items.feat: project-scoped charts + distributions
spendByDay/modelMixByDay/sessionScatter/costDistributiontake an optionalprojectId(via one sharedprojectScope/scopedAllhelper);projectTrends()bundles the six chart series for/api/projects/:id/trends(per-project memo against the index fingerprint; unknown ids 404 so the cache keyspace stays bounded).toolUsage()/turnDepthStats()built on the same row-fold helpersanalyticsRollupuses, so portfolio and project surfaces can't drift.trend-charts.tsx(one SVG geometry kit for the whole SPA; Trends page refactored onto it).feat: compactions in the index (schema v7)
inherited— continuation files copy the parent's final boundary, so counting them would double-count one compaction across two rows.compactionsINT (own main-chain only) +compactions_jsondetail. v6 indexes drop and rebuild on open (standard migration path — firstcc-analyzer indexafter this re-parses everything).compactionUsage()→/api/analytics: auto/manual/unknown/subagent/inherited split (single-sourced from the JSON via the shared predicate) + per-project pressure table in the web Tools view; project rows and the TUI preview carry the summed count.fix: multi-angle review pass (8 finder angles + verification, all confirmed findings applied)
bucketSeries/metricValue/weeklySerieshoisted to bun-free core; one SVG geometry kit; shared tool/depth folds;markerRowsimplified; derivable index fields dropped;BurnPanel/ScatterPanelre-memoized; marker placement is one cursor pass.projectTrendsinto a single scan andcompactionUsageinto the rollup scan (endpoints are fingerprint-memoized; scans are indexed/filtered), merging/api/trends?project=(distribution/depth/tools are project-only extras), moving TUI preview queries out of render (sub-ms indexed lookups, memoized per highlight).Format assumptions validated against real-world sources
compact_boundaryshape (compactMetadata: { trigger, preTokens }): fsck.com deep dive, dev.to /compact write-up, vibe-replay.isCompactSummarysynthetic prompt immediately follows the boundary; parsers should skip it as machine-generated (fsck.com).input + cache_creation + cache_read— same formula as Claude Code's statuslineused_percentage, ccusage, ccstatusline (docs, ccusage#480).inheritedflag.Tests & gates
279 tests across 33 files (compaction capture incl. interleaving/inherited/aggregate mode, chart-series ordering + marker placement + canonical split, project-scoped rollups, v7 indexer roundtrip, API routes incl. the 404 guard, TUI/web render smoke tests, transcript labeling).
bun test,bun run lint, both typechecks, andbun run build:weball green.🤖 Generated with Claude Code
https://claude.ai/code/session_012FVf8TYr2EAWW3NjN3Pdbq