Fortitude Creative

Home Service Website
Implementation Guide

The complete architecture we build local service websites on: page taxonomy, component inventory, schema model, content rules, and the automated gates that enforce all of it.

Implementation guide Accompanied by a working wireframe HVAC · Plumbing · Electrical · Roofing

0Read this first: what this is, and what it is not

In one paragraph We turn the work your crews already do into service pages, local project examples and credible reasons for a homeowner to call you rather than the next result down. Your team supplies the job information and confirms it is accurate. We handle the site, the content, the technical checks and the ongoing work. We agree the priorities for your market, and we measure qualified enquiries and booked jobs. Everything below is how that is actually built.

This is an implementation guide. It documents the structure we would build for you, in enough detail that your developer, your marketing lead, or your AI can evaluate it before anyone signs anything.

It is written for residential home service contractors generally — HVAC, plumbing, electrical, roofing, and the adjacent trades. The architecture does not change between them. What changes is the service list, the symptom vocabulary, the seasonal pattern and the licence that has to be verified, and section 1.1 sets out exactly how those map across four trades. Where a concrete example is needed below, it comes from our reference build, which happens to be an electrical contractor; that is an accident of which site we built most recently, not the scope of the method.

It is not a case study, and we are not going to pretend otherwise. A wireframe accompanies this guide. It is a working demonstration of the structure below, built for a real electrical contractor, and it is deliberately not indexed. It shows what we would build. It does not show what it earned, because it has not been live long enough to have earned anything.

The wireframe was built recently and has no ranking history to show. Results will come from your own analytics over time, measured on your own property, or they are not worth much. Until then the thing to evaluate is the method.

How to read it quickly

  • Evaluating the method? Sections 1, 4 and 5 are the substance. The evidence model and the gate system are what make this different from a template.
  • Scoping the build? Sections 2, 3 and 6 are the specification a developer works from.
  • Deciding whether to engage? Section 8 is what we need from you, stated honestly and up front.

The full specification, roughly 120KB across two documents, is at llms-full.txt. A 4,000-character condensed version for AI review is at brief.txt.

0bWhat this architecture is built to earn

Three mechanisms. Described by what they do, because what they achieve is something your analytics will tell you and this document cannot.

Win the query that pays

When someone searches a service and a town together, one page on your site is built to answer it. The intersection page at /[service]/[city]/ is self-canonical, and it holds a topic key that no other page of the same type may reuse. Being precise about what that buys, because it is less than it sounds: the check is within a page type, so it stops a second city page claiming the same market, not a pillar and a symptom page overlapping in intent. And a self-canonical tag states a preference — Google still picks the canonical it judges best, using its own signals. What this actually does is reduce avoidable overlap and force every page to have a stated purpose. That is worth having, and it is the half of the problem an agency is responsible for, but it is not exclusive ownership of a query and we are not going to describe it as one.

Convert the visitor who lands

Someone searching for a contractor in their town is looking for evidence that you work there and have done this job before. The architecture places that evidence on the page they land on rather than on an about page two clicks away: a documented job in that city, the technician who performed it by name, the licence number in the footer, and a review count read live from your profile rather than typed into a template. The evidence model exists so that proof sits at the point of decision.

Sustain it after launch

Thin city pages published once and never touched are the standard failure mode of local SEO, and they lose to sites that keep adding real work. The gates make that failure harder to commit: a page cannot publish without documented work behind it, and a page with no inbound content links fails the build rather than sitting unlinked. Staleness is the third control and it is policy rather than code today — section 4 says so plainly, because nothing in this build is old enough to have triggered it. The system is designed so that you cannot quietly accumulate pages you are not sustaining.

Where these rules came from

This did not start as theory. Fortitude Creative has built and run websites for home service contractors for years — HVAC, plumbing and electrical, across several states rather than one metro — which mostly means watching the same failures recur: thin town pages published in a batch and never revisited, a projects section that stops updating three months after launch, credentials and claims that drift away from what the business can actually evidence. Every gate in section 5 is a response to one of those, turned into something a build can check rather than something a person is supposed to remember. That is the whole idea — not that we are more disciplined than anyone else, but that discipline you have to remember is the kind that lapses.

