Skip to content

fix: Timeline chart reflects actual usage patterns (Issue #58)#70

Open
GalDayan wants to merge 2 commits into
mainfrom
issue-58
Open

fix: Timeline chart reflects actual usage patterns (Issue #58)#70
GalDayan wants to merge 2 commits into
mainfrom
issue-58

Conversation

@GalDayan
Copy link
Copy Markdown
Contributor

Problem

The "Total Usage Over Time" chart was showing spikes at the wrong hours because all session cost was bucketed into the session start time, even for long-running sessions.

Example: A session started at 05:00 with heavy API usage from 10:00-12:00 would show all cost at 05:00, creating misleading spikes.

Root Cause

  • Analytics aggregated by startedAt timestamp only
  • Multi-hour sessions had all cost attributed to one bucket
  • Chart showed when sessions started, not when work happened

Solution

✅ Added per-message cost tracking (CostPoint interface)
✅ Created listSessionsForAnalytics() that parses message timestamps from JSONL
✅ Updated aggregation to distribute cost across actual message times
✅ Backward compatible: falls back to startedAt for sessions without cost points
✅ Sessions counted once per bucket to avoid inflated sessionCount

Impact

Charts now accurately show:

  • ⏰ Spikes appear when work actually happened (e.g., 10:00-12:00 local time)
  • 📊 Multi-hour sessions distribute cost across their actual runtime
  • 🌍 Timezone handling preserved (UTC storage, local display)

Testing

  • TypeScript compilation: ✅ clean build
  • Backward compatibility: ✅ sessions without cost points fall back to startedAt
  • Performance: Minimal impact (cost points collected during existing JSONL parse)

Closes #58

Fixes #58 - Timeline chart now accurately reflects when work actually happened.

## Problem
Analytics bucketed ALL session cost into the session start time, even for
long-running sessions. A session started at 05:00 with heavy work at 10:00-12:00
showed all cost at 05:00, creating misleading spikes at wrong hours.

## Solution
- Added CostPoint interface to track per-message cost and timestamp
- Created listSessionsForAnalytics() that parses cost points from JSONL files
- Updated analytics aggregation to distribute cost across actual message times
- Falls back to startedAt for sessions without cost points (backward compat)
- Sessions counted once per bucket to avoid inflated sessionCount

## Impact
Charts now show accurate usage patterns:
- Spikes appear when work actually happened (e.g., 10:00-12:00 local time)
- Multi-hour sessions distribute cost across their actual runtime
- Timezone handling preserved (UTC storage, local display)
@GalDayan GalDayan self-assigned this Mar 18, 2026
Copilot AI review requested due to automatic review settings March 18, 2026 20:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes misleading “Total Usage Over Time” spikes by attributing usage to the actual message timestamps (when work happened) rather than the session startedAt.

Changes:

  • Added per-message cost/token time series (costPoints) collection during JSONL parsing for analytics.
  • Introduced listSessionsForAnalytics() to load session summaries with costPoints (separate cache key).
  • Updated /api/analytics aggregation to bucket cost/tokens by costPoints timestamps and prevent sessionCount inflation per bucket.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
backend/src/sessions.ts Adds CostPoint + optional costPoints on SessionSummary, and an analytics-specific session listing that collects per-message cost points.
backend/src/routes.ts Switches analytics endpoint to use cost points for bucketing and adjusts session counting to avoid double-counting within a bucket.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread backend/src/routes.ts
Comment on lines 795 to 801
const from = req.query.from as string | undefined;
const to = req.query.to as string | undefined;

let sessions = await listSessions(profile);
let sessions = await listSessionsForAnalytics(profile);

// For hourly view, default to last 3 days if no from specified
let effectiveFrom = from;
Comment thread backend/src/sessions.ts
Comment on lines +332 to +339
// Collect cost points for analytics time bucketing
if (collectCostPoints && msgCost > 0 && ts) {
costPoints.push({
timestamp: ts,
costUsd: msgCost,
tokenCount: msgTokens,
});
}
…e zero-cost token points

Addresses PR #70 review comments:
- Filter sessions by lastActivityAt instead of startedAt so long-running
  sessions with activity inside the date range are included
- Use epoch ms comparison instead of string ordering to avoid ISO format bugs
- Collect cost points when tokens > 0 even if cost is 0, so cached/discounted
  usage is represented in analytics bucketing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Timeline data does not accurately reflect actual usage patterns

2 participants