Skip to content

ADR-0010: Zod v4 for schema validation

Status: Accepted Date: 2026-04-20

Core types are compile-time only. Every byte that crosses the boundary into our runtime — agent configs loaded from disk, tool inputs produced by LLMs, delegated tasks sent between agents — is just a string or Record<string, unknown> until something validates it. Without a validator, ADR-0006’s security guarantee is aspirational: a TypeScript readonly modifier is erased by the time the code runs.

We need a validation library. Three serious candidates exist: Zod, Valibot, and ArkType. They do the same job; the trade-offs differ.

Use Zod 4.3.6 as the validation library, pinned to that exact version. Schemas live in the separate @agent-platform/schemas package (see ADR-0011).

  • Ecosystem and maturity. Zod is the most battle-tested of the three candidates. At trust boundaries, conservative is the right default — we want the library to have had its bugs found by other teams before ours.
  • JSON Schema export. Zod 4 has z.toJSONSchema built in. This matters because tool definitions must advertise JSON Schemas to LLMs for function-calling. Having the conversion built in avoids a second dependency or a hand-maintained schema layer.
  • Bundle size. ~30KB gzipped. A non-issue on Node.js; manageable on Cloudflare Workers within their size limits. If the runtime platform turns out to be tight on size, the schema shapes translate nearly 1:1 to Valibot — this is not a one-way door.
  • Strict-mode ergonomics. Zod’s .strict() rejects unknown object keys, which is the mechanism that makes DelegatedContextSchema an ADR-0006 enforcement point. No custom validator code needed.
  • Tight TypeScript inference. z.infer plays well with our strict compiler settings, including exactOptionalPropertyTypes and readonly.
  • Pinned version. save-exact=true in .npmrc means an upgrade is a conscious decision, not a side effect. Zod 4 is stable but the minor cadence is brisk.
  • Valibot. ~3KB gzipped, modular by design. Smaller community; JSON Schema export is a separate plugin with less breadth. Right choice if bundle size dominates (e.g., edge Workers with many schemas). Not our bottleneck yet.
  • ArkType. Fastest parser in the category, uses TypeScript-like syntax for schemas. Smallest community of the three; ecosystem tooling (editor plugins, error formatters) less developed. Would reconsider in a year.
  • Hand-rolled type guards. No schema declaration, no JSON Schema export, no composition. Verbose, error-prone at scale, and requires us to maintain a separate “here’s how to check an AgentDefinition” file that will inevitably drift from the types.

If we later want to switch to Valibot (bundle size) or ArkType (perf), the Assert<Equals<z.infer<typeof S>, CoreType>> checks would detect drift during migration, and individual schema files are small. Budget: roughly a day of focused work per ~10 schemas. This is not a decision we’re stuck with.