Repository navigation
Conversation
…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.
📊 Automated PR Analysis
SummaryFixes 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
Linked issues: #4761 Analyzed automatically by wshm · This is an automated analysis, not a human review. |
KuSh
left a comment
There was a problem hiding this comment.
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
groupByProjectand emit one row per month;--monthly --format jsonoutput 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
developpasses fmt, clippy andcargo test --all.
Next
Nothing — approving. Thanks for the clean split into refactor, fix and test.
Summary
rtk cc-economics --monthly, the default summary and the JSONtotalsnow sum the rows ccusage 18 emits per project for one month, instead of keeping only the last one.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_monthlyinsrc/analytics/cc_economics.rscallsset_ccusageonce per row on the same map entry, so each row overwrites the one before it. The default summary builds its totals from the samemerge_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::fetchalready sums rows that share a key (aggregate_weekly_periods, from #4409), but only in itsGranularity::Weeklybranch.Solution
refactor(analytics): renameaggregate_weekly_periodstoaggregate_periodsand pass the granularity its overflow errors name. Weekly errors read as before (ccusage weekly input tokens overflowand so on); the existing unit tests only change their call sites.fix(analytics): inccusage::fetch, runaggregate_periodson 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.test(analytics):duplicate_legacy_month_rows_sum_every_metricintests/cc_economics_weekly_test.rsrunsrtk cc-economics --monthly --format jsonagainst the file's ccusage stub, with the two project rows of the weekly duplicate test repeated under2026-09. It asserts a single monthly row, andtotals, 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.aggregate_weekly_periodscall site is left, and the weekly overflow messages the unit tests match (input tokens overflow,total cost overflow,active tokens overflow, ...) keep their text.rtk cc-economics --monthlynot run locally.The new test sits in a
#![cfg(unix)]file, like the weekly ones, so it runs on the Linux and macOS legs.