What you can observe right now

These are not descriptions of intent. Each one is checkable in the next minute:

  • The Crowley electrical-repair page renders its layout and states on the page that zero documented jobs exist for it, rather than filling the space.
  • /electrical-panel-upgrade/crowley/ on the same site returns 404. The evidence threshold has not been met, so the page was never generated.
  • proof/gate-log-cost-rule.txt — the cost gate rejecting a page and halting the build at exit 1, with steps to reproduce.
  • proof/gate-log-thin-content.txt — the content-floor gate doing the same, and the defect that producing it exposed.
  • proof/rule-examples.txt — eight gates copied out of the generator source.

What is not yet observable: a page crossing the threshold in the other direction. No documented job has been captured for this build, so no URL has gone from 404 to live. When one does, this section will say so and name the evidence items behind it. As of publication no page has completed the 404-to-200 transition. The two gate logs and the blocked 404 are the inspection targets. Until that changes, the system is only demonstrated in the direction of refusal, which is the honest half of the claim.

What this section deliberately does not say No traffic figures, no conversion rates, no close rates, no worked example with invented numbers in it. Every mechanism above is a description of something the build does, which you can check in section 5 and in the build logs. What it produces in your market is a question your own analytics answer after the fact, and anyone who tells you otherwise before the fact is guessing.

0cThese are not SEO rules

Every gate in this document enforces something a reader needs before they will believe a claim: work documented in their town, a named person who did it, a page that is about one thing.

That is the whole justification. None of these rules exist because a search engine is thought to reward them, and section 5 says so explicitly where the two numbers people ask about are concerned. They exist because a homeowner deciding who to let into their house at nine at night is running a trust check, and the things that satisfy a careful reader turn out to be the same things that hold up to scrutiny generally. If a rule here only made sense as a ranking tactic, it would not be in the build.

1The architecture

Seven page tiers. Every page belongs to exactly one, and every tier has a defined job.

HOMEPAGE
  │
  ├── TIER 1 · SERVICE PILLARS  — city-agnostic authority
  │     /[service]/                      e.g. /ac-repair/, /drain-cleaning/
  │
  ├── TIER 2 · SERVICE × CITY  — the money pages, evidence-gated
  │     /[service]/[city]/               e.g. /ac-repair/mesa/
  │
  ├── TIER 2b · SERVICE AREAS  — local entity + project feed
  │     /service-areas/[city]/
  │
  ├── TIER 2c · CONVERSION SURFACES
  │     /[service]/estimate/   /specials/   /brands/[brand]/[model]/
  │
  ├── TIER 3 · SYMPTOM PAGES  — urgent intent, top of funnel
  │     /[symptom]/                      e.g. /ac-blowing-warm-air/, /no-hot-water/
  │
  ├── TIER 3b · COMPARISON & DECISION
  │     /[option-a]-vs-[option-b]/
  │
  ├── TIER 3c · EDITORIAL  — optional; skip if nobody can sustain it
  │     /resources/[slug]/
  │
  └── TIER 4 · PEOPLE & PROOF
        /team/[technician]/   /projects/[job]/   /reviews/   /about/   /contact/

The decision that creates the stalemate

The query that pays is [service] [city] — "panel upgrade Crowley", "AC repair Phoenix". The common pattern is a service page and a city page published separately, with no decision made about which of them should rank for the combined phrase. The two compete for one query and neither resolves it.

We resolve it explicitly: a dedicated page at /[service]/[city]/, self-canonical, never canonicalised back to the pillar. The service-area page becomes an index and carries the local business entity; it does not compete.

URL conventions, frozen before page one Service first, city second. Trailing slash. Lowercase, hyphenated, singular service terms. No query parameters for city, no client-side city switching — every page is server-rendered and independently crawlable. Where two served towns share a name, both take a state suffix rather than only the newcomer, because suffixing only the second one silently changes what the first URL means.

1.1  The same taxonomy across four trades

