Activity chart: metric selector + composite & cognitive-load scores - #307
Open
brendangooden wants to merge 8 commits into
Open
Activity chart: metric selector + composite & cognitive-load scores#307brendangooden wants to merge 8 commits into
brendangooden wants to merge 8 commits into
Conversation
…vity chart Replace the fixed sessions/insights Activity chart with a metric selector and two new derived scores. Server (`/api/analytics/activity`): - New all-time, gap-filled per-day series (sessions, projects, messages, tool_calls, tokens, cost). Metric defs match the CLI dashboard (tokens = input+output+cache_creation+cache_read). - Cognitive load per day: a human turn keeps a session "warm" for 15 min; load at each minute = concurrent warm sessions x concurrent warm projects^2 (projects compound), integrated over active minutes. Human turns only (type='user' with non-empty content) so idle/autonomous sessions don't count. Also returns peak concurrent sessions/projects for the day. Dashboard: - Activity card now has a metric dropdown: Composite score, Cognitive load, Cost, Tokens, Tool calls, Messages, Projects, Sessions. - Composite = weighted blend (cost .30, tokens .25, tool_calls .20, messages .15, projects .10) on a FIXED all-time reference, so a day's score is stable across range changes; Weighted (area) vs Stacked (bars) toggle. - Single-series metrics render as a gradient area; stacked composite as bars. - Y-axis zooms to the min/max shown. Tooltip shows the full per-day breakdown plus an interpretable "Peak multitasking: N sessions across M projects" line. - New useActivity() hook, fetchActivity() client, ActivityDay type, and centralized per-metric colors.
Groups projects table rows into logical projects (name + normalized path, collapsing path-hash/git-remote duplicates and folding worktree paths back to their parent repo path), then computes a 60-day-inactivity state machine (active/reactivated/dropped) per ISO week since the first-ever session. Adds a shared mondayOfIsoWeek() helper to shared-aggregation.ts alongside the existing parseIsoWeek/formatIsoWeek for the weekly bucketing. No SQLite schema changes — reads existing projects and sessions tables only.
Additive types only (ProjectLifecycleWeek, ProjectLifecycleSummary, ProjectsLifecycleResponse) — no changes to existing Session/Project types.
Combo chart (stacked cumulative Area for active/reactivated/dropped + diverging Bar overlay for weekly started/dropped events) plus a sortable status table, matching DashboardActivityChart's Card/Recharts conventions.
Registers /projects after /analytics in App.tsx routing/titles and in Header.tsx's NAV_ITEMS (lands in the mobile 'More' sheet as the 5th item, bottom tab bar unaffected).
A worktree's raw project row carries its own auto-generated folder name (e.g. "keen-davinci-c306f0"), not its parent repo's name — path.basename(cwd) in cli/src/parser/jsonl.ts. Grouping by name+path therefore could never fold a worktree into its parent, contradicting the design intent. Group by normalized path alone instead. For the logical project's display name, prefer the most-active row whose name doesn't look machine-generated (the .claude/worktrees/<name> basename, or the adjective-noun-hex sandbox pattern used by tools like Lovable); fall back to the highest-session-count name only if every row in the group looks auto-generated. Verified against the real local DB: keen-davinci-c306f0 now correctly folds into ubt-sales-app (84 sessions, up from 18) instead of appearing as its own logical project. Total logical projects dropped from 80 to 67.
…ction Replace the adjective-noun-hex name-pattern heuristic with a precise path-containment check: prefer a raw row whose ORIGINAL path does not contain a .claude/worktrees/ segment (the parent repo's own row, if one was ever synced), falling back to highest-session-count only when every row in the group is a worktree path. Simpler and more reliable than fuzzy name matching, per team lead review. Verified against the real local DB: ubt-sales-app still correctly absorbs its keen-davinci-c306f0 worktree's sessions. Also confirmed ubt-tool-hub has no non-worktree row at all in the real data (every session there was recorded under one of 30+ randomly-generated worktree names) — added a synthetic test for the optimistic-mclean-1ce467/ubt-tool-hub pair since that exact scenario doesn't exist in the real DB to demo against.
…hart feat(dashboard): Projects lifecycle chart + status table
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.
What
Turns the dashboard Activity card from a fixed sessions/insights chart into a metric explorer, and adds two derived scores: a Composite activity score and a Cognitive load score.
Metric selector
A dropdown to plot any of: Composite score · Cognitive load · Cost · Tokens · Tool calls · Messages · Projects · Sessions, over 7d / 30d / 90d / All.
Composite score
Weighted blend — cost .30, tokens .25, tool calls .20, messages .15, projects .10 — on a fixed all-time reference, so a given day's score is stable when you change the range (only the y-axis zoom changes). Toggle between Weighted (single area) and Stacked (per-metric bars summing to the score).
Cognitive load (context-switching cost)
Volume metrics miss multitasking cost. This models it:
type='user'with non-empty content — not automated tool-result messages) keeps its session "warm" for 15 minutes.How
Server — new
GET /api/analytics/activityreturns an all-time, gap-filled per-day series. Metric definitions match the CLI dashboard (tokens = input+output+cache_creation+cache_read). Cognitive load is computed from a single ordered pass over human-turn message timestamps joined to their project.Dashboard — single-series metrics render as a gradient area, stacked composite as bars; y-axis zooms to the visible min/max; rich breakdown tooltip. Adds
useActivity()hook,fetchActivity()client,ActivityDaytype, and centralized per-metric colors. Reuses existing shadcnCard/ recharts / theme for visual consistency.Notes
sessions/messagestables.computeCognitiveLoad.