@brandlab-digital/conformance
Proprietary software © Brandlab Digital Marketing. All rights reserved. Published for Brandlab client sites.
Service-area maps (from 3.6.0)
brandlab-area-map places --out src/data/locations/siteOwnedBoundaries.json Young@2594 Cowra Cootamundrawrites a site's own towns (outside the Sydney and Central Coast registries) and the land around them in the shared area-map format, from the NSW Spatial Services suburb layer (name, and postcode where it repeats; registry places are refused). Options:--margin-km(land around the towns, default 15) and--tolerance(metres, default 20).- The estate journey fails a service-area map (
<figure data-area-map>) without a caption carrying the CC BY boundary credit, with a link that is a tab stop or does not go to an indexable catalogue page, with more thanareaMapMaxBytesinline (default 60 000), or whose<use>base-layer file does not answer 200 asimage/svg+xmlwith the groups it uses.
Site System ready gate (from 3.8.0)
A site adopts Site System v2 by declaring siteSystem: { tier: 'shell' | 'enquiry' | 'full' } in
brandlab.config.ts (newBuild: true for new builds and redesigns; samples adds pages to check; labels,
freeQuotes, thirdParties and phones record the quote labels, free-quote offer, third-party hosts and extra
tel: links the shell rules accept; refreshExceptions lists estate families allowed to refresh more often than
every 7 days, each { family, revalidateSeconds, reason, decisionId }, for W3 from 3.13.0; replyTime records a
decision to promise no reply time on the thank-you page, for C7 from 3.15.0, below). Without it, nothing in the gate
blocks.
C7, the thank-you page (enquiry tier): every sampled thank-you page (a /thank-you-style path or the library's
ThankYou block) is noindex, follow, has a tel: link in the block (or <main>) and says in its reply slot
(data-slot="reply") when the business will reply, as a specific time ("within the hour", "within one to two business
days"; C7_REPLY_TIME). Vague words such as "shortly" or "as soon as we can" fail. When the owner decides to promise
no time, the site records it (3.15.0):
siteSystem: {
tier: 'enquiry',
replyTime: { promise: false, wording: 'We will call you back as soon as possible.', decidedBy: 'owner', decidedOn: '2026-10-24' },
},
C7 then accepts that wording in the reply slot instead of a time (compared ignoring case, spacing and punctuation),
fails a page without it or one that still promises a time, and names the recorded decision in its pass message. The
wording may not contain a time itself (digits, minutes, hours, "same day", "next day", a business or working day,
"today", "tomorrow"); decidedBy is owner or romie (for the owner) and decidedOn a real date. { promise: true }
or no record keeps the specific-time rule. Noindex, the call link and everything else C7 checks are unchanged.
-
brandlab-ready(afternext buildand the site-system journeys) writesdocs/ready/ready-report.mdand.json: every check with its tier, whether it blocks for this site, its status (pass, fail, not run, excepted, n/a; a pass may carry warnings, which never block) and what was found. Checks of the declared tier and the tiers below it block from the release that brings them; a blocking check that failed or did not run exits 1, and so does a missing home or contact page, a--base-urlsample that does not answer with HTML, or abrandlab.config.tsthe gate cannot read in full. Higher tiers and later releases report only. It reads the Playwright JSON report (--playwright-report, defaulttest-results/site-system.json; test titles start with the check id,[N3] …), runs the rule modules over sample pages (home, contact, the representative routes, one page per other built route andsamples) read from the build or fetched with--base-url, merges--findings <file>results for K10 and X1 (the lenient harness) and the brandlab-verify search, contact and analytics families, and honours the project'sexceptions(ruleIdis the check id; expired exceptions do not count). -
The journeys come from
tests/e2e/site-system.spec.ts, run with the JSON reporter intotest-results/site-system.json:import { defineBrandlabSiteSystemTests } from '@brandlab-digital/conformance/site-system-e2e' defineBrandlabSiteSystemTests({ tier: 'full', // the tier brandlab.config.ts declares pages: { home: '/', money: ['/services/blocked-drains'], // C1, from the enquiry tier: service pages with the enquiry form contact: '/contact', families: ['/locations/ryde', '/blog/blocked-drain-cost-sydney'], // W9, full tier: one built page per dynamic route family }, })A journey registers from the release that brings it (W2 and W9 from 3.13.0). The release is the site's installed Brandlab release, read from package.json as
brandlab-readyreads it, a canary included (3.15.0: before it the journeys used the conformance package's last published release, so a canary ran none of its newer journeys while the gate blocked on them). Leavereleaseout; set it only to preview a later release's journeys. When the report has journey results but none for a blocking journey check, the gate says which tier, pages and release it needs. -
brandlab-ready record <id> --evidence <where the proof is> [--note <text>]records a launch-day step (T2, T3-R, C6, X3 to X10) or a recorded check (K10, X1) indocs/ready/launch.json, or a human step (truth-pass, trust-carry-over, usefulness-read, codex-second-opinion, launch-proof, look-pick) indocs/ready/human.json.brandlab-ready --launchalso fails until every step is recorded. -
Rule modules export a
SiteSystemRenderedRunnerfrom@brandlab-digital/conformance/site-systemand are listed inSITE_SYSTEM_RENDERED_RUNNERS. -
W2 (shell tier, from 3.13.0) runs inside the site-system journeys on each sampled page at 390 x 844 and 1440 x 900: at most 8 prefetched pages on landing and 12 after a full scroll; none from footer, header panel, drawer, area (
coverage), nearby (nearby-areas), map (area-map,data-area-map), finder or other-services links (a site marks its own lists withdata-bl-links="area|nearby|map|finder|other-services"), none through a hidden link and never the current page. Compressed prefetch bytes above twice the page's own HTML add a warning. -
brandlab-prefetch-probe <url> [--viewport 390x844 ...] [--json] [--strict]measures one page locally (a local build, a preview or a live page) in headless Chromium: the pages prefetched on landing and after a full scroll at each viewport, their compressed and decoded bytes, the edge cache result and the W2 result. No request leaves for another site, and it never crawls.
Release and CI tools (4.2.0)
brandlab-preview-enquiry <preview-url> [--changed-from origin/main] [--path /contact] [--field key=value]...sends one marked test enquiry (bl_test=1) through a pull request's Preview when the change touches contact or runtime files (package files,next.config,vercel.json, the proxy or instrumentation,api/contact, contact config and forms); otherwise it sends nothing. It first posts an empty marked request, which the route rejects without sending, and goes on only when the answer carriesx-brandlab-contact-mode: preview; test-inbox=brandlab(site-next 4.2.0+ on a Preview), so it can never reach a client's inbox. The enquiry uses ordinary wording that must pass the spam filter, with [email protected] as its email. From 4.2.2 it fills the site's own form: it reads the Preview's contact page (--path, default/contact), gives the usual fields test values, any other required field a plain value and a required select one of its own options, keeps hidden values, leaves honeypots empty and sends no field the form lacks (the route refuses unknown fields).--field key=valuesets one of the form's own fields; a name the form does not have is refused. A page without the form in its HTML is refused before anything is sent. A protected Preview needsVERCEL_AUTOMATION_BYPASS_SECRETin the environment. Then confirm the email arrived.brandlab-live-check <https://www.site> --service /path --location /path [--css token]... [--vercel-project name --vercel-scope team]is the check after a release goes live: home, the top service page, one location page and contact answer 200,sitemap.xmlandrobots.txt200, a made-up URL 404, each--cssclass or token is in the stylesheets home links, and (with the Vercel CLI signed in) the production deployment, the rollback deployment (vercel rollback <id>), its runtime errors and any 5xx from/api/contactsince--since(30m). Requests are one at a time, at least 0.5 s apart, and stop at the first 403. It never runs page JavaScript, so no GA4 session.@brandlab-digital/conformance/playwrightexports a Playwrighttestwhose browser context blocks Google Analytics, Tag Manager and Ads requests in every test,expect,blockAnalytics(contextOrPage)for one-off scripts andisAnalyticsUrl. Use it for any site-owned spec or script that loads a page, above all a live one; the shared journeys already fail closed for every other host.BRANDLAB_ESTATE_SCOPE=changed(set by CI v2 throughbrandlab-estate-scopeon a ready-for-review pull request whose files stay inside page families) crawls the hubs, the first and last route of each kind, every route of a changed family, the routes changed content and data files feed and 20 fixed-seed routes; the family journeys open only the changed families and home. Shared changes keep the full crawl, which also runs on the merge to main.
Contact journeys
defineBrandlabContactTests and the contact journey of defineBrandlabAnalyticsTests expect an accepted enquiry
to land on /thank-you. A form that confirms on its own page passes successPath (a path or a RegExp), for example
successPath: '/contact'. Both block every request that leaves the site, Google's tag included. A site that takes
over scrolling (GSAP ScrollSmoother, Lenis) can move the form after Playwright scrolls to the submit button; run the
journeys at a width where the site scrolls natively: test.use({ viewport: { width: 390, height: 844 } }).
Cost guards (from 3.13.0)
- W5 (static, shell):
vercel.jsonregionsequalbrandlab.config.tshosting.originRegion(Sydney,syd1, when there is no hosting block). Any other region needs anorigin-region-v1decision whosereviewByis at most six months afterdecidedOn; the check fails oncereviewBypasses (Sydney time). The engineering checkvercel-region(brandlab-verify --engineering, inside every site'snpm run verify) applies the same rule except the date: a lapsedreviewBynever stops a build, a fix or a rollback. - W10 (rendered, shell): the built
robots.txt(or the running site's, with--base-url; a Preview that answers noindex, or a build made for a Preview whoserobots.txtdisallows everything, is not judged) lets every search and answer crawler (site-coreBRANDLAB_SEARCH_CRAWLERS) fetch home and the representative routes, and gives none of them a Crawl-delay. Firewall rules that match on the user agent (vercel.jsonrouteswithmitigate, andhosting.firewall) must not deny or challenge them; a rate limit on one is a warning. - W9 (journey, full):
defineBrandlabSiteSystemTests({ pages: { families: [...] } })names one built page per dynamic route family; an unknown slug answers the shared 404, the uppercase slug answers it too or redirects once (301 or 308) to its exact lowercase form, and?utm_source=xserves the same cacheable page as the clean URL. W9-params (static, full): no page under a dynamic route readssearchParams. - K12-slots (rendered, full, from 3.14.0): the lines a trade requires inside a section (site-core
SECTION_TRADE_PROFILES, for the tradedefineSectionOrdernames): for trees, the council permit note inLocalFactson suburb and service-in-suburb pages and the qualifications and insurance lines on the about page. The section carriesdata-bl-slot="<slot>", or lists<slot>:no-…indata-bl-slot-skip(a warning, so the record is re-checked). The skip is read only from the top-level section element (the outermostdata-bl-section): a site element that wrapsLocalFactsin its own section hides the skip, and the page fails as if no reason were given. RenderLocalFactsitself as the sequenced section, not inside a wrapper. K13 (rendered, full, from 3.16.0): signature sections (data-bl-section="signature") carry adata-bl-signaturename that their manifest lists, at most two on the home page, one on a service page and none elsewhere (site-coreSIGNATURE_LIMITS), and the home page's are the onesstrategy.tsrecords inlook.homeSections.signatures, so D4 compares the real look (a recorded one the home page doesn't show is a warning). K12 leaves placed signatures out of its order walk and, from 3.16.0, warns when an optional or recommended section with data drops for the cap. E1-pattern (rendered, full, from 3.14.0): every location page outsidedocs/recorded-titles.jsonhas the H1 "{Service} {Place}" (no "in", no "the") and a title that starts with it, at most 60 characters (site-corelocationHeadingProblem). LOC-INDEX also readsestatein strategy.ts (3.14.0): every location page is indexed now or in a wave, areturnwave's pages are in the build already, and a region page at a suburb's slug sends the suburb to "{slug}-suburb"; renamed slugs are read as their registry places. - Estate plan snapshot (3.15.0): the gate reads
wavesandestatefrom strategy.ts without running it. A site that computes them (from its location catalogue, as AA does) records them withwriteEstatePlanSnapshot(siteStrategy, { projectRoot, sources? })from@brandlab-digital/conformance/estate-snapshot, called from a site test sonpm run verifykeepssrc/data/estate-plan.generated.jsoncurrent (fail that test in CI when it reportschanged; commit the file). The file is deterministic: waves in date order, every list sorted, no timestamp, and a hash of the files undersources(defaultsrc/data; tests and*.generated.jsonleft out). LOC-INDEX (and L3, L6, L8, which read the same plan) use the snapshot when it is present, and LOC-INDEX fails it when it is stale: the hashed files changed since it was written, or the record is plain data and says something else. A literal record needs no snapshot. - Media snapshot (3.15.0): T6 reads
src/data/media.tsthe same way. A computed manifest (gallery entries spread in) is recorded withwriteMediaSnapshot(media, { projectRoot, sources? })from@brandlab-digital/conformance/media-snapshotintosrc/data/media.generated.json, kept current from a site test in the same way; T6 reads it, and fails it when stale. A computed manifest without a snapshot fails T6 with that instruction (before 3.15.0 it reported nothing, so the gate showed "No result"). brandlab-edge-probe [--json] <url>...(run locally asnpm run probe:edge, at most 25 URLs) logsx-vercel-cache,age,x-vercel-id(edge and function region) and time to first byte for each page, its RSC payload and its_treeprefetch, and a GET to each origin's/api/contact(expected 405). It is the measuring stick of the origin-region benchmark (operations/shared-platform/ORIGIN_REGION.md).