Skip to content

Fix the 1D tab showing all-time totals on rollup-only stats panels - #224

Merged
ralyodio merged 2 commits into
masterfrom
worktree-exit-pages-1d-range
Aug 30, 2026
Merged

Fix the 1D tab showing all-time totals on rollup-only stats panels#224
ralyodio merged 2 commits into
masterfrom
worktree-exit-pages-1d-range

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Exit pages, devices, browsers and operating systems have no sub-day data source, so #222 gave them the rollup ranges only — with "1D" documented to mean today's UTC rollup rather than a rolling 24 hours.

But the 1d range object is defined with minutes: 1440 and no days, since it is a raw-event window for every other panel. resolveDays looks for days, finds nothing, and falls through to tracker_first_day, which returns the project's entire history. So those four panels answered the 1D tab with all-time totals, and on any site younger than 30 days 1D, 1M and All were byte-identical.

How it surfaced

rssamplifier.com is 15 days old. Its exit-pages card read 169,207 for /login at 1D, 1M and All alike, while the top-pages card — raw-backed, and therefore correct — read ~4k for the same path over the same 24 hours. That gap is what made it look like a stats bug rather than a traffic spike.

Verified against the rollups directly:

window exit /login pageviews /login
1 day 536 537
7 days 42,499 42,532
30 days 169,209 169,315

The data was always right; only the day resolution was wrong.

The fix

rollupDays() collapses any raw range to 1 for these four panels and passes real rollup ranges through untouched. The 1D tooltip is relabelled from "Last 24 hours, hourly buckets" to "Today so far, UTC day" for the same panels, so the tab no longer promises a rolling window it cannot serve. Panels with a raw source are unaffected — pages at 1D still hits tracker_recent_top_pages with p_minutes: 1440.

Testing

tests/tracker-rollup-ranges.test.ts pins all of it: the 1D/1M day resolution per panel, the relabelled tooltip, and that raw-capable panels keep their 7 tabs and rolling source.

Local install is broken at HEAD (the @profullstack/autoblog pin), so there is no local vitest or tsc — the assertions were run by bundling the module graph with esbuild, 25/25 passing, and CI is the real gate here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WaCMqhvNYfKtjLHmHQD1oq

Exit pages, devices, browsers and operating systems have no sub-day data
source, so #222 gave them the rollup ranges only — with "1D" documented to
mean today's UTC rollup rather than a rolling 24 hours.

But the 1D range object is defined with `minutes: 1440` and no `days`, since
it is a raw-event window for every other panel. resolveDays looks for `days`,
finds nothing, and falls through to tracker_first_day, which returns the
project's entire history. So those four panels answered the 1D tab with
all-time totals, and on any site younger than 30 days 1D, 1M and All were
byte-identical.

rssamplifier.com is 15 days old, which is how this surfaced: its exit-pages
card read 169,207 for /login at 1D, 1M and All alike, while the top-pages
card — raw-backed, and therefore correct — read ~4k for the same path over
the same 24 hours. Today's actual figure is 536.

A sub-day window against a day-resolution rollup is one day, so rollupDays()
collapses any raw range to 1 for these panels and passes real rollup ranges
through untouched. The 1D tooltip is relabelled from "Last 24 hours, hourly
buckets" to "Today so far, UTC day" for the same four panels, so the tab no
longer promises a rolling window it cannot serve. Panels with a raw source
are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaCMqhvNYfKtjLHmHQD1oq
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

40 finding(s)

HIGH/CRITICAL: 3 | MEDIUM: 28 | LOW: 9

Severity Rule Location
HIGH tls-verification-disabled lib/onion.ts:47
HIGH secret-generic-credential lib/sp/platforms/facebook.ts:32
HIGH sh-remote-script-execution prober/deploy/provision.sh:30
MEDIUM js-unescaped-html-sink app/(app)/dashboard/admin/email-broadcast/EmailBroadcastForm.tsx:125
MEDIUM js-unescaped-html-sink app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx:214
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:67
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:97
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:104
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:110
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:186
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:190
MEDIUM js-unescaped-html-sink app/c/[project]/[slug]/page.tsx:77
MEDIUM js-unescaped-html-sink app/c/[project]/page.tsx:57
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:228
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:285
MEDIUM js-unescaped-html-sink app/layout.tsx:129
MEDIUM js-open-redirect app/login/form.tsx:39
MEDIUM js-unescaped-html-sink app/r/[token]/page.tsx:176
MEDIUM js-open-redirect app/signup/form.tsx:43
MEDIUM js-open-redirect components/billing/buy-credits-modal.tsx:98
MEDIUM js-unescaped-html-sink components/json-ld.tsx:8
MEDIUM js-unescaped-html-sink components/report/markdown-view.tsx:15
MEDIUM redos-nested-quantifier lib/careers/jobs.ts:139
MEDIUM js-unescaped-html-sink lib/careers/page-templates.ts:198
MEDIUM redos-nested-quantifier lib/emailMarkdown.ts:130
MEDIUM redos-nested-quantifier lib/lx/articleGen.ts:98
MEDIUM redos-nested-quantifier lib/sp/parseHandle.ts:92
MEDIUM redos-nested-quantifier lib/tracker/agent-gate.ts:61
MEDIUM sql-template-interpolation scripts/detect-slot-themes.ts:31
MEDIUM sql-template-interpolation scripts/purge-constructed-keywords.ts:163
MEDIUM sql-template-interpolation scripts/purge-offniche-keywords.ts:124
LOW secret-generic-credential app/(marketing)/docs/autoblog-webhook/page.tsx:145
LOW secret-generic-credential lib/sp/platforms/linkedin.ts:25
LOW js-dynamic-code-execution tests/careers-page-templates.test.ts:21
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:19
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:69
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:51
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:52
LOW secret-generic-credential tests/contract/posthog-integration.test.ts:13
LOW secret-generic-credential tests/lead-campaign.test.ts:16

Snippets are redacted; ThreatCrush never prints matched credential material.

EmptyRange looked the range description up in the global table, so an exit
pages / devices / browsers / OS card with no data for 1D still said "last 24
hours, hourly buckets" while its own tab tooltip said "today so far, UTC
day". Both call sites already hold the panel's range list; pass it through
and read the label from there, falling back to the global lookup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaCMqhvNYfKtjLHmHQD1oq
@ralyodio
ralyodio merged commit fa18e98 into master Aug 30, 2026
10 checks passed
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