Commit graph

5 commits

Author SHA1 Message Date
iamtoruk
3e48f8d073 fix(aggregator): overlayProviderDaySlices overwrote settled cache days with shrunken parses
The provider-scoped overlay blind-set a fresh slice for every date it could
produce one. A settled day's sources age off disk continuously, so that fresh
slice is a lower bound, not a correction, and the overlay replaced finalized
cache days with whatever the surviving files still explained. It ran over the
whole period and over the whole history, so the scoped view could report a
fraction of the same day the all-provider view served off the same cache.

The overlay now routes through mergeDayEntries with guardPartialSurvival:
fresh may FILL a (date, provider) the cache lacks and still wins inside the
settle window, but it can no longer SHRINK a settled day the cache already
holds. Filling has to keep working, because on a cold cache the scoped parse
is the only source a historical day has.

Measured on this machine over 2026-08-03..2026-09-01, comparing scoped
--provider claude against the claude slice of the unscoped run:

  before  $16649.42 / 81812 calls   (short by $291.70 and 2507 calls)
  after   $16941.12 / 84319 calls   (exact match, both fields)
2026-09-02 07:32:02 -07:00
ozymandiashh
bcf1155255 test: fix the five environment-sensitive failures the first ubuntu run exposed
- cli-durable-totals: the live fixture session was stamped at noon today,
  so every before-noon run saw it in the future; the provider-filtered
  path drops future instants while the all-provider path keeps the whole
  day, failing the parity assertion. Relative-and-clamped timestamps,
  the same fix project-filter-durable-totals got in 1596220.
- parser (copilot, 2 cases): the fixture's fixed 2026-05-01 dates crossed
  copilot's durable 90-day age-out on 2026-07-30, so the first parse
  pruned the freshly-cached session. Relative timestamps.
- parser-incremental-append: unlink-then-create let ext4 hand the freed
  inode straight back, breaking the new-inode premise. The replacement
  is now created beside the original and renamed over it.
- parser-proxy-pricing: normalizeProxyPath folds case only on darwin and
  win32, deliberately; the test now asserts the platform-correct
  behavior on both kinds of filesystem instead of hardcoding macOS.
- cli-status-menubar: the config-source filter case does real multi-parse
  work and needs more than the 5s default on shared runners; 30s cap.
2026-08-04 03:38:18 +03:00
AgentSeal
dbdbf466a0 fix(aggregator): attribute a straddling turn's category to today on the all-provider view
buildDurablePeriod derived the today slice of the multi-day, all-provider
headline from the unsliced whole-range parse, so a turn spanning local midnight
kept its category and turn count anchored on its yesterday start. The per-call
cost and calls bucketed onto today correctly, but By Activity and the JSON
daily turn count lost the post-midnight half — categories summed to only the
pre-midnight cost while the headline, By Model and By Project were right.

Slice the today parse with filterProjectsByDays first, which re-anchors the
straddling turn to its surviving today calls, so today's category cost lands on
today. Category cost is the sum of the slice's own calls, so day-N + day-N+1
still equals the whole-range total (no over-count); the per-day turn-count
split matches the cache side and the documented per-day semantics.

Adds a regression test in the straddling-turn conservation suite
(mutation-checked: fails on the pre-fix code). Also fills in the CHANGELOG
Unreleased entries for the batch (#853, #856, #872, #846/#859, #866/#867, #833).
2026-08-03 22:46:54 +02:00
KENSHI601
7591e68851 fix(parser): make day-spanning turn slices conservation-correct
Extends the per-call range filter so multi-day periods stop losing
usage (review on #857):

- Re-anchor a sliced turn's timestamp to its first surviving call in
  parseProviderSources, so every turn slicer shares one split rule.
- scanProjectDirs (Claude Code path): slice per call instead of
  dropping the whole turn on its first assistant timestamp; category/
  subCategory/retries/hasEdits stay classified from the full turn.
- aggregateProjectsIntoDays: bucket cost/calls/tokens (and the model,
  project, provider-slice rollups built from them) under each call's
  own day; turn-level stats (categories, editTurns, oneShotTurns) stay
  turn-anchored. This is the conservation fix: cache (<= yesterday) +
  live (today) unions now sum to the whole range for straddling turns.
- buildJsonReport's dailyMap fallback follows the same per-call rule
  so the no-durable path can't diverge from durable.days.
- filterProjectsByDateRange (dashboard) and filterProjectsByDays
  (menubar/history) slice per call instead of dropping whole turns.

Adds the straddling-turn buildDurablePeriod case to the durable-totals
parity suite (day-N + day-N+1 == whole-range calls/cost/tokens,
verified to fail without the fix), covers the today view and the
surface filters, and makes the suite hermetic on machines with real
provider data.
2026-07-31 00:37:20 +08:00
iamtoruk
dea61bad4e fix(report): CLI totals read the durable daily cache, matching the menubar exactly 2026-07-20 06:33:56 -07:00