Skip to content

pm-stack(aggregator): freshness() ages impossible and non-ISO measuredAt stamps as fresh — Date.parse rolls 2026-02-30 to Mar 2 and accepts 'March 2, 2026' #5102

Closed

Description

@dezren39

pm-stack(aggregator): freshness() ages impossible and non-ISO measuredAt stamps as fresh — Date.parse rolls 2026-02-30 to Mar 2 and accepts 'March 2, 2026'

Evidence

freshness(measuredAt, cadenceDays, now) in pm-stack/aggregator/src/derive.ts:941 (callers: hub/service card rendering in src/render.ts:2234, :2300, :2513, :2526) parses the stamp with raw Date.parse in both branches:

  • Date-only branch (derive.ts:951): Date.parse(\${measuredAt}T00:00:00Z`)` — an impossible calendar date rolls to the next real day instead of failing.
  • Datetime branch (derive.ts:962): Date.parse(measuredAt) — any string V8/Bun can parse, ISO or not, is accepted.

Every sibling aggregator gate that consumes an external stamp uses the real-calendar ISO check isIsoDate / parseIsoDate (pm-stack/aggregator/src/validate.ts:66 / :116), which re-checks the components so impossible dates return null. freshness() never applies it.

Verified this run with a laptop bun probe importing freshness from origin/main dda1def97, now = 2026-03-01T12:00:00Z, cadenceDays = 7 (/tmp/probe-fresh.ts on the laptop):

measuredAt result expected
2026-02-30 (impossible) { stale: false, ageDays: 0 } — rolled to Mar 2, reported as read today invalid stamp → { stale: true, ageDays: null }
March 2, 2026 (non-ISO) { stale: false, ageDays: 0 } invalid stamp → { stale: true, ageDays: null }
2026-04-31 (impossible) { stale: true, ageDays: null } — only via the future-tolerance bound, by accident of the chosen now invalid
2026-02-27 (control) { stale: false, ageDays: 2 } correct
not-a-date (control) { stale: true, ageDays: null } correct
2026-03-01 (control) { stale: false, ageDays: 0 } correct

Why it matters

freshness() decides whether hub and service cards render as stale. An extractor or upstream doc that emits an impossible or non-ISO measuredAt is currently reported as a read taken today (ageDays 0, not stale) — the exact failure the aggregator's ingest contract (#2576 family) exists to prevent: malformed stamps must read as unknown age, never as maximally fresh. The future-tolerance bound (#1568) catches only impossible dates that roll more than a day past now; one that rolls to today or tomorrow passes as fresh.

Acceptance criteria

Dedup: distinct from #1568 (closed; future bound only), #2206 (closed; saas-extract freshestDate), #4987 (closed; parseQuotaCurrent measuredAt). No open issue covers freshness() in derive.ts.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    source/creatorFiled by the new-issue-creator job
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    queue/queuedHas a queued task in the Supabase agent queue
    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 · state: filed

    • Agent: new-issue-creator job (new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-11-03 13:35 CST
    • Found via: filed this run
    • Task: Gate freshness() in pm-stack/aggregator/src/derive.ts with the real-calendar ISO check so impossible/non-ISO measuredAt stamps read as unknown age instead of fresh
    • Expectation: effort s — done when the acceptance criteria in the issue body pass and the aggregator bun test suite is green
    • Links: queue packet packet-5102-fix.json (repo-health-code-pilot)
  3. 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
  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  5. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  6. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 3, 2026
  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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