Skip to content

ADR-0003 — Zod as the single source of truth ​

Status: superseded by ADR-0018 · Date: 2026-09-14

Context ​

Three artifacts must agree: runtime validation of inbound requests, the TypeScript types used in code, and the published API documentation. Maintaining them separately guarantees they drift.

Decision ​

Zod schemas in src/contracts/ are authoritative. TypeScript types are inferred from them (z.infer), and fastify-type-provider-zod + @fastify/swagger generate the OpenAPI document from the same objects that validate at runtime.

All inbound schemas are .strict().

Consequences ​

  • Documentation cannot drift from validation; they are the same object.
  • .strict() turns an upstream platform change into a loud 400 rather than a silently ignored field — for an integration against a vendor UI we do not control, failing loudly is the safer default.
  • Model output is validated with the same machinery as network input, which is what makes "model output is untrusted" (Constitution C3) mechanical.