# ADR-0003: Enforce SEO rules with Zod + build linter (not Pydantic) **Status:** Accepted — 2026-07-15 ## Context We want SEO/AEO rules (title/description length, canonical, OG image, structured data, single H1, alt text) to be *enforced*, not aspirational. The question of a Pydantic-style validator came up, but this is a TypeScript/Astro build — Pydantic is Python and would mean bolting a foreign runtime onto a JS pipeline. ## Decision Enforce in two layers, both native to the stack: 1. **Zod schema** on content-collection frontmatter (Astro Content Collections already run on Zod). Encodes: title ≤ 55 chars, description 120–158, valid canonical, required OG image, `noindex` flag, `primaryTopic`. Violations **fail `npm run build`**. 2. **Build-time SEO linter** (`scripts/seo-lint.mjs`) over `dist/**/*.html` for rules Zod can't see in frontmatter: exactly one `