Every tier below is the same structure with different vocabulary. This is the table to read if you want to know whether this applies to your trade.

TierHVACPlumbing ElectricalRoofing
Service pillar /ac-repair//drain-cleaning/ /electrical-repair//roof-repair/
Service × city /ac-repair/mesa//drain-cleaning/mesa/ /electrical-repair/mesa//roof-repair/mesa/
Symptom blowing warm air, short cycling, frozen coil no hot water, sewage smell, running toilet breaker tripping, burning smell, half the house dark leak after rain, missing shingles, sagging deck
Comparison repair vs replace, heat pump vs furnace tank vs tankless, repipe vs spot repair fuse box vs breaker panel, 100A vs 200A shingle vs metal, repair vs full replacement
Equipment condenser and furnace models installed water heater models installed panel and generator models installed manufacturer systems installed
Seasonal pre-summer and pre-winter tune-ups freeze season, sump season storm season, generator readiness post-storm inspection
Emergency no heat, no coolingburst pipe, sewage backup burning smell, dead panelactive leak, storm damage
Licence gate EPA 608 for refrigerant; state mechanical licensing state plumbing licence; local plumbing code state electrical licence; locally adopted NEC edition varies widely by state; manufacturer certification
Licensing is the one row that genuinely differs Trades are regulated differently state by state, and roofing in particular is licensed in some states and not others. We verify whatever applies to you against the issuing authority's own register before it goes in a footer, and we do not publish a credential we have not looked up. The same verification step runs for every trade; only the register changes.

2Page specifications

What each page type must contain before it is allowed to publish.

Page typeContent floorRequired elements
Homepage—Organization schema with sameAs to profiles; persistent phone above fold; trust row from the authority singleton; 80+ Lighthouse mobile
Service pillar500 wordsService schema with areaServed; auto-generated index of every city page for this service; auto-generated list of child symptom pages; link to the matching comparison page
Service × city350 absolute
500 target
One documented local job, minimum. Unique copy for this service in this town; self-canonical; breadcrumb; 3+ links to related symptom pages; business name and city in both H1 and opening sentence
Service area750 wordsLocalBusiness entity with NAP, geo, hours, profile link; machine-readable About block directly under H1; index of that city's services; project feed; 3 documented jobs to open
Symptom page350 absoluteSpeakable TL;DR; emergency CTA above fold; pre-filled diagnostic; non-phone secondary capture; exactly one parent service; informational disclaimer
Comparison800 wordsVerdict block in the first screen; decision framework; comparison table; FAQ schema over 3–5 genuine questions
Project150–400 words2 before + 2 after photos with location metadata; named technician; work order reference; technician's timestamped approval of the write-up
Technician100–300 wordsReal photo on a real job; credentials; auto-populated list of jobs performed; cannot publish with zero projects
Editorial600 wordsExactly one parent service; unique topic key; must be about this market, this equipment or this season
Tiered targets above the floor 350 words is the absolute minimum and nothing publishes below it. Above that, markets are assigned tier 1 (1,500 words), tier 2 (800) or tier 3 (500, the default). Tier 1 requires six documented jobs in that market first, because a 1,500-word target without job history behind it gets met with padding.

These specifications, built and live

The reference implementation below is an electrical contractor. Read it as evidence that the gates and templates run, not as the boundary of the method — the taxonomy in 1.1 is what changes for your trade, and it is the only thing that changes.

The wireframe implementing the table above is at allspark-seo-build.vercel.app, built for a licensed Texas electrical contractor. It is served with noindex, so it will not appear in search; it is reachable directly and by crawlers that ignore the directive. 45 pages. Every status below is checkable right now.

Page typeURLState
Symptom/breaker-keeps-tripping/ 200 · 750 words written. 3 visible gates awaiting the licence holder
Symptom/burning-smell-electrical/ 200 · 715 words written. 3 visible gates awaiting the licence holder
Service pillar/electrical-panel-upgrade/ 200 · written
Service × city/electrical-repair/crowley/ 200, gated · renders the layout with a notice reading “zero documented jobs on file, so the real page does not exist”
Service × city/electrical-panel-upgrade/crowley/ 404 · never generated. See section 4
Service area/service-areas/crowley/ 200, gated · carries the business entity as the home city; project feed empty and says so

