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>
31 lines
1.4 KiB
Markdown
31 lines
1.4 KiB
Markdown
# 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 `<h1>`, heading order, canonical/title/description present, non-empty
|
||
`alt` on every `<img>`, every JSON-LD block parses. Runs in CI (and locally via `npm 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 `seo` schema.
|
||
- Detailed rules in `docs/seo-aeo-optimization.md` §1.
|