Skip to content

pm-stack(aggregator): saas-extract export overlays accept impossible/non-ISO measuredAt (and cycleStart) — Date.parse-only freshness gates vs the file's own parseIsoDate rule #5018

Closed

Description

@dezren39

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

  • applyCloudflareExport changes nothing (returns the services untouched, prose fallback) when doc.measuredAt is not a real ISO calendar date/time under the shared validate.ts rule — e.g. 2026-02-30T12:00:00.000Z, 2026-04-31T12:00:00.000Z, March 2, 2026 12:00 UTC are refused; valid toISOString() stamps are unchanged.
  • applyCloudflareExport likewise changes nothing when a present doc.cycleStart is not a real ISO calendar date/time, instead of rolling it into a neighbouring month for the same-month check.
  • applySupabaseExport applies the same measuredAt rule.
  • Real timestamps still overlay, including leap dates (2024-02-29T12:00:00.000Z); the existing tests in pm-stack/aggregator/test/cloudflare-export.test.ts and pm-stack/aggregator/test/supabase-export.test.ts stay green.
  • New tests in those two files cover the impossible-date and non-ISO refusals for measuredAt (both overlays) and for cycleStart (Cloudflare), plus a leap-date control that is kept.

Dedup / related

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-11-03 10:05 CST
    • Found via: filed this run
    • Task: Filed this verified bug — saas-extract.ts export overlays (applyCloudflareExport :811/:819, applySupabaseExport :933) gate measuredAt/cycleStart on Date.parse only, so impossible/non-ISO stamps roll over, pass freshness, and are stamped verbatim on the cards; the same file's other dates use parseIsoDate (aggregator(snapshot-records): shape-only dates win freshestDate — a 2026-13-01 'Last checked' silently drops a pool's snapshot history, 2026-02-30 is stored as a phantom March date #2264). Verified by laptop bun probe at origin/main bb592a6 (/tmp/probe-saas.log on laptop). A repo-task/v1 small_fix packet is enqueued to repo-health-code-pilot for the fix.
    • Expectation: effort/s — gate the three fields on the shared validate.ts real-calendar rule, keep valid stamps unchanged, add regression tests in cloudflare-export.test.ts / supabase-export.test.ts.
    • Links: evidence in the issue body above
  3. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

    • Agent: repo-worker-code, lane code-j (direct execution lane)
    • Task: task 8497 (small_fix) — saas-extract export overlays must refuse impossible/non-ISO measuredAt (and Cloudflare cycleStart) instead of relying on Date.parse rollover.
    • Plan: laptop worktree on branch queue/8497-5018-saas-extract-dates; enforce the shared validate.ts real-calendar ISO rule at the three gates (Cloudflare measuredAt ~:811, Cloudflare cycleStart ~:819, Supabase measuredAt ~:933), add refusal + leap-date control tests to cloudflare-export.test.ts / supabase-export.test.ts, run targeted tests on the laptop, open PR, complete task 8497.
    • Labels: claim labels synced (queue/claimed + status/in-progress + work/assigned; queue/queued + status/needs-triage removed).
  4. added
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  5. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: repo-worker-code, lane code-j (direct execution lane)
    • Task: task 8497 (small_fix) — completed as fixed_merged.
    • Change: PR fix(aggregator): saas-extract export overlays refuse impossible/non-ISO dates #5024 (merged, merge commit 048da81): the Cloudflare measuredAt, Cloudflare cycleStart, and Supabase measuredAt gates in pm-stack/aggregator/src/saas-extract.ts now parse with the shared parseIsoDate real-calendar rule instead of Date.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' measuredAt refusals, the cycleStart refusal, and leap-date controls.
    • Verification: laptop 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).
    • Labels: claim labels removed at this terminal state.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/pm-stackpm-stack (Pad/Grist) in developing-today/codekind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions