Skip to content

feat(history): answer long windows from the union and take a range key - #92

Closed
Fl0p wants to merge 2 commits into
mainfrom
flo-591-history-range
Closed

feat(history): answer long windows from the union and take a range key#92
Fl0p wants to merge 2 commits into
mainfrom
flo-591-history-range

Conversation

@Fl0p

@Fl0p Fl0p commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What

/history was the last read endpoint outside the range contract: it took from/to and read raw spans only, so on year or all it charted a visibly shorter window than the Costs panel beside it. The Overview papered over that by borrowing the sessions list's covered_since and printing a note. This removes the need for that note wherever the union can genuinely cover the window.

Acceptance criteria

  1. range via parseRangeDefault, echoing the resolved key — default is month. That is the window parseDateRange already applied when a caller passed neither from nor to, so a bare GET /history answers exactly what it answered before. (all would have silently widened every existing caller; /sessions and /models took all for the mirror-image reason — they had no filter before.)
  2. Explicit from/to win — same explicitDateRange precedence /costs sets; the response then echoes "range": null.
  3. day resolves over the unionhistoryUnionSeries reuses the existing usageCTE/newUsageFilter at the raw-floor split. No second union was written.
  4. hour stays raw-only and reports covered_since via rawCoveredSince.
  5. Overview drops the borrowed noteHistorySection reads the history response's own covered_since instead of the sessions list's, so it is gone on every range the union covers and remains only where coverage is genuinely short.
  6. Testsinternal/api/history_range_test.go, following overview_range_test.go and seeding off the same seedRangeFixture.
  7. Docsdocs/operations/api-reference.md gains a GET /history section; the "not part of this contract" line is replaced, the endpoint added to the contract paragraph and the defaults table. CHANGELOG.md + a README.md clause.

Also in scope: RANGE_DAYS and HISTORY_EPOCH are gone from Overview.tsx — the page no longer derives from/to from the range key at all, so its copy of the window lengths cannot drift from the server's. (The ticket flagged a leap-year skew between RANGE_DAYS.year = 365 and a server AddDate(-1, 0, 0); rangeSince on main actually uses AddDate(0, 0, -365), so there was no live skew — deleting the frontend copy removes the class of bug regardless.)

Beyond the criteria — one addition worth a look

heatmap_covered_since. Both heatmaps resolve hour of day, which daily_usage does not keep, so they stay raw-only at every granularity. Now that the charts above them reach back a year, the calendar draws ~11 months of empty cells that read as "no activity" rather than "rolled up". Same rawCoveredSince call, one note per heatmap. Easy to drop if you consider it out of scope.

The second commit exists for the same reason: covered_since had no consumer where it was actually reachable. The Overview only asks for hour on its Day range, where a 24 h window is always inside raw retention — so the History page at 1y + Hour showed a raw-only chart under a year-long KPI header and said nothing. It now shows the note.

Verified

  • go test ./... green, go vet ./... clean (Docker golang:1.23-bookworm; no native Go toolchain on this box).
  • npm run build clean (tsc + vite).
  • Browser, against a seeded DB (200 raw spans over 25 days + 945 daily_usage rows, retention run so aggregates cover 90 days and the raw floor sits at 2026-07-27):
Request Result
/history?granularity=day&range=all 90 buckets, 2026-05-22 → 2026-08-20, $1132.20
/costs?range=all 90 days, 2026-05-22 → 2026-08-20, $1132.20 — the two panels now agree exactly
/history?granularity=hour&range=all raw-only: 170 buckets, 200 spans, covered_since 2026-07-27T08:00:00Z
/history?range=all&from=2026-08-01 "range": null, clamped to 20 buckets
/history?granularity=day&range=month 30 buckets / 407 spans — reaches 6 aggregate days inside the month window, covered_since null, heatmap_covered_since set

Coverage notes rendered, per page and range:

  • Overview All / Year / Month / Dayno History note at any range; only the Sessions note remains, which is correct.
  • History page 30d → heatmap notes only.
  • History page 1y + Hour → hourly-series note and heatmap notes.
  • No console errors on any page.

One caveat on the screenshots: Recharts area/line marks do not rasterise in this headless Chromium at Overview's chart size — the untouched Costs chart is blank in exactly the same way. I checked the DOM instead: the Overview History area is a real 179-segment path 1098 px wide spanning all 90 days.

Scrutinize

  • The month default (criterion 1) — it is the behaviour-preserving choice, but it is the one call that changes what a bare request means if I read parseDateRange wrong.
  • Whether heatmap_covered_since is welcome or scope creep.

🤖 Generated with Claude Code

Wayland and others added 2 commits August 20, 2026 23:56
/history was the last read endpoint outside the range contract: it took
from/to and read raw spans only, so on year or all it charted a visibly
shorter window than the Costs panel beside it, and the Overview papered
over that by borrowing the sessions list's coverage note.

It now takes range, defaulting to month — the window from/to already
defaulted to, so an existing caller sees exactly what it saw before —
with explicit from/to still winning and echoing a null range, the
precedence /costs sets. At day, week and month granularity buckets and
by_model resolve over the existing usageCTE union at the raw-floor
split, so a long window keeps charting after retention has deleted the
raw spans.

hour stays raw-only and says so in covered_since: daily_usage consumes
whole UTC days and cannot produce a sub-day bucket, so the alternative
is day-shaped data under an hour label. Both heatmaps resolve hour of
day at every granularity and get the same treatment under
heatmap_covered_since, rather than drawing empty cells for days that
were merely rolled up.

The Overview's History block reads that coverage for its own series
instead of the sessions list's, so the note is gone on every range the
union covers. RANGE_DAYS goes with it — the page no longer derives
from/to from the range key at all.

Co-Authored-By: Wayland <wayland@agents.flopbut.local>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hourly series reports covered_since, but only the Overview read it —
and the Overview asks for hour granularity solely on its Day range,
where a 24h window is always inside raw retention. So the one place the
clamp is reachable, the History page's 1y window at Hour granularity,
said nothing: the charts silently showed the last raw days while the
KPI header totalled a year.

Both notes now come from one component keyed by which series it
describes: covered_since above the activity charts, and
heatmap_covered_since under each heatmap.

Co-Authored-By: Wayland <wayland@agents.flopbut.local>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e443fb16-86ec-451c-8230-3de1712304db


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Fl0p

Fl0p commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Squash-merged to main as 1438d17 with agent identity preserved (author=committer=Daedalus), per the local-merge rule. Review notes on the issue thread.

@Fl0p Fl0p closed this Aug 20, 2026
@Fl0p
Fl0p deleted the flo-591-history-range branch August 20, 2026 22:13
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.

1 participant