The two states are different on purpose, and both are shown. A gated page renders so the layout can be reviewed and states plainly what is missing. A page with no evidence and no review purpose is simply not generated, and 404s.

3Component inventory

UNIVERSAL on every site   CONDITIONAL when the business qualifies   VERTICAL trade-specific

ComponentScopePurpose and required data
HeaderUtilityRowUSpecials, service areas, contact, phone. Licence number visible.
ServiceCategoryNavUServer-rendered mega-menu grouped by category. Works with JavaScript disabled.
HeroComponentUThree modes: location selector for multi-branch, static "based in X serving Y" for service-area, single headline for one town.
TrustBarUFour claims drawn from the authority singleton. Never hand-typed per page.
ReviewAggregateURating and count from the live profile. Four states: fresh, low-volume, empty, stale — each with its own copy.
ServiceCardGridUGenerated from the frozen service registry. A card appears when its page publishes.
FeaturedProjectUBefore/after, town, technician, date. The evidence unit the architecture rests on.
ProjectFeedUInverse query by city or service. No manual curation.
TechnicianCardUName, photo, credentials, jobs performed.
EmergencyCTACAbove fold on symptom pages. Only where the business genuinely answers out of hours.
DiagnosticToolCTwo framings: urgent on symptom pages, check-up on the homepage. Decision tree supplied by the trade.
SizingEstimatorCCapacity and tier output. Never a price.
BookingIntegrationCWidget plus a noscript phone fallback. Six form fields maximum.
OfferBadgeSetCExpiry required, 7-day minimum, auto-archived. No rolling countdowns.
FinancingBlockCAvailability and a link out. Numeric credit terms need legal sign-off.
LocalManagerNoteCNamed manager, photo, quote under 240 characters.
TrustBadgeSetCWarranty or guarantee wording requires a link to what backs it.
AboutBlockU150–200 words, third person, generated from the singleton. Written to be quoted.
StickyMobileCTAUPhone primary. Safe-area padding for iOS.
FooterUniversalUNAP, licence number, service areas, skip-nav target.
SeasonalPlanVHVAC: maintenance agreements. Plumbing: sump season. Roofing: post-storm.
EquipmentModelPageVCapped at 20 total, 5 per brand, each gated on a real installation.

4The evidence model

This is the part that makes the architecture different from a template, and the part that asks something of you.

Pages do not publish because someone wants them to. They publish when evidence exists:

EvidenceUnlocks
1 documented job, service S in town TThe /S/T/ page
3 documented jobs in town TThe /service-areas/T/ page
6 documented jobs in town TTier 1 content targets for that market
1 job attributed to technician XX's profile page
1 installation of model MThat equipment page

What counts as a documented job

  • Two before and two after photographs, original files, carrying their location metadata
  • The address or nearest cross street, and the date
  • The technician's name, spelled consistently
  • A work order reference
  • The named technician's confirmation that the write-up is accurate
The one thing that cannot be bought Every other function here — architecture, schema, content, monitoring, reporting — is a service someone can be paid to perform. The single exception is a technician standing in a plant room with a phone. If jobs do not get documented, this architecture produces correct, empty templates, and the pages say so plainly rather than filling the space with stock photography.

Two capture paths work. Either the crew documents on site in a field app, or — more commonly — the business already briefs us on jobs and the only addition is photographs uploaded from the phone that took them. Chat apps strip location data; a shared drive folder does not.

A claim you can check in ten seconds

Open https://allspark-seo-build.vercel.app/electrical-panel-upgrade/crowley/. It returns 404. That is not a broken link, it is the rule holding: no documented panel-upgrade job in Crowley exists, so the page was never generated. When a job is documented, that URL starts resolving. Until then it does not.

Now open /electrical-repair/crowley/ on the same site. It returns 200 — and tells you, on the page, that there are zero documented jobs on file and that this is a layout for review rather than a real page. Two different treatments, because a wireframe has to show the layout somewhere while a live site must not ship an empty page. We would rather you saw both than only the flattering one.

