Repository navigation
Description
aggregator(tier-items): isIso accepts impossible calendar dates — delegation timestamps (updatedAt, proposal.at, unable.at) pass on a bare Date.parse, are stored verbatim, and the proposal TTL is clocked from a day that never existed
Summary
sanitizeDelegation (pm-stack/aggregator/src/tier-items.ts) is the boundary sanitizer for tier-item delegation extensions — the fail-soft gate that decides which publisher-supplied delegation fields survive into the dashboard. Its only timestamp check is the local helper at tier-items.ts:68-70:
function isIso(v: unknown): v is string {
return typeof v === "string" && v.includes("T") && !Number.isNaN(Date.parse(v));
}Date.parse normalizes impossible calendar days instead of refusing them (2026-02-30 → 2026-03-02), so isIso admits them at all three call sites:
delegation.updatedAt— tier-items.ts:153delegation.proposal.at— tier-items.ts:168delegation.unable.at— tier-items.ts:185
The impossible string is then stored verbatim on the sanitized delegation (d.updatedAt = raw.updatedAt, d.proposal = { summary, at: p.at }, d.unable = { …, at: u.at }).
The repo already has the strict rule, one module over: validate.ts parseIsoDate refuses 2026-02-30 (returns null), and isTimestampNotFuture refuses 2026-02-30T00:00:00Z (pinned by test/validate.test.ts:197). The tier-item sanitizer just never uses it — the same asymmetry pattern as #3393 (actions.ts) and #3365 (snapshots.ts), in a different gate with different consumers.
Why it matters
proposal.at is not decorative: proposalExpired (tier-items.ts:308-313) clocks the 48-hour proposal TTL (#1293, PROPOSAL_TTL_MS) from it with the same normalizing Date.parse. A proposal stamped 2026-02-30T00:00:00Z is read as drafted on Mar 2, so at Mar 3 00:00Z it is exactly 48h old and not expired — while the Feb 28 control at the same instant is 72h old and expired. The impossible date silently extends the proposal's approvable life by ~2 days (and updatedAt/unable.at feed ordering and rendering consumers that re-parse the stored string the same way). A publisher typo or a non-leap-year Feb 29 passes every check, is stored naming a day that never existed, and no surface ever reports it.
Evidence (verified this run, 2026-11-01)
Laptop probe ~/git/fleet/probe-2026-11-01-laneA-tieriso/ (bun 1.4.2, importing the real sanitizeTierItemExtensions / proposalExpired from pm-stack/aggregator/src/tier-items.ts and parseIsoDate / isTimestampNotFuture from src/validate.ts; tier-items.ts md5 ab984916… and validate.ts md5 2078ddfd… identical in the sandbox clone at main d85e4098 and the laptop clone):
control valid 2026-02-27: KEPT
control garbage at: DROPPED
control no-T date 2026-02-27: DROPPED
impossible 2026-02-30T00:00:00Z: proposal.at KEPT stored=2026-02-30T00:00:00Z | updatedAt KEPT stored=2026-02-30T00:00:00Z
impossible 2026-02-29T00:00:00Z: proposal.at KEPT stored=2026-02-29T00:00:00Z | updatedAt KEPT stored=2026-02-29T00:00:00Z
impossible 2026-04-31T00:00:00Z: proposal.at KEPT stored=2026-04-31T00:00:00Z | updatedAt KEPT stored=2026-04-31T00:00:00Z
impossible 2026-02-30T25:00:00Z: proposal.at DROPPED | updatedAt DROPPED (hour range IS checked by Date.parse)
unable.at 2026-02-30 / 2026-02-29 / 2026-04-31: KEPT (probe.ts run 1)
validate.parseIsoDate("2026-02-30") = null
validate.isTimestampNotFuture("2026-02-30T00:00:00Z", …) = false
Date.parse("2026-02-30T00:00:00Z") = 2026-03-02T00:00:00.000Z
proposalExpired(at=2026-02-30, now=2026-03-03T00:00Z) = false (rolled to Mar 2 → exactly 48h, not > TTL)
proposalExpired(at=2026-02-28, now=2026-03-03T00:00Z) = true (control: 72h > TTL, expired)
Dedup: open + recently closed search for tier-items / isIso / sanitizeTierItemExtensions / proposalExpired — no existing issue covers this gate. Distinct from #3393 (actions.ts validateNewAction payload.until / proposalUpdatedAt — different file, fields, and writer path) and from #3365/#3379 (snapshots).
Acceptance criteria
isIso(or its replacement) refuses impossible calendar dates — e.g.2026-02-30T…,2026-02-29T…in a non-leap year,2026-04-31T…— at all three call sites (updatedAt, proposal.at, unable.at), reusing or matching the strictness ofvalidate.ts(parseIsoDate/ a round-trip component check), so a delegation carrying one is dropped by the sanitizer exactly as a garbage timestamp is today.- Timestamps that are valid today stay valid: the fix must not narrow the accepted formats beyond the impossible-date rule (existing published shapes with a
Tand a parseable, real date/time keep passing); tests pin at least one offset-bearing and one fractional-second valid timestamp. - New tests in the aggregator suite pin each refused shape for each of the three fields, plus the
proposalExpiredconsequence (an impossibleproposal.atcan no longer extend the TTL window). - No change to the fail-soft contract: a refused timestamp drops the delegation field, never the item or the section.
Activity
developing-today commentedon Nov 1, 2026
CODE-1294
dezren39 commentedon Nov 1, 2026
🤖 Agent note · filed
19 remaining items
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
Merge pull request #4452 from developing-today/queue/6065-3396-is-iso…
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done