Skip to content
Ritesh FirodiyaGet in touch

Work / Chitragupt / Wiki / Decisions

2026-06-10-zod-parse-firestore-boundary

Decisioncanonicalverified 2026-06-26

DECISION.2026-06-10.ZOD-PARSE-FIRESTORE-BOUNDARY

Phase 7e: zod-parse boundary on high-impact Firestore reads

Decision

Convert eight high-impact Firestore snapshot reads to zod-parse-at-boundary via a new parseDocData / parseField helper. Leave the remaining ~25 trivial primitive casts in place — they are runtime-safe.

Why

Phase 7e exit criterion targeted "14 as casts on Firestore snapshot data replaced with zod-parse-at-boundary". Audit surfaced ~42 total casts; on inspection, the bulk are field-level as Timestamp | undefined / as string | undefined which Firestore JS SDK preserves natively — replacing them with zod adds noise without protection. Schema drift bites in nested object + array shapes, not primitives.

Impact

  • New helper apps/functions/src/_lib/parse-snap.ts exports parseDocData(snap, schema, label) + parseField(snap, field, schema, label) — throws HttpsError with the exact failing path on drift.
  • Eight high-impact reads converted: admin/kpis.ts (AdminKpi snapshot), ca/clients.ts (CaEngagement), family/overview.ts (TaxReview for member drill-in), marketplace/engagement.ts (CaPricingMatrix on consult request), expense/dispute.ts (AnomalyDisputeBankForm in grievance PDF), tax/exports.ts (TaxReview consumed by both alert gate + CA-handoff PDF render — replaces 5 casts with one parse), _lib/subscription-gate.ts (AyUnlock array — paywall-critical), _lib/storage-accounting.ts (SubscriptionPlan enum — tier gate).
  • Remaining ~25 trivial casts on Timestamp / string / number / Record<string, unknown> deliberately left; documented as runtime-safe.

Status

Active.

Sources

  • .context/wiki/decisions/* § "2026-06-10 — Phase 7e: zod-parse boundary on high-impact Firestore reads"

Every project of mine is written down like this.

Read the résumé