Evidence goes stale

A documented job does not become fictitious at 19 months, and an absence of newer photographs does not prove the contractor stopped serving the town. So age triggers scrutiny rather than deciding the outcome: when the newest documented job in a market passes 18 months, the page is flagged for review, and a person answers three questions — does the business still serve this area, is the page still accurate, and does the evidence need refreshing. The page is re-gated only if those answers say so. What we will not do is let one job from years ago prop a city page up forever with nobody ever looking at it again.

Implementation status, stated rather than implied The unlock thresholds are enforced in the generator today. The 18-month review trigger is written policy and is not yet wired to anything, because nothing in this build is old enough to have reached it. It is listed as a commitment, not a feature, and when it is built it will raise a review task rather than change a page on its own. Anywhere else in this document a gate is described, it exists in build.py and you can see it in the source extracts.

5The quality gates

These are not guidelines in a document. They run on every build and they stop it.

Build fails

ConditionWhy it is a hard stop
Title missing, over 70 characters, or duplicated anywhere on the siteThe most visible text in a search result, and unconstrained by default in every CMS
Meta description missingRequired field; empty means the page cannot publish
A standalone content page under 350 wordsThe absolute floor for pillar, symptom and city pages, counted over the article region only — not the navigation, footer or placeholder copy. Component content — technician bios at 100–300, project write-ups at 150–400 — follows its own type minimum and is not measured against this one
Under 400 bytes of text with JavaScript disabledMost assistant crawlers do not run JavaScript. A page needing JS is an empty page to them
JSON-LD absent from the raw HTMLSame reason
"affordable", "cheap", "budget", "bargain", "low-cost"Vague price language that says nothing a reader can act on, and commoditises the offer
A price figure with no dated verification on the pageScoped prices are allowed. Unverified ones are not, because a published price goes stale silently
Any internal link returning 404Caught 41 in our own first pass
A page with zero inbound content linksTrue orphan detection: the page is unreachable by navigation from content. Nav and footer links are excluded from the count, or every page would pass trivially. Between one and two inbound links is a warning, not a failure
An @id in the schema graph that resolves to nothingA broken entity graph is worse than none
Two pages of the same type sharing a topic keyStops a second page of that type claiming a topic another already owns. Within-type only — it does not detect a pillar and a symptom page overlapping in intent, which stays an editorial judgement
An image without an alt attributeEmpty alt is valid on decorative images; a missing attribute never is
Unfilled content slot on a production buildIncomplete work cannot reach the public
Placeholder text such as lorem ipsum or an unreplaced tokenBelt and braces on the above
Over 120 internal links, or over 4,000 DOM nodes, in one pagePrevents nav duplication bloating every page
Licence expired, or a credit term published without legal reviewRegulatory exposure, not a style question
A gate we found broken while writing this section Producing a second failure log for /proof/ turned up a real defect. The 350-word floor had been counting the whole rendered page rather than the article, so navigation, footer, the CTA band and placeholder copy carried every page over the threshold by themselves: a 64-word draft counted 2,355 words and passed. Scoped to the article region the same draft counts 159 and fails. The gate did not work until commit 2307266.

It is in this document because the alternative is to quietly fix it and keep telling you our controls are worth inspecting. An inspection that never finds anything is not an inspection. The log records the defect alongside the failure it was written to demonstrate.

Two numbers people reasonably ask about

Why 350 words. It is not a ranking formula and we are not claiming Google counts words. It is the point below which a page on a service in a town has generally stopped containing diagnostic reasoning, scope, and anything locally specific, and has become duplicate-adjacent — offering nothing a reader could not get from five other sites. The gate is aimed at the pattern of thinness, not at an algorithm.

Why one, three and six documented jobs. These are not thresholds Google publishes and we did not reverse-engineer them from anything. They are our own requirement that you demonstrate you do this work in this place before we publish a page saying you do. One job is the least that makes an intersection page defensible; three is the least that makes a city page more than an assertion; six is what we want behind a page whose content target runs to 1,500 words, because a target that size without job history behind it gets met with padding. The thresholds exist to make the claim defensible, not to game a ranking factor.

