Static Astro 5 site for BlueCap Strategies with exact live-site content parity, deployed to k3s via GitOps (ArgoCD). - Page copy in Markdown content collections (services, focus-areas, pages, insights) with a Zod SEO schema enforced at build time - Homepage + About restored to exact live copy; real live-site imagery - Build-time SEO linter (scripts/seo-lint.mjs) and Playwright e2e suite - Multi-stage Dockerfile (nginx serves dist/) and Kustomize manifests (k8s/) - Per-agent robots.txt; config-driven PostHog + Umami analytics scaffold - ADRs and engineering docs under docs/ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1.4 KiB
1.4 KiB
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:
- 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,
noindexflag,primaryTopic. Violations failnpm run build. - Build-time SEO linter (
scripts/seo-lint.mjs) overdist/**/*.htmlfor rules Zod can't see in frontmatter: exactly one<h1>, heading order, canonical/title/description present, non-emptyalton every<img>, every JSON-LD block parses. Runs in CI (and locally vianpm run seo:lint).
Do not introduce Python/Pydantic.
Consequences
- Every commit is validated before ArgoCD deploys; "the rules are followed" is guaranteed by CI.
- Rules live with the content schema and the build, in one language.
- Requires the content collections from ADR-0002 to carry the shared
seoschema. - Detailed rules in
docs/seo-aeo-optimization.md§1.