Commit Graph

1 Commits

Author SHA1 Message Date
null 0d575f2977 feat(build): the copy is checked before a single page is built from it
src/data is prose in a data structure, and nothing checked it. The long-form
service pages make that dangerous in a specific way: their copy arrives as an
owner-approved markdown sheet that MIXES DIRECTIONS TO THE WEBSITE MANAGER INTO
THE COPY. "Do not promise that every number is always portable." "Keep this
factual:" "Place an official 8x8 Work screenshot beside this section." Those
lines look exactly like copy, and publishing one puts an internal instruction on
a customer-facing page.

scripts/lib/content.js decides whether the content layer is publishable, and
prerender.js runs it before rendering anything, so every build enforces it: the
pre-commit hook, npm run verify, and the Docker image build. It refuses a
website-manager direction, an em dash, a U+FFFD, markdown or an HTML tag left in
a string, an unknown block type, a section id that is not letter-first, unique
and free of the layout's own ids, a section that does not open with its direct
answer (unless it declares kind list or faq), a FAQ question with no answer, a
link to a route or fragment that does not exist, an image whose src is missing
from public/ or has no alt or no dimensions, and the missing benefits or
idealFor list that the short layout maps without checking. A description over
160 characters is a note, not a failure: owner-approved copy is published as
written.

scripts/lib/routes.js is now the one route list. prerender.js built its own
while src/routes.jsx built the router's, and nothing compared them: a route in
one and not the other is never prerendered, so the server answers it with
404.html while the site's own navigation links to it. entry-server.jsx exports
the router table so the build can compare the two.

Proven by mutation, seventeen of them, each expecting exactly one finding and
getting it: unknown block type, FAQ answer removed, answer moved below its list,
duplicate id, digit-leading id, link to /services/contact-centre,
#no-such-section, missing image file, image without dimensions, image without
alt, em dash, a manager direction, markdown bold, U+FFFD, an HTML tag, missing
h1, empty section. Both generated content modules pass unmutated. Against a real
build: an em dash added to industries.js failed npm run build naming the field,
and a /pricing route added to src/routes.jsx failed it naming the route.

Closes #230.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 04:43:01 -05:00