Repository navigation
Description
Problem
pm-stack/aggregator/src/saas-extract.ts applies two live file-export overlays — applyCloudflareExport (#1048) and applySupabaseExport (#1050) — whose freshness gates are Date.parse-only:
- Cloudflare
measuredAt:const measuredMs = Date.parse(doc.measuredAt)(saas-extract.ts:811), checked only for finiteness and a 30-minute / +5-minute freshness window (:812-813). - Cloudflare
cycleStart:const cycleMs = Date.parse(doc.cycleStart)(saas-extract.ts:819-821), checked only for finiteness before its year/month are compared against now to reject a previous cycle's totals. - Supabase
measuredAt:const measuredMs = Date.parse(doc.measuredAt)(saas-extract.ts:933), same finiteness + freshness checks (:934-935).
Date.parse rolls day-overflow dates over and accepts non-ISO strings, so an export document stamped 2026-02-30T12:00:00.000Z parses as March 2 and passes freshness at a wall-clock time near March 2. The overlay then stamps doc.measuredAt.slice(0, 10) verbatim onto the card (Cloudflare :846, Supabase :996), so the card displays a measured date that never existed — for a non-ISO stamp like March 2, 2026 12:00 UTC the slice is literally March 2, 2.
An impossible cycleStart of 2026-02-30 likewise rolls into March, evading the same-month check whose whole purpose is to keep a previous cycle's totals off a fresh month's card.
The same file already holds its other dates to the real-calendar rule: datesIn filters through parseIsoDate (saas-extract.ts:26, comment: "Real calendar dates only (#2264)") and the subscription renewsAt extraction requires parseIsoDate(...) !== null (:1175). The shared validate.ts contract (parseIsoDate/isIsoDate) also already lives in this file's imports. These three overlay fields are the residual gap.
Verified this run (laptop bun probe, origin/main bb592a6)
Probe /tmp/probe-saas.ts on the laptop (log /tmp/probe-saas.log), driving applyCloudflareExport / applySupabaseExport directly over extractSaasServices, with now = 2026-03-02T12:05:00Z:
CF measuredAt="2026-02-30T12:00:00.000Z" cycle="2026-03-01T00:00:00.000Z" -> used=75225 sourceClass=api cardMeasuredAt=2026-02-30
CF measuredAt="March 2, 2026 12:00 UTC" cycle="2026-03-01T00:00:00.000Z" -> used=75225 sourceClass=api cardMeasuredAt=March 2, 2
CF measuredAt="2026-03-02T12:00:00.000Z" cycle="2026-02-30T00:00:00.000Z" -> used=75225 sourceClass=api cardMeasuredAt=2026-03-02
CF measuredAt="2026-03-02T12:00:00.000Z" cycle="2026-03-01T00:00:00.000Z" -> used=75225 sourceClass=api cardMeasuredAt=2026-03-02
CF measuredAt="not-a-date" cycle="2026-03-01T00:00:00.000Z" -> used=null sourceClass=manual cardMeasuredAt=2026-10-20
SB measuredAt="2026-02-30T12:00:00.000Z" -> used=1.154 sourceClass=api cardMeasuredAt=2026-02-30
SB measuredAt="2026-03-02T12:00:00.000Z" -> used=1.154 sourceClass=api cardMeasuredAt=2026-03-02
SB measuredAt="not-a-date" -> used=null sourceClass=manual cardMeasuredAt=2026-10-28
So: impossible and non-ISO measuredAt values are applied as live api data on both cards and displayed verbatim; garbage is refused (control); a valid stamp behaves identically (control); an impossible cycleStart passes the month check a real February cycle start would fail.
Why it matters
These overlays are the "live measured data" path for the Cloudflare and Supabase cards: a passing document replaces the recorded prose values and re-classes the card's source as api. A drifted, skewed, or hand-edited export write whose stamp names no real instant should fall back to the prose, exactly as a stale or unparseable one does — instead the rollover gives it a real, possibly-fresh instant, it wins, and the card confidently displays a nonexistent measured date. That is the same failure mode the file's #2264 real-calendar rule was adopted to stop everywhere else.
Acceptance criteria
applyCloudflareExportchanges nothing (returns the services untouched, prose fallback) whendoc.measuredAtis not a real ISO calendar date/time under the sharedvalidate.tsrule — e.g.2026-02-30T12:00:00.000Z,2026-04-31T12:00:00.000Z,March 2, 2026 12:00 UTCare refused; validtoISOString()stamps are unchanged.applyCloudflareExportlikewise changes nothing when a presentdoc.cycleStartis not a real ISO calendar date/time, instead of rolling it into a neighbouring month for the same-month check.applySupabaseExportapplies the samemeasuredAtrule.- Real timestamps still overlay, including leap dates (
2024-02-29T12:00:00.000Z); the existing tests inpm-stack/aggregator/test/cloudflare-export.test.tsandpm-stack/aggregator/test/supabase-export.test.tsstay green. - New tests in those two files cover the impossible-date and non-ISO refusals for
measuredAt(both overlays) and forcycleStart(Cloudflare), plus a leap-date control that is kept.
Dedup / related
- Distinct from open pm-stack(aggregator): applyCloudflareExport accepts negative used values — Supabase overlay refuses them #4942 (same file/function: Cloudflare negative
usedvalues — a value-range rule, not the date gates) and closed aggregator(saas-extract): applySupabaseExport never range/type-checks the export doc — negative used/limit/billedUsd are stamped as live measured data, and an array measuredAt passes the freshness gate and corrupts the card #2508 (Supabase doc shape/range checks, including a non-stringmeasuredAt; a string impossible date still passes today, verified above). - Distinct from open pm-stack(aggregator): parseQuotaCurrent accepts impossible and non-ISO measuredAt — entry and window measuredAt are typeof-only, unlike the isIsoDate gates on resetsAt/credentialExpiresAt in the same functions #4987 (
quota-current.tsmeasuredAtgates) and the other aggregator date-gate filings: same defect class, different file and gates. - Dedup checked this run:
gh search issuesforsaas-extract measuredAt,applyCloudflareExport,applySupabaseExport, and opensaas-extractreturned only pm-stack(aggregator): applyCloudflareExport accepts negative used values — Supabase overlay refuses them #4942, closed aggregator(saas-extract): applySupabaseExport never range/type-checks the export doc — negative used/limit/billedUsd are stamped as live measured data, and an array measuredAt passes the freshness gate and corrupts the card #2508, and the closed feature issues feat(usage-hub): v2 Cloudflare live collector — hourly analytics feed + billing scope #1048/feat(usage-hub): v2 Supabase usage collector — Management API scope + muse-dev metrics #1050 — none covers these date gates.
Activity
dezren39 commentedon Nov 3, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 3, 2026
🤖 Agent note · picked up
measuredAt(and CloudflarecycleStart) instead of relying onDate.parserollover.queue/8497-5018-saas-extract-dates; enforce the sharedvalidate.tsreal-calendar ISO rule at the three gates (Cloudflare measuredAt ~:811, Cloudflare cycleStart ~:819, Supabase measuredAt ~:933), add refusal + leap-date control tests tocloudflare-export.test.ts/supabase-export.test.ts, run targeted tests on the laptop, open PR, complete task 8497.dezren39 commentedon Nov 3, 2026
🤖 Agent note · done
fixed_merged.measuredAt, CloudflarecycleStart, and SupabasemeasuredAtgates inpm-stack/aggregator/src/saas-extract.tsnow parse with the sharedparseIsoDatereal-calendar rule instead ofDate.parse, so impossible (2026-02-30,2026-04-31) and non-ISO stamps are refused with the prose fallback instead of rolling into a fresh-looking instant; 5 new tests cover both overlays'measuredAtrefusals, thecycleStartrefusal, and leap-date controls.bun test test/cloudflare-export.test.ts test/supabase-export.test.ts— 26 pass, 0 fail. PR was MERGEABLE/CLEAN. Codex review could not run (usage limits, zero findings posted) — disposition recorded on PR fix(aggregator): saas-extract export overlays refuse impossible/non-ISO dates #5024 (comment 6609715922).