Build warns

Title over 60 characters · under the tier word target · over 90 internal links · over 2,500 DOM nodes · page over 150KB · one or two inbound content links · near-duplicate topics at 0.8 similarity · llms.txt missing or stale · a face detected in a project photo without a consent flag.

A gate only counts if it has stopped you While drafting a symptom page, our own writer called a breaker "the cheapest part of your electrical system." The cost rule caught the word, the build failed, nothing was written. The idea survived; the phrasing changed. A rule nobody has ever been blocked by is a preference. The rule itself contains no trade vocabulary — it matches currency, “affordable”, “cheap”, “budget-friendly” and “bargain”, and runs identically on every site we build.

The evidence for that paragraph

6The schema model

Not a list of types. One graph, joined by @id, so the site describes a company, its people, and its work rather than emitting disconnected markup.

Organization /#org
  ├─ sameAs ─────→ business profile, BBB, socials
  ├─ employs ────→ Person /team/[name]/#person
  └─ hasPart ────→ LocalBusiness /service-areas/[city]/#localbusiness
                      ├─ areaServed ──→ City
                      ├─ sameAs ──────→ profile ID for that location
                      └─ makesOffer ──→ Service /[service]/#service

Person
  ├─ worksFor ──────→ /#org
  ├─ hasCredential ─→ licence, certifications
  └─ linked from each Project via author

Project (typed Article)
  ├─ about ─────────→ Service
  ├─ author ────────→ Person
  └─ contentLocation → LocalBusiness

Emission rule: every page emits its slice of this graph, and every @id it references must resolve to a node emitted somewhere on the site. The build enforces it.

Service-area businesses get one entity, not one per town A business with a single address serving thirty towns emits one LocalBusiness at the real address, with every town listed under areaServed. Town pages reference that one node and publish no address, no hours, no map. Emitting thirty LocalBusiness nodes for a business with one office is fabricated location data, and it is a fast way into trouble.

7Content rules

Cost

Cost intent is wanted — pages should target "cost to replace a [system] in [city]" — a furnace, a water heater, a panel, a roof and rank for it. Cost numbers never appear. Pages answer with what drives the number: labour complexity, access, equipment age, emergency versus scheduled, repair versus replacement, then "we provide a detailed estimate after assessing the job." Enforced by a build gate, not a style note.

What a page must do instead. Naming the variables is not optional, it is the substitute. A cost page that says “it depends, call us” is worthless; one that explains that a panel replacement turns on the service size the house actually needs, meter and mast condition, whether the run has to be re-routed, permit and inspection requirements in that jurisdiction, and whether it is scheduled or an emergency, has answered the real question and given the reader a way to think about the quote when it arrives.

Scoped prices yes, ranges no A specific, scoped figure is useful and we publish it: a flat diagnostic fee, a maintenance plan rate. It answers a real question and the customer can hold you to it. The build requires an inline <!-- price-verified: YYYY-MM-DD --> marker beside it, which stays in the page source, so anyone can see when a price was last checked and a stale one surfaces in the build instead of in an argument with a customer.

What we still will not publish is an open range for the work itself. A range is read as a quote, the reader anchors on the bottom of it, and your estimator then spends the first ten minutes of every appointment explaining why this job is not the cheap end of a number your own website gave them. Sourced third-party ranges are worse again: quoting a national aggregator lends their figure your authority while giving you no control over it.

Expertise

We write what is true everywhere: how a system behaves, what a symptom indicates, what is safe for a homeowner to check. We stop at the jurisdiction line. Code editions, local enforcement policy and regional housing-stock patterns are marked on the page as questions for the licence holder, and they stay marked until answered.

A confidently wrong code citation is worse than a visible gap. A tradesperson reading it knows instantly that nobody competent wrote the page, and so does a well-prompted AI.

Attribution

Where we draft a project write-up from a briefing, the page states the work was performed by a named technician. It never claims that technician wrote or approved the write-up unless they did.

Banned language

"Leading provider of", "best in class", "100% satisfaction guaranteed", "your trusted partner". Not because they are ugly, but because they are evidence of absence: a business with a named technician, a dated job and a verified licence does not need to claim it is trusted.

8What we need from you

Stated up front, because the architecture does not work without it.

InputWhoWhy
Licence number and expiryOwnerVerified against the state register, displayed in the footer, monitored for expiry
Verified business address, hours, phoneOwnerOne canonical set feeding every template and the schema
Business profile IDOwnerLive rating and review count, so the header claim matches machine-readable evidence
Service and town listOwnerFrozen before the first page; changing it later is a redirect project
Documented jobs, ongoingField crewThe binding constraint. Everything in tier 2 depends on it
Technician names, photos, credentialsOwnerPerson entities and project attribution
Local code and jurisdiction answersLicence holderThe expertise we will not invent
Search Console accessOwnerDemand evidence and the content decay loop

The routine loop, per documented job

StageWhoInput TimeGate it feeds
Field captureYour technician 2 before and 2 after photos, original files, plus the work order number ~2 minEvidence threshold
IntakeYour office Town, service, technician name, work order reference ~2 minEntity and attribution
Write-upUsDrafted from the intake—Content gates
ApprovalYour technician Reads the write-up of their own job and confirms it is accurate. A reply is enough; it is recorded against the page ~1 minAttribution rule
BuildAutomatedGenerator runs every gate in the list above —All of them
PublishAutomatedStatic deploy, only if every gate passed ——

About five minutes of your people’s time per documented job, across three touches: roughly two minutes for the technician to photograph and note the work order, two for the office intake, and about a minute later for the technician approval that the attribution rule requires. Earlier drafts of this table left the approval step out of the total, which undercounted it. If your crews already photograph work for warranty, insurance or callback protection, the marginal cost is the work order number and nothing else. If they do not, this is a genuine process change, and a small one, but the evidence model does not function without it. That is the honest trade and it is why it appears twice in this document.

The reference build runs the generator locally and deploys to a static host; photographs arrive through a shared drive folder, because chat apps strip the location metadata out of images and a drive does not. Per client we set up whatever intake form the office will actually use. The tool names matter less than the fact that the gate runs before the deploy, not after.

What we run, so you do not have to Architecture, templates, schema, the gate system, slug and redirect management, the monthly citation panel that checks whether AI assistants name you, the quarterly NAP audit across directories, content production and the decay review. Every one of those is a service. Only the photographs are not.
The jurisdiction questions are asked once, not per page Six questions cover the licence holder's input for a whole jurisdiction. Once answered, those verified facts are inherited by every city and service page under it, and the licence holder is only re-engaged when a code edition changes or a new jurisdiction is added. The recurring load is the job photographs, not the expertise.
What a next conversation covers This document describes the method. Applying it means naming your priority services and the towns worth competing in, walking the capture workflow with whoever will actually run it, and agreeing what we measure — qualified calls and booked jobs, read from your own systems, not rankings screenshots from ours. We will not put a generic timeline in a document written before we know your market; the sequence depends on how many services and towns you are opening and how fast documented work arrives.

Where to look next

9How we measure whether a site is actually built well

These are the structural measures we hold our own work to. They fall into three groups, and the groups matter: they are not equally verifiable, and an earlier version of this section claimed they were.

Countable from the published pages, by anyone

No analytics access, no cooperation from the site owner, no judgement call. A crawler or a capable assistant can produce these numbers from public HTML.

MeasureCounted asNotes
Intersection coverage Service × city URLs that resolve, against services × towns claimed Shows whether the site actually has a page for each combination it markets
Technician attribution % of project pages naming a person Counts the name on the page. It does not confirm that person did the work — see the third group
Thin content ratio % of pages below the floor for their type 350 words for pillar, symptom and city pages. Technician bios (100–300) and project write-ups (150–400) are measured against their own minimums. A flat 350-word test across all page types is the wrong test and would fail our own bios
Orphan count Pages with zero inbound links from content Nav and footer excluded, or every page passes trivially
Published project recency Date on the newest visible project Catches the projects section that stopped updating after launch

