Skip to content

audit: schema accepts impossible calendar dates (2026-02-31, 2026-13-01) → 'undefined 2026' labels and silently shifted RSS dates #33

Description

@tkowalczyk

Evidence (reproduced through the real schema + formatters)

isoDate in src/content/statement-schema.ts:13-16 validates only the regex \d{4}-\d{2}-\d{2}:

  • statementSchema.safeParse(...) ACCEPTS period_start: "2026-02-31", "2026-13-01", even "2026-00-00" (verified).
  • Downstream, formatPeriodLabel("2026-13-01", ..., "monthly") renders "undefined 2026" into the page eyebrow/HTML (month index 12 → out of array bounds).
  • rfc822FromIsoDate in src/lib/feeds/rss.ts silently rolls over: 2026-02-31Tue, 03 Mar 2026 and 2026-13-01Fri, 01 Jan 2027 in RSS pubDates — wrong dates published with no error anywhere.

Also accepted today: duplicate categories (["politics","politics"] passes z.array(category).min(1)).

This schema is the cross-repo contract with the poly-track producer, so the change is a narrowing — per docs/statement-schema.md it must be documented and coordinated (it only rejects inputs that were previously garbage, so no SCHEMA_VERSION bump should be needed, but state that explicitly in the doc).

TDD plan (failing tests first)

src/content/statement-schema.test.ts exists — add cases FIRST:

  1. Reject 2026-02-31, 2026-13-01, 2026-00-00, 2026-04-31; accept 2026-02-28, 2024-02-29 (leap), 2026-12-31.
  2. Implementation: extend isoDate with a .refine that round-trips through Date.UTC (y, m-1, d then compare getUTCFullYear/Month/Date) — no dependency needed.
  3. Reject duplicate entries in categories (refine: new Set(v).size === v.length).
  4. Add a regression test in period.test.ts: formatPeriodLabel should never emit the string "undefined" for any schema-accepted input (property-style loop over valid dates is enough).

Acceptance criteria

AC Test
Calendar-invalid dates fail schema validation with a clear message unit
Duplicate categories rejected unit
docs/statement-schema.md documents the narrowing + upstream note review
Existing statements still parse content collection loads in pnpm build / existing tests

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkAgent-grabbable: implementable without further inputbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions