01h1
Exactly one h1 in the built page. Zero is a fail; two is a fail.
Vault · the gates
Nine gate families run on every change. Fourteen checks per page, roughly twenty site-wide. A build that fails a check does not ship.
01 · Why gates exist
Not a crash. Not a red build. Valid output, every check green, pointing at the wrong thing. The repository's own working notes record the four that taught the lesson — quoted as written, not paraphrased.
Every serious defect found here has been a silent failure: valid output pointing at the wrong thing, with every check passing.
schemaOrg.imagewas a 1000×1500 portrait on 27 of 29 pages. The<meta>tag was valid; the card it produced was a sliver.- Workflow specs moved opportunities into
"Patient Pipeline", which has never existed in this location. The adapter emits{{UNRESOLVED:<name>}}and continues, so the step carryingmonetaryValuepointed at nothing. That is why every opportunity read $0 — not missing data entry.- Hardcoded asset URLs in
config.tswere never checked against the build. A rename would have shipped a 404 with a green build.- The first version of the compliance sweep scanned a field called
body. The SMS field iscopy. It passed its own negative test while scanning nothing.So: when you add a check, prove it fails. Break the thing on purpose, watch the gate catch it, restore, confirm green. A check that cannot fail is worse than no check, because it is trusted.
Prove it fails. A gate is added by breaking the thing it guards on purpose, watching the gate go red, restoring, and confirming green. The order matters: a check that has never failed has never been shown to look at anything. The compliance sweep in the fourth line above passed its own negative test while scanning an empty field, and was trusted for exactly as long as nobody broke it deliberately.
This page holds itself to the same rule. The fourteen checks below are read from the gate's own source, the report from the committed file, and the quotation above from the working notes — at build time. If any of them stops saying what this page claims, the page does not build.
02 · Fourteen per-page checks
One row per page in every audit report, fourteen cells per row. The names are the gate's own; the sentence under each is what the cell asserts. A check renamed in the gate fails this page's build.
01h1
Exactly one h1 in the built page. Zero is a fail; two is a fail.
02title
A title tag is present and no longer than 60 characters.
03desc
A meta description is present and no longer than 155 characters.
04canonical
The canonical is this page on the root domain, and no vercel.app address survives anywhere in the markup.
05og
og:title present, and an og:image that is an absolute URL on the site, resolves to a real file, and measures at least 1200px wide at about 1.91:1 — read from the bytes, not the tag. Runs on production builds; a staging build has no shareable link to unfurl.
06index
The robots directive matches the registry. Indexable pages carry no noindex and sit in the sitemap; unlisted pages carry noindex and do not. On staging every page must be unindexable.
07schema
Every JSON-LD block parses. The Dentist node is home-only; Service, FAQPage, ItemList, AboutPage, ContactPage and BreadcrumbList carry the shape the page is registered for.
08entity
Every @id reference resolves to a defined node, no @id carries two types, no self-authored rating markup, and a telephone appears on the practice node only — the public number.
09links
Zero dead internal links, resolved against the built files, and zero orphans: every page has at least one inbound link.
10images
Every raster image carries srcset, width, height and loading. No referenced raster over 300KB; no preloaded image over 200KB.
11tracking+consent
With tracking configured: one tag-manager loader per page, consent defaults and first-party capture ahead of it, the consent sentence verbatim. Without: zero Google tags in the whole build. A chat widget, if enabled, deferred to window load and placed after the loader.
12phone/zip
No forbidden ZIP anywhere in the build. Only the public number in tel: and sms: links; a tracked number never in JSON-LD and never where a crawler would read it.
13bans
The word warranty nowhere; the practice's own banned phrases nowhere; no Webflow CDN reference in any file. Locked compliance copy is exempted verbatim, and an exemption that no longer matches the page is itself a failure.
14tier
The persona switches match the output: the motion kit on every page or on none; a specimen says so on every page and is noindex; every declared font face resolves; no CDN script, canvas or WebGL anywhere.
03 · Site-wide families
The gate files its site-wide assertions under lettered families; sub-families share a letter with their parent. One card per family, each carrying the line the specimen's report actually printed for it. A family whose marker leaves the gate, or whose line leaves the report, fails this page's build.
Family A
The route registry and the built output match exactly. Every registered page is built; nothing is built that is not registered. Either direction fails.
Family B
No forbidden ZIP in any HTML, JS, CSS, XML or text file in the build. Every tel: and sms: link carries the public number. A tracked number never reaches JSON-LD, and a Google-only number never reaches an indexable page or a crawler-readable string.
Family C
Asserted in both directions. A practice with tracking carries exactly one tag-manager loader per page, with consent defaults and first-party capture ahead of it and the consent sentence verbatim. A practice without tracking carries zero Google tags anywhere. A chat widget, when enabled, is deferred to window load and placed after the loader; when disabled, no marker exists.
Family D
One h1 per page, root-domain canonicals, no vercel.app address in the markup, titles within 60 characters and descriptions within 155.
Family D2
The card a messaging app renders is fetched from the destination page, so a texted link's credibility is decided here. og:title present; og:image an absolute URL that resolves to a file at least 1200px wide at about 1.91:1, measured from the bytes. Skipped on staging, where there is no shareable link — flipping the practice to production turns it on.
Family D3
Every hand-typed absolute asset URL in a practice's config — logo, images — resolves to a real file in the build. A rename that would ship a 404 with a green build fails here instead.
Family E
Production: noindex pages excluded from the sitemap, the sitemap exactly the indexable set, and every noindex landing page still followable. Staging inverts it: every page noindex and nofollow, no sitemap, no llms.txt, a robots.txt that disallows everything, an X-Robots-Tag noindex header in vercel.json, and a registry that marks every page unindexable.
Family F
Every internal href resolves to a built page, a declared redirect, or a real file on disk. Every page has at least one inbound link. The pass line is guarded on both counts — it once printed zero dead links above nine recorded ones.
Family N
Every raster image carries srcset, width, height and loading. No referenced raster over 300KB. No preload-as-image candidate over 200KB, because preloaded bytes sit in the LCP critical window.
Family G
Every JSON-LD block parses. The Dentist node appears on the home page and nowhere else. Service pages carry Service with a name and provider; FAQ pages carry four to eight answer-first items; hubs carry an ItemList of the declared minimum; About and Contact carry their page types; every BreadcrumbList counts 1 to n.
Family M
The home graph defines the practice, the doctor and the website. Every @id reference on every page resolves to a defined node; no @id carries two types; no self-authored AggregateRating, Review or Rating; a telephone only on the practice node, and only the public number.
Family I
Every legacy route in config is in vercel.json with the same target. No redirect shadows a built page; no target is missing from the build; no redirect exists in vercel.json without a source of truth in config.
Family J
The word warranty appears nowhere in any practice's site. The practice's own banned phrases appear nowhere. No Webflow CDN reference survives in any file. Compliance-signed copy that legitimately contains a banned word is exempted verbatim, page by page — and an exemption that no longer matches the built page is itself a failure, so a stale exemption cannot keep masking the ban.
Family P
Every persona switch is checked against the built output in both directions. The motion kit on every page or on none. A specimen says so on every page and is noindex on every page. Every declared font face resolves and is preloaded. View-transition names unique per page. No CDN script, canvas, three.js, gsap or WebGL anywhere. Section modules render only where switched on.
Family S
Source-side and repo-level, run before any practice is audited. Within a single .astro file, the same class selector defined twice at the same media scope with conflicting declarations fails — the cascade would otherwise resolve it silently, which is how a page-level style once restyled the flagship hero. Redefinition under a different media query passes. Reported to the console, not to the per-practice file.
Runs against source; prints to the console, not to the report.
Also in the report
04 · Performance budget
Read from the Lighthouse assertions themselves. Measured on mobile emulation — 412×823, simulated throttling, 3 runs per URL — against the built static output, for every practice in scope. A change to a shared surface puts every practice in scope. Third-party tags are blocked during the run so the budget measures the practice's own payload, not tag weather.
Largest contentful paint
≤ 2.5 s
mobile, simulated throttling
Cumulative layout shift
≤ 0.1
no reflow after first paint
Performance score
≥ 85
Lighthouse category, out of 100
Image weight per page
≤ 1.5 MB
total raster bytes on the page
One line for every practice; no page carries an exception. A heavy image fails CI exactly like a wrong ZIP does — it does not ship and get noticed in a month. The measured values print to the log on pass and on fail, so the margin is visible before anything trips.
Specimen — a fictional practice built by NextGen Practice Solutions
05 · The committed file
The audit report the gate writes for Halden Cosmetic Dentistry — persona demonstration — cosmetic and elective — rendered from the committed file. The report carries no timestamp by design: an unchanged site regenerates byte-identically, so a dirty file in CI means the audit actually moved.
docs/audit-report-halden.md
Generated by scripts/launch-qa.ts against the built site. Deterministic — regenerate with pnpm build && pnpm launch-qa; CI fails if the committed copy differs.
Result: PASSED · 12 pages audited · indexing mode: staging (every page must be unindexable).
Per-page matrix
✅ pass · ⚠️ warn · ❌ fail
| Page | h1 | title | desc | canonical | og | index | schema | entity | links | images | tracking+consent | phone/zip | bans | tier |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| / | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /philosophy | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /services | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /services/smile-design | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /services/veneers | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /services/full-arch | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /services/whitening | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /consultation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /doctors | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /financing | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /contact | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| /privacy | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Site-wide checks
One elision, marked above. The taxonomy line quotes the specimen's own list of 9 banned phrases, and several of those words are retired vocabulary on this site — the gate that guards this page forbids them even inside a quotation. The count is shown; the words are not. Every other character is the file as committed.