Skip to content

fix(analytics): sum per-project ccusage rows in monthly economics - #4771

Merged
KuSh merged 3 commits into
rtk-ai:developfrom
viniciusdsandrade:fix/4761-monthly-ccusage-project-rows
Nov 1, 2026
Merged

KuSh merged 3 commits into
rtk-ai:developfrom
viniciusdsandrade:fix/4761-monthly-ccusage-project-rows

Conversation

@viniciusdsandrade

Copy link
Copy Markdown
Contributor

Summary

Closes #4761

Context & Problem

With per-project grouping on ({"commands":{"monthly":{"groupByProject":true}}} in its config), ccusage 18 returns several rows for the same month, and none of them carries the project. merge_monthly in src/analytics/cc_economics.rs calls set_ccusage once per row on the same map entry, so each row overwrites the one before it. The default summary builds its totals from the same merge_monthly, so its spend and the cost per token derived from it are understated by the same amount. #4761 measured $27.58 reported for a month that cost $713.21.

ccusage::fetch already sums rows that share a key (aggregate_weekly_periods, from #4409), but only in its Granularity::Weekly branch.

Solution

  1. refactor(analytics): rename aggregate_weekly_periods to aggregate_periods and pass the granularity its overflow errors name. Weekly errors read as before (ccusage weekly input tokens overflow and so on); the existing unit tests only change their call sites.
  2. fix(analytics): in ccusage::fetch, run aggregate_periods on monthly reports as well. Daily keeps its current path: as cc-economics: monthly totals and the summary keep only the last per-project ccusage row #4761 notes, ccusage 18 emits one row per day there.
  3. test(analytics): duplicate_legacy_month_rows_sum_every_metric in tests/cc_economics_weekly_test.rs runs rtk cc-economics --monthly --format json against the file's ccusage stub, with the two project rows of the weekly duplicate test repeated under 2026-09. It asserts a single monthly row, and totals, carrying the summed counters and cost (408 input, 82 output, 3000 total tokens, $40) joined with the tracker's savings, plus the blended and active cost per token derived from them. Before the fix the row carries the second project only (307 input, $29).

Test plan

  • cargo fmt --all && cargo clippy --all-targets && cargo test: not run locally, left to the CI matrix.
  • Checked by reading: no aggregate_weekly_periods call site is left, and the weekly overflow messages the unit tests match (input tokens overflow, total cost overflow, active tokens overflow, ...) keep their text.
  • Manual testing: rtk cc-economics --monthly not run locally.

The new test sits in a #![cfg(unix)] file, like the weekly ones, so it runs on the Linux and macOS legs.

…rity

`aggregate_weekly_periods` sums the rows legacy ccusage emits per project
for one week. Monthly reports need the same sum (rtk-ai#4761), so rename it to
`aggregate_periods` and pass the granularity its overflow errors name.
Weekly errors read as before, and weekly behavior does not change.
With per-project grouping on (`"groupByProject": true` in its config),
ccusage 18 emits one monthly row per project, and no row carries the
project. `merge_monthly` calls `set_ccusage` once per row on the same
map entry, so each row overwrote the one before it:
`rtk cc-economics --monthly`, the default summary and the JSON `totals`
kept only the last project's spending, and the cost per token derived
from it.

Sum the monthly rows by month with `aggregate_periods`, as the weekly
report has done since rtk-ai#4409. Daily rows are left as they are: ccusage 18
emits one row per day there.
Run `rtk cc-economics --monthly --format json` against a ccusage stub
that repeats one month with the two project rows of the weekly
duplicate test. The monthly row and `totals`, which the default summary
prints, must carry the sum of both rows joined with the tracker's
savings. Before the fix they carried the second row only.
@rtk-wshm-sync-bot rtk-wshm-sync-bot Bot added analytics area:cli bug Something isn't working labels Oct 23, 2026
@rtk-wshm-sync-bot

Copy link
Copy Markdown

wshm · Automated triage by AI

📊 Automated PR Analysis

🐛 Type bug-fix
🟢 Risk low

Summary

Fixes a bug where rtk cc-economics --monthly undercounted spend when ccusage emits multiple per-project rows for the same month, since only the last row was kept instead of being summed. The fix generalizes the existing weekly row-aggregation helper (aggregate_weekly_periods → aggregate_periods) to also run for monthly reports, with a new test covering the summed metrics.

Review Checklist

  • Tests present
  • Breaking change
  • Docs updated

Linked issues: #4761


Analyzed automatically by wshm · This is an automated analysis, not a human review.

@KuSh KuSh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Round 1 — APPROVED

Claim: rtk cc-economics --monthly, the default summary and the JSON totals sum the per-project rows ccusage 18 emits for one month instead of keeping the last one.
Scope: accept.
Ran: ccusage 18.0.11, 19.0.3 and 20.0.36 on real Claude Code logs (Docker), with and without {"commands":{"monthly":{"groupByProject":true}}}, × --monthly --format json, --monthly --format csv, the default summary and --all --format json, develop merge-base vs this head. Invariants: every decision, non-default config, unchanged sections under --all.
Pins: 1 decision (monthly reports go through aggregate_periods), 2 mutations, 0 survived. Removing the monthly call, or pointing it at daily, fails duplicate_legacy_month_rows_sum_every_metric. Mutating the shared summing (cost and cache-read last-wins, active-token check dropped) fails the weekly and monthly tests.
CI @06bce4d36: all green.

Checked and correct — no need to re-verify

  • ccusage 18 with grouping emits 25 rows for 4 months (up to 17 for one month). The sum of those rows equals ccusage's own ungrouped row for every month, to the token. This head reports exactly that sum: 2026-09 $6,700.86 and 14,030,096 output tokens (develop: $104.18 and 280,540), 2026-10 $5,981.84 (develop: $26.33). The summary's spend goes from $903.82 to $13,456.02, and the CSV rows match the JSON.
  • ccusage 19 and 20 ignore groupByProject and emit one row per month; --monthly --format json output is byte-identical before and after.
  • Daily: ccusage 18 with grouping on for daily emits 68 rows, no repeated date, so leaving the daily path alone is right. Under --all, the daily and weekly sections are unchanged.
  • Weekly overflow messages read as before; the merge with current develop passes fmt, clippy and cargo test --all.

Next

Nothing — approving. Thanks for the clean split into refactor, fix and test.

@KuSh
KuSh merged commit ed0216f into rtk-ai:develop Nov 1, 2026
16 checks passed
@rtk-release-bot rtk-release-bot Bot mentioned this pull request Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

analytics area:cli bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cc-economics: monthly totals and the summary keep only the last per-project ccusage row

2 participants