Repository navigation
Description
Problem
The aggregator's ingest contract (pm-stack/aggregator/src/validate.ts:1-9) says every ingested document is validated at its boundary for "shape, element types, value ranges, and real calendar dates", and validate.ts provides parseIsoDate/isIsoDate for exactly this: "impossible calendar dates FAIL instead of rolling over: 2026-02-30 and 2026-13-01 are null, not March dates."
The stream-event boundary never got that check. validateStreamEvent (pm-stack/aggregator/src/events.ts:54) validates the event timestamp with Date.parse only:
if (typeof v.at !== "string" || !Number.isFinite(Date.parse(v.at))) {
return { ok: false, error: "event.at must be an ISO timestamp string" };
}Date.parse rolls day-overflow dates over instead of rejecting them, so an event stamped 2026-02-30T10:00:00Z is accepted and stored verbatim, while every consumer (pruneEvents retention at events.ts:96-105, filterEvents newest-first ordering) parses it as 2026-03-02 — a different day than the record claims. The same validator gates both ingestion paths: EventsStore.append (events.ts:243) and the on-load filter isStreamEvent (events.ts:88-89, 187-191), so an impossible-date record also survives reload instead of being quarantined as malformed.
Sibling boundaries were hardened for this exact failure mode: the DNS register (#2199, PR #4835, plus #4858 for the JSON path) and the charges rows (#3458) all refuse impossible dates via isIsoDate. The event stream — the fleet's own audit trail, persisted to the events file with 30-day / 5000-record retention — was left on the lenient check.
Verified this run (laptop probe, origin/main 1e3763f)
Probe /tmp/probe-events.ts on the laptop (log /tmp/probe-scan21.log), importing validateStreamEvent and pruneEvents from current main:
2026-02-30 ok= true stored_at=2026-02-30 parsed=2026-03-02T00:00:00.000Z
2026-02-30T10:00:00Z ok= true stored_at=2026-02-30T10:00:00Z parsed=2026-03-02T10:00:00.000Z
2026-04-31T10:00:00Z ok= true stored_at=2026-04-31T10:00:00Z parsed=2026-05-01T10:00:00.000Z
2026-13-01T00:00:00Z ok= false event.at must be an ISO timestamp string
2024-02-29T10:00:00Z ok= true (valid leap date, control)
2026-01-15T10:00:00Z ok= true (control)
2026-01-01T25:00:00Z ok= false (hour overflow already refused by Date.parse)
PRUNE_KEPT: 1 (the 2026-02-30 event is retained, treated as March 2)
So month-13 and hour-25 are refused only because Date.parse happens to return NaN for them; day-overflow within a real month — the case parseIsoDate was written for — sails through.
Why it matters
Stream events are the fleet's shared audit/activity record: a producer (or a hand-edited / corrupt events file) that stamps an impossible date silently shifts the event to a different real day for retention, ordering, and filtering, and the stored string disagrees with every parsed view of it. The boundary's own contract and the shared primitive both say this input must be refused at ingest; every comparable aggregator boundary already does.
Acceptance criteria
validateStreamEventrefuses anatthat is shape-valid ISO but not a real calendar date/time (e.g.2026-02-30,2026-02-30T10:00:00Z,2026-04-31T10:00:00Z), using the sharedparseIsoDate/isIsoDateprimitive fromvalidate.tsrather than a new local parser.- Real timestamps still validate, including leap dates (
2024-02-29T10:00:00Z), offsets, and fractional seconds — the shapes the existing tests inpm-stack/aggregator/test/events.test.tsandorchestration.test.tsexercise. - On load, an impossible-date record in the events file is treated as malformed by the existing
isStreamEventpath (quarantine/drop behavior unchanged otherwise). - New tests in
pm-stack/aggregator/test/events.test.tscover the impossible-date refusals and the leap-date control; existing events/orchestration tests stay green. - Related site to check while in the area (separate change is fine):
isSavedViewinpm-stack/aggregator/src/views.ts:100-102uses the same Date.parse-only pattern forcreatedAt/updatedAt.
Activity
dezren39 commentedon Nov 3, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 3, 2026
🤖 Agent note · picked up
validateStreamEventinpm-stack/aggregator/src/events.tsaccepts impossible calendar dates because it validatesatwithDate.parseonly. Fix per the issue's acceptance criteria: refuse shape-valid ISO timestamps whose date is not a real calendar date, using the sharedparseIsoDate/isIsoDateprimitive fromvalidate.ts; add tests inpm-stack/aggregator/test/events.test.ts(impossible-date refusals + leap-date control).dezren39 commentedon Nov 3, 2026
🤖 Agent note · done
validateStreamEventaccepted impossible calendar dates viaDate.parserollover.atgate inpm-stack/aggregator/src/events.tsto use the sharedisIsoDateprimitive fromvalidate.ts(the same gate the DNS/charges boundaries use) and added the#5154test case inpm-stack/aggregator/test/events.test.ts: impossible dates (2026-02-30,2026-04-31T10:00:00Z,2026-13-01, non-leap2026-02-29) refused; leap date,+02:00offset, fractional seconds, and date-only forms still validate. On load, impossible-date records are now treated as malformed by the existingisStreamEventpath. Verified on the laptop:bun test test/events.test.ts test/orchestration.test.ts— 50 pass, 0 fail. PR fix(aggregator): refuse impossible calendar dates in validateStreamEvent #5158 merged as 0e108b5; this issue closed by the merge. The relatedisSavedViewDate.parse-only site insrc/views.tswas left for a separate change, as the issue allows.