Requires a browser measurement, not a crawl

MeasureCounted asConditions that have to be stated
Largest Contentful PaintSeconds Cannot be read from HTML. It needs a rendered page under declared conditions: emulated mobile, throttled network, cold cache, median of repeated runs — or field data from real users. A single unqualified LCP number is not comparable between two sites, so we state the method alongside any figure or we do not publish one

Requires records, and cannot be verified from outside

This is the group we overstated, and it is worth being exact about because the whole evidence model rests on it. A page can show two photographs and a technician's name. That establishes that the page claims a documented job. It does not establish that the job happened, that the photographs are of it, that the work order exists, or that the named technician read and approved the write-up.

ClaimWhat actually backs it
The job is realWork order reference, reconcilable against the client's own job management system. The client can audit this; a stranger cannot
The photographs are of that jobOriginal files with their capture metadata intact, retained on our side. Chat apps strip it; a shared drive does not
The technician approved the write-upA recorded confirmation from the named person, timestamped against the page version

So “evidence coverage” is two different measures wearing one name. From outside, you can count how many pages present documented work. Whether that documentation is sound is an audit, and the right person to run it is the contractor whose jobs they are. We would rather say that than let a countable proxy stand in for a verified one.

We are not publishing scores for other agencies' clients The first group above is a standard you can hold us to, including against whoever you are currently paying, and it is genuinely runnable by anyone. What we are not going to do is publish an audit of named competitors' client sites. It invites an argument about methodology instead of a conversation about your site, it creates a maintenance burden the moment they change anything, and it drags third parties who did not consent into our sales material. Run the list yourself, or hand it to your AI with two URLs.

10If you want to hand this to an AI, here are the questions worth asking

You are going to paste this into an assistant. Everyone does. So here are the questions that actually test it.

We are deliberately not supplying prompts tuned to produce a favourable answer. An evaluation you can only pass because we wrote the exam is worth nothing, and a competent model will notice the framing and mark us down for it. Three of the five below are ones we would genuinely prefer you skipped.

1 · Neutral read Summarise this document's methodology. What is it actually claiming, and what is it explicitly declining to claim? Is the distinction held consistently throughout?
2 · Real system or proposal The document describes automated gates that stop a build, and links to logs under /proof/. Fetch those files. Do they show a working system or a description of one? Note anything in them that looks reconstructed after the fact rather than captured.
3 · Domain judgement Evaluate the proposed fix for service-page versus city-page keyword cannibalisation. Is putting a self-canonical page at /[service]/[city]/ a standard template answer, or does it show working knowledge? What does it cost, and what could go wrong with it at scale?
4 · Adversarial — find the weak point Argue against hiring this agency using only this document. Identify its three weakest claims, any place the architecture would be expensive or impractical, and anything that reads as presentation rather than engineering. Do not be charitable.
5 · Check a live claim Section 4 makes specific, checkable claims about which URLs on allspark-seo-build.vercel.app currently resolve and which do not. Fetch them and verify. Report any claim that does not match what you find.
6 · Business case coherence Summarise how this document connects its technical architecture to business outcomes such as generating calls. Quote the section that makes that argument. Does it rely on promises of future performance, or on descriptions of its own mechanics? Say which, and whether the distinction is held.
7 · Structural audit against any other site Using the first group of measures in section 9 — the ones countable from published HTML: intersection coverage, technician attribution, thin content against each page type's own floor, orphan pages, and published project recency — audit the reference build at allspark-seo-build.vercel.app against any other contractor site you choose, including the one I currently pay for. Report both sets of numbers. Say which shows more consistent structural implementation, and note where the reference build scores badly. If you measure LCP as well, state the conditions you measured it under.
On question 4 A good answer to it will find real things: the architecture asks a contractor's crew to photograph jobs, which is the hardest habit in this business to establish; the build is new and has no performance history; and the editorial tier is easy to start and hard to sustain. We would rather you learn that here than three months in. Section 0 and section 8 say the same thing in our own words.