Queue-North-Website/scripts/verify.d/15-built-html

22 lines
988 B
Plaintext
Raw Permalink Normal View History

feat(build): audit what the site actually serves, in the build and on the wire Everything here checked an input: the content check reads the data, the secret scan reads the diff, the build reads the source. Nothing read the OUTPUT, which is the only thing a visitor or a crawler ever sees. Two live defects made the case: every page preloaded the wrong image for months, and eleven pages shipped a run-on description. Both are plain in the built HTML and invisible in the source. Build mode is guard 15-built-html, after 10-build. Per page it requires exactly one title, one non-empty description, one canonical equal to the site origin plus the route, and one h1; JSON-LD that parses, with no FAQPage, which the owner ruled out; no em dash and no U+FFFD; a preload naming the image the page actually paints first; and no description that runs its short description into the next sentence. Across pages it requires every internal link and every fragment to resolve, the sitemap to list exactly the routes the site serves, and 404.html to carry noindex and no canonical. It exits 2, not 0, when dist/ is missing or older than the sources: auditing stale output is auditing nothing. That also guards a specific hazard. react-helmet-async on React 19 does not merge, so a second <SEO> anywhere on a page silently emits a second title and a second canonical, and a search engine picks whichever it likes. URL mode fetches every page in a live sitemap once per crawler user agent (OAI-SearchBot, PerplexityBot, ClaudeBot, Googlebot, bingbot), requires HTTP 200 and identical bytes across agents, runs the same page rules, and reports any URL without a lastmod. It is deliberately NOT wired into deploy.sh: a check that runs after publication cannot stop it, and pretending otherwise is worse than not having it. Run it after a deploy. Proven by mutation, nine of them, each restored afterwards: a wrong canonical (7 pages), a second h1 (4), FAQPage markup, an em dash in copy, a link to a route that does not exist (18), a fragment that is not on its target page (7), the preload keyed on the old attribute (19), the template title left in place giving two titles (19), and the industry routes dropped from the route list (56). A clean build audits clean, and URL mode passes against the local server as all five crawlers. Closes #228. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 04:51:08 -05:00
#!/usr/bin/env bash
#
# What the site actually serves, checked after it is built.
#
# Every other guard here reads an input: the content check reads the data, the
# secret scan reads the diff, the build reads the source. This one reads the
# OUTPUT, which is the only thing a visitor or a crawler ever sees. Two live
# defects made the case for it: every page preloaded the wrong image for months,
# and eleven pages shipped a description that read as one run-on sentence. Both
# are plain in the built HTML and invisible in the source.
#
# It sorts after 10-build on purpose: there is nothing to read until the build
# has run, and it refuses (exit 2) rather than pass when dist/ is missing or
# older than the sources.
#
# Exit 0 clean, 1 findings, 2 nothing was audited.
set -uo pipefail
cd "$(git rev-parse --show-toplevel)" || exit 1
[ -f scripts/audit-html.js ] || { echo "audit: scripts/audit-html.js is missing, so nothing was audited." >&2; exit 2; }
node scripts/audit-html.js