Version 4.2 — Implementation Specification

Home Service Website Architecture

This is an implementation specification, not a wireframe. It defines what content must exist, the data structures beneath it, and how quality is enforced at build time. It contains no page layouts and it is not a client-facing document — if you are looking for what the site will look like, this is the wrong artifact.

v3 was judged an 85th–90th percentile architecture that could not be built as written, because it deferred five load-bearing decisions. v4 makes those five decisions and writes them as a spec an engineer can implement without asking a follow-up question. Everything in this document either resolves a deferred decision or states a rule that a template must enforce. Later rounds added conversion surfaces on top of this architecture without changing it: the installation estimator, capped equipment pages, specials and the review counter (annex section M), and the conversion and trust modules in annex section N.

This page is one of four documents. The set:
DocumentWhat it holdsPlain text
Wireframe v4
this page
The architecture. Sitemap, the service × city intersection decision, page wireframes, build sequence. /wireframe-v4.md
Implementation Annex The build spec, sections A–L. CMS validation, slug registry, JSON-LD graph, CI gates, AI retrieval, off-site authority and measurement. /annex.md
AI Visibility Runbook The human protocols behind section L. GBP cadence, review velocity, NAP audit, the monthly citation panel, named owners. /ai-visibility.md
Operational Runbook Deployment and maintenance. Non-production mirrors, quarterly schema drift review, license regex ownership, per-city pre-launch checks. /runbook.md
Reading this with an AI? Hand it one of these instead of the page. /llms-full.txt — architecture and build spec, complete, in one fetch · /brief.txt — every decision condensed under 4,000 characters, for tools that truncate · /llms.txt — index of everything here
No login, no JavaScript, no bot challenge. Plain text with open CORS, so any assistant, agent, or crawler can fetch it directly.

Part 1 — Why this exists

1.1 The premise

The most durable SEO strategy for a home service business — HVAC, electrical, plumbing, roofing, or any trade that sends a van — is to be demonstrably the most trustworthy choice for the homeowner, and to make that demonstrable in a form a machine can read.

That is the whole argument. Everything downstream is mechanism.

Search systems evaluating a local business are trying to establish three things: that it is real, that it is local, and that it is competent. This architecture proves all three by requiring documented jobs with photographs and locations, named technicians with verifiable credentials, a licence number checked against the issuing state's database, and reviews from customers who exist. A site meeting those requirements does not merely rank well. It deserves to, because it represents a business that actually does the work.

The structure makes the evidence machine-readable. The evidence itself is for humans. If those two ever conflict, the human wins, and several decisions in this document were made on exactly that basis.

1.2 What this document is, and is not

It is an implementation specification: content model, validation rules, schema, and the build gates that enforce them. An engineer can build from it.

It is not a wireframe, a visual design, or a client-facing pitch. It contains no page layouts. Earlier versions were titled "wireframe" and that was simply wrong.

Scope: United States home-service SMBs. Licence patterns, NAP conventions and regulatory assumptions throughout are US-specific. Internationalisation is out of scope and is not gestured at.

1.3 The dependency this asks of the business, stated before anything technical

This system produces empty templates when nobody documents the work.

Page publication is gated on evidence. A city page needs three documented jobs in that market. A service-and-city page needs one. A technician profile needs a job attached to that technician. When those do not exist, the templates render blocks saying so, in plain language, rather than filling the space with stock photography and adjectives.

That is a deliberate design decision and it is the single largest risk to the whole approach. It is not a CMS problem or a developer problem. It is an operational workflow the business has to run:

If that does not happen, the architecture will be correct and the site will be thin. No amount of engineering downstream fixes it, and this document would rather say so on page one than bury it.

1.4 Questions a reviewer should ask, answered

Is this just optimising for Google?

No, and the distinction matters. Structuring a licence number, a project photograph or a technician's credentials makes existing, real trust legible to a machine. Google's objective is to identify the most trustworthy local expert; the objective here is to prove that is this business. A crawler that cannot parse the evidence is a customer who never sees it. Nothing here asks anyone to manufacture a signal they have not earned.

Does a service-by-city grid produce thin content?

It would, without a gate. That is why there is one. Pages do not publish until a documented job exists for that service in that town, with photographs and a named technician. The theoretical maximum is cities multiplied by services; the actual count is capped by how much work the business can document. Ten jobs a month is ten pages a month, and the spec says cut the count, never the floor.

Why real photographs instead of stock?

Because it is the only defence against commoditisation a competitor cannot copy by buying the same template. Attributing a specific job to a specific technician in a specific neighbourhood produces something checkable, which matters disproportionately in a category where a bad decision costs a homeowner real money and occasionally their safety.

My competitor ranks fine with five pages. Over-engineered?

For some businesses, yes, and that is a legitimate answer. A five-page site works until it needs to capture intent it has no page for, or expand into a new area. Worth saying plainly: a competitor with 140,000 reviews and fifteen years of links will outrank a structurally better site. Structure does not beat authority. It makes every review, link and job you do earn count for more.

Where are the colours, fonts and layouts? Deliberately absent. This is brand-agnostic by design, so the same architecture serves an electrician in Texas and a roofer in Florida. Visual identity is applied on top during design.

1.5 What this document does not claim Several things here are honest bets rather than established mechanisms, and they are labelled as such where they appear. Three mechanisms proposed during review were rejected outright, with the reasoning recorded in place: a publish gate tied to Google review counts, which inverts cause and effect; automated demotion of requirements by correlation, which is statistically undefined when every page implements every requirement; and failing production deploys over a missing llms.txt, which trades concrete harm for speculative benefit.

A specification that cannot say what it is unsure about is not worth trusting on the parts it is sure about.

Revision record

This specification is in its fourth major version. Every correction below was made because a review found something wrong, and each is marked in place in the document rather than quietly absorbed. A document that cannot show its own corrections is asking to be taken on faith.

Corrections applied

CorrectedFromToBecause
Project schema typeHowTo with performerArticle with authorHowTo has no performer property, so the reference silently dropped; Google retired HowTo rich results
Location node id#local#localbusinessThe architecture doc and the annex disagreed; a developer reading only one would emit unresolvable references
Person to Project linkperformerInauthor on the ArticleperformerIn expects an Event, not a record of work
Service slugs/heat-pumps/, /maintenance-plans/singular throughoutThe annex locked a singular rule the sitemap then violated
Business age fieldyearsInBusinessyearEstablishedA count is wrong the day after it is typed, and nothing detects the drift
Zero-click eventsg_clicksearch_bouncebackIt measured a return to search, not a zero-click visit. The name claimed something the metric could not see
Speakable targetLocalBusiness, Servicethe page's WebPage nodeSpeakable has never been documented for those two types
Licence numbersfree textverified against the issuing state's registerA licence number is a legal claim, not copy
Three mechanisms rejected Proposed during review, declined, with the reasoning kept in the document rather than the argument being erased.
  1. A publish gate tied to Google review count. Blocking a new location's page until it has 20 reviews removes the page that generates reviews. Cause and effect inverted.
  2. Automated demotion of requirements by correlation. Every published page implements every requirement by construction, so the independent variable has no variance and the correlation is undefined rather than merely weak.
  3. Failing production deploys over a missing llms.txt. A concrete harm traded for a speculative benefit, on a file convention with no documented adoption.

What changed from v3

Deferred decision in v3v4 resolutionWhere
Service × location intersection
Was blocking the whole build
Model A adopted. Dedicated intersection pages at /[service]/[city]/, self-canonical, with a content floor and one local project each. Location hubs become indexes, not competitors. Section 1
Internal linking existed as documentation only Moved into the CMS as required reference fields. Links are generated from relationships, not typed by hand. Orphan detection runs nightly. Section 3
Schema listed as a stack of types Rewritten as one entity graph joined by @id, so the site describes a company, its people, and its jobs rather than a pile of unconnected markup. Section 4
Featured Local Projects assumed content capacity nobody had A capture-to-publish workflow with a moderation gate, a named owner for every step, and an honest volume rule if the pipeline underdelivers. Section 5
Problem pages depended on a click that AI Overviews now absorb Conversion paths that survive zero-click, plus the diagnostic tool returned to urgent framing on problem pages. Section 6

1. The service × location decision

ac repair phoenix is the query that pays. v3 published /ac-repair/ and /service-areas/phoenix/ and never said which one is supposed to rank for it. That ambiguity is how two pages end up competing for one query and neither wins.

Adopted: Model A — dedicated intersection pages

URL convention, one convention only: /ac-repair/phoenix/. Service first, city second.

The rule that keeps this from becoming a doorway farm Page count is capped by project supply, not by ambition. Cities × services is a theoretical maximum, never a build target. Ship the intersections you can actually fill, in descending order of revenue per job.

Rejected: Model B — multi-intent location hubs

One /service-areas/phoenix/ page carrying every service query for that city. Viable only at 1,500+ words per city with unique H2s, FAQs, and projects per service section.

Why it lost: it asks for more content per city than Model A does, while giving one URL to a dozen different money queries. The dilution is real and the internal linking logic never resolves cleanly. If the content team can write the mass Model B needs, they can write Model A's pages instead and get separate rankings for the effort.

Rules that follow from the decision

  • No query parameters for city. ?city=phoenix is not an architecture.
  • No city name in the service pillar URL. The pillar is /ac-repair/, city-agnostic.
  • Suburbs get their own intersection only when there is a real job to show. Otherwise they are a section on the nearest metro intersection page.
  • One slug convention, frozen before the first page ships. Changing it later is a redirect project, not an edit.
  • Service pillar links down to every city intersection. Every intersection links up to its pillar and across to its location hub.

2. Site architecture

HOMEPAGE /
Tier 1 — service pillars (city-agnostic) /ac-repair/ /ac-installation/ /furnace-repair/ /heating-installation/ /heat-pump-repair/ /heat-pump-installation/ /maintenance-plan/ /indoor-air-quality/
Tier 2 — intersections THE MONEY PAGES /ac-repair/phoenix/ /ac-repair/scottsdale/ /ac-repair/mesa/ /furnace-repair/phoenix/ …one per service × city that has a project to show
Tier 2b — location hubs (indexes + local entity) /service-areas/ /service-areas/phoenix/ /service-areas/scottsdale/ /service-areas/mesa/
Tier 2c — conversion surfaces v4.1 /ac-installation/estimate/ /furnace-installation/estimate/ /heat-pump-installation/estimate/ /specials/ /brands/[brand]/[model]/ sizing and tier, never a price · models capped at 20, each gated on a real installed job
Tier 3 — problem / symptom pages (top of funnel, urgent) /ac-blowing-warm-air/ /ac-not-turning-on/ /furnace-wont-ignite/ /ac-making-loud-noise/ /thermostat-not-responding/ …each references exactly one parent pillar
Tier 3b — comparison / decision cluster /repair-vs-replace/ /heat-pump-vs-furnace/ /ac-size-guide/ /seer-rating-guide/
Tier 3c — editorial OPTIONAL /resources/[slug]/ seasonal · rebates · local code · community — one required parent pillar each, unique topicKey, 600-word floor
Tier 4 — people, proof, trust /team/mike-rodriguez/ /projects/ /projects/[slug]/ /reviews/ /about/ /financing/ /brands/
pillar service authority, city-agnostic intersection self-canonical, project-backed location hub index + LocalBusiness entity problem symptom intent, emergency CTA comparison decision support, AI Overview target trust people and proof

3. CMS content model and enforced internal linking

Linking rules written in a document decay within months, because the person who wrote them stops being the person who publishes. Rules written as required CMS fields cannot decay, because a page will not save without them.

Content typeRequired reference fieldsWhat the template generates from them
ServicePillar city[] (areas served) An ItemList of every intersection page for this service. A block of every ProblemPage that references this pillar. A link to the matching comparison page.
Intersection service (required, one)
city (required, one)
project (required, min 1)
Breadcrumb, the up-link to its pillar, the across-link to its location hub, the project card with technician attribution, and its full JSON-LD block.
LocationHub city (required, one) An index of every intersection in that city, the city's project feed, NAP and hours from the LocalBusiness record, GBP link.
ProblemPage parentService (required, exactly one) A contextual link to the parent pillar using the exact service term, the pre-filled diagnostic widget, and the emergency CTA.
Project technician, service, city, photos[], gps (all required) Appends itself to the matching intersection page, location hub feed, and technician profile. No manual linking, ever.
Technician credentials[] Profile page, the list of every project performed, and the Person node in the graph.

Anchor text rules, applied by template

From → ToAnchor
Pillar → intersectionExact: AC repair in Phoenix
Intersection → problemSymptom-rich partial: AC blowing warm air
Problem → pillarExact service term: AC repair
Intersection → location hubCity name: Phoenix service area
Project → technicianTechnician name

Orphan detection NIGHTLY JOB

A cron job walks the published set and logs any page with fewer than 3 inbound internal links. The list goes to the SEO lead as an automated alert, not a dashboard somebody has to remember to open.

Orphans are the failure this architecture is most likely to produce, because the page count grows faster than anyone's attention. Catching them on a schedule is the only version of this that works.

4. The schema entity graph

v3 listed schema types. Types alone describe nothing. What earns entity understanding is the @id references between them: this company employs this person, who performed this job, which was this service, at this location.

// One graph, joined by @id. Every page emits its slice of it.

Organization @id: /#org
  ├─ sameAs ──────→ GBP profile, BBB, social profiles
  ├─ employs ─────→ Person @id: /team/mike-rodriguez/#person
  └─ hasPart ─────→ LocalBusiness @id: /service-areas/phoenix/#localbusiness
                       ├─ areaServed ──→ City (Phoenix)
                       ├─ sameAs ──────→ GBP CID for that location
                       └─ makesOffer ──→ Service @id: /ac-repair/#service

Person @id: /team/mike-rodriguez/#person
  ├─ worksFor ────→ /#org
  ├─ hasCredential → EducationalOccupationalCredential (NATE)
  // the Person-to-Project link is `author` on the Project node below, not performerIn:
  // that property expects an Event, not a record of work.

Project (typed Article) @id: /projects/phx-4412/#project
  ├─ location ────→ /service-areas/phoenix/#localbusiness
  ├─ about ───────→ /ac-repair/#service
  └─ author ──────→ /team/mike-rodriguez/#person

Service @id: /ac-repair/#service
  └─ provider ────→ /service-areas/phoenix/#localbusiness  (the city being served)

Per-location identity

Every location page gets its own LocalBusiness with a unique @id, its own NAP, geo, openingHours, and sameAs to that location's GBP CID. Not one shared business entity repeated.

FAQ markup discipline

FAQPage applies only to a discrete Q&A accordion block, never to a whole page because it happens to answer questions. Comparison pages use Article plus HowTo or ItemList for the decision framework, and add FAQPage over their 3 to 5 genuine Q&A pairs. Symptom pages get no FAQ markup at all.

Built for extraction

A TL;DR summary block sits at the top of problem and comparison pages. If an AI Overview is going to quote something, it should be the sentence you chose. The optional SpeakableSpecification markup goes on the page’s WebPage node, with an honest caveat: Google has only ever documented speakable for news publishers, so treat it as cheap positioning rather than a ranking feature, and never sell it to a client as one. Same for HowTo, which no longer produces rich results.

5. Featured Local Projects: the operating procedure

This is the module most likely to fail, and the failure is operational, not structural. Most contractors are not content studios. A workflow that needs two hours of technician time per project ships four projects instead of forty, and every page with a required project field stays unpublished. The procedure below exists to make capture cost minutes, not hours.

Step 1 — Field capture

  • Technicians capture in CompanyCam or Fulcrum using a structured form, on site, before leaving.
  • Required: 2 before photos (one wide, one close), 2 after photos, what was wrong, what we did, the part or brand, and the neighborhood or landmark.
  • GPS is auto-captured in EXIF. This is non-negotiable and it is the moderation gate.

Step 2 — Moderation queue

  • The content coordinator clears the queue daily.
  • Reject anything missing GPS or a signed work order. No exceptions, because the whole claim of local proof rests on it.
  • Accepted jobs create a Project node with status Needs copy.

Step 3 — Ghostwriting with real attribution

  • An AI draft is generated from the captured fields. The technician does not write prose.
  • The draft goes to the named technician on mobile: Is this correct? Yes / No plus comments.
  • The timestamped approval is what legitimizes the byline. Without it, the byline is a lie and the E-E-A-T signal is worth less than nothing.

Step 4 — Publish and link

  • On approval, the CMS hook appends the project to its intersection page, the city feed, and the technician profile.
  • It adds its @id to the JSON-LD graph automatically.
  • Nobody links anything by hand. The relationship is the link.

Who owns what

RoleOwns
Field techniciansPhoto capture and job data, on site. Nothing else.
Content coordinatorDaily moderation, AI draft, chasing the fact-check, publishing.
SEO leadSchema QA, the internal-link audit, Core Web Vitals monitoring, the quarterly decay crawl.
CSR / dispatcherGoogle Business Profile Q&A inside a 24-hour SLA, and the weekly GBP post.

Capture path B — the client briefs the agency

Path A assumes the technician captures on site. That is the strongest evidence and the hardest habit to start. Most agency clients already do something simpler: they tell you about the job. A call, a text thread, a weekly catch-up. The work is being documented; it is just being documented to you instead of into a system.

Already happeningThe ask on top
Client describes the jobJob address or nearest cross street, and the date
Mentions who did itThe technician's name, spelled the same way every time
Sometimes sends a photoTwo photos, original files, from the phone that took them
The EXIF trap, and it is the one that silently kills this WhatsApp, iMessage, Telegram, Slack and Facebook Messenger all strip location metadata and recompress on send. A photo arriving through a chat thread has no GPS, so it fails the moderation gate while looking perfectly fine to the person who sent it. Use an upload form that stamps geolocation at upload time, or ask for the original file through a link. Do not ask people to "send photos" through chat and then wonder why nothing passes.

The Drive folder intake, and why it shrinks the ask

A shared Google Drive folder is the practical answer to the EXIF trap. Drive stores the original file; chat apps do not. One folder per client, the crew drops photos in, and a watcher on the folder creates the draft.

This makes the ask on the crew smaller than it first appears If EXIF survives, the photo already carries the GPS coordinates and the timestamp — so nobody has to type an address or a date. The system derives the location and the day from the file, and the briefing that already happens supplies the only things a photo cannot know: which technician, what was wrong, what was done.
Crew drops photos in Drive  →  watcher reads EXIF (GPS + timestamp)
                            →  derives city, matches to service area
                            →  creates a Project draft
                            →  the existing briefing fills the narrative
                            →  moderation queue, then publish

How photos must reach the folder, because the method decides whether any of this works:

Verify on day one with a single test photo, before assuming ninety days of capture If Location Services is turned off for the camera app on a crew phone, every photo that phone ever takes will arrive without GPS, and nothing in the workflow will announce it. That is a phone setting, not a process problem, and it is a two-minute fix if you find it on day one instead of day eighty-nine.

The intake validates EXIF on arrival and replies when it is missing, so a silent failure becomes a visible one.

Grade A evidence

GPS in EXIF, work order reference, technician approval. Unlocks everything: tier-1 eligibility, the full project claim, the technician byline.

Grade B evidence

Client-briefed job with address, date, named technician and photos without verified GPS, confirmed in writing by the client. Publishes the page and counts toward the 3-project minimum. Does not count toward tier-1.

The byline rule holds in both grades If the agency writes the piece from a briefing, the byline does not claim a technician wrote or approved it unless that technician actually did. The page may say the work was performed by a named technician, because that is true. Attributing the writing to someone who never saw it is the one thing this whole evidence model exists to prevent.
The honest volume rule If the pipeline produces 10 projects a month, you get 10 project-backed intersection pages a month. Cut the page count, never the content floor. A hundred thin intersection pages is the doorway-page pattern that gets a site filtered; ten real ones is a moat competitors cannot copy without sending their own trucks out.

6. Problem pages and the zero-click problem

Symptom pages target exactly the queries AI Overviews synthesize and answer in place. Plan for the click not to happen, and make the page worth publishing anyway.

Required on every problem page

  • Diagnostic tool, pre-populated with the symptom the page is about. The visitor arrives mid-problem; the tool should already know which problem.
  • Emergency CTA above the fold. Still not cooling? Schedule same-day service. Phone number tappable, visible without scrolling.
  • A secondary capture that is not a phone call — a downloadable checklist that takes an email or mobile number, for the visitor who is troubleshooting but not ready to book.
  • Remarketing pixel with search-bounceback detection: fire a search_bounceback event when the referrer carries a search query and viewport time stays under 7 seconds. Naming correction: this is not zero-click. A true zero-click visitor never reaches the site at all and cannot be measured from it. This measures someone who arrived, did not find the answer fast enough, and went back — still real demand worth remarketing to, and still worth fixing the page for.

Correction carried over from v3

v3 repositioned the diagnostic tool toward a low-intent check-up framing. That moves the tool away from the exact context where it converts.

  • Problem pages: urgent framing. Something is broken right now. The tool triages and hands off to a same-day booking.
  • Homepage and maintenance pages: check-up framing. Nothing is broken; the tool is a reason to engage.

Same tool, two entry framings, chosen by page type in the template.

7. Page-level wireframes

Intersection page NEW IN V4/ac-repair/phoenix/
H1AC Repair in Phoenix, AZ
Above foldPhone (tel: link) + same-day booking CTA + trust row
Unique copy — 350–500 wordsThis service, in this city. Local conditions, response area, what the job usually involves here.
Featured Local Project — required, min 1Before/after photos, neighborhood, named technician, link to profile
Auto-generated links↑ AC Repair pillar · → Phoenix service area · ↓ 3+ related problem pages
ReviewsFiltered to this city where volume allows
SchemaService + LocalBusiness @id + BreadcrumbList + Project
Service pillar/ac-repair/
H1AC Repair Services
What we fixScope, process, what to expect, pricing approach without numbers
Cities we serve — ItemListAuto-generated link to every intersection page for this service
Common problemsAuto-generated from every ProblemPage referencing this pillar
Decision supportLink to /repair-vs-replace/
SchemaService with areaServed array + FAQPage only if a real Q&A block exists
Problem / symptom page/ac-blowing-warm-air/
TL;DR summary blockSpeakable. The sentence you want quoted back by an AI Overview.
Emergency CTA — above foldStill not cooling? Same-day service. Tap to call.
Diagnostic tool, pre-filledSymptom already selected, urgent framing
Causes, rankedWhat a homeowner can check, then what needs a technician
Secondary captureDownloadable checklist → email or SMS
Parent linkExact-match anchor to AC Repair pillar
SchemaArticle + hasPart HowTo + Speakable
Location hub/service-areas/phoenix/
H1Plumbing Services in Phoenix, AZ
NAP + hours + mapFrom the LocalBusiness record, unique @id
Index of services in this cityAuto-generated links to every intersection
City project feedEvery project performed in Phoenix, newest first
Technicians who cover this areaPerson cards
SchemaLocalBusiness @id + sameAs GBP CID + geo + openingHours
Technician profile/team/mike-rodriguez/
Photo + name + years in tradeA real person, photographed on a real job
CredentialsNATE, EPA, manufacturer certifications
Jobs performedAuto-populated from every approved Project
SchemaPerson + worksFor + hasCredential. Projects link here via author; there is no valid inverse to emit.
Comparison / decision page/repair-vs-replace/
TL;DR verdict blockSpeakable. Answer the question in the first screen.
Decision frameworkAge, repair cost ratio, efficiency, refrigerant type
Comparison tableStructured, extractable
SchemaArticle + HowTo or ItemList. Not FAQPage for the whole page.

8. Technical defaults, enforced by the template

These are not recommendations. The template blocks publishing when they are unmet.

  • Meta description: required field. Empty means the page cannot publish.
  • Canonical: self-referential by default, with a deliberate override only.
  • Phone number: above the fold on every template, as a tel: link with contactPoint schema.
  • Hero image: 100 KB or under, preloaded.
  • Video: lite-YouTube embed pattern. Never a raw iframe.
  • Third-party widgets (reviews, financing): lazy-loaded through IntersectionObserver.

9. Acceptance criteria by page type

Page typeShips only when all of these are true
HomepageOrganization schema with sameAs to GBP, BBB, and social · speakable summary block · persistent phone in header · Lighthouse mobile performance 80+
Service pillarService schema with areaServed array · links to every city intersection · FAQ markup only where genuine Q&A exists · link to the relevant comparison page
Intersection350–500 words unique to service × city · minimum 1 Featured Local Project with technician attribution · self-canonical · LocalBusiness @id reference · 3+ links to related problem pages
Location hubLocalBusiness schema with unique @id, NAP, geo, openingHours · sameAs to that location's GBP CID · index of every intersection in the city · at least one project in the feed
Problem pageArticle schema with hasPart → HowTo · pre-filled diagnostic widget · emergency CTA above fold · exact-anchor link to parent pillar · speakable TL;DR
Technician profilePerson schema with worksFor and hasCredential · relationship to every project performed · a bio with demonstrable experience, not adjectives

10. Build sequence

Ordered by dependency and gate, not by calendar. Each phase ends at a gate; the gate is what earns the next phase, and a gate that fails means the phase repeats rather than the schedule slipping.

Phase 1 — decisions frozen

Slug conventions and canonical rules, written down and locked

Intersection convention /[service]/[city]/ committed. City list committed. Canonical policy documented. Nothing is built yet, because every later phase encodes these choices and changing them afterward is a redirect project.

Gate: one document, approved, that a developer could hand to a second developer without explanation.
Phase 2 — the content model

CMS types, required reference fields, schema generators, auto-linking

Build the relationships from Section 3 as enforced fields. Schema emits from those relationships. The auto-link blocks are generated, not authored.

Gate: a test intersection page cannot be saved without a service, a city, and a project attached, and its JSON-LD graph validates with every @id resolving.
Phase 3 — templates

Page templates with the publish-blocking defaults from Section 8

Core Web Vitals guardrails, meta defaults, canonical behavior, auto-link blocks, and the two diagnostic-tool framings wired per page type.

Gate: a page with an empty meta description is rejected by the CMS, and mobile Lighthouse performance clears 80 on the heaviest template.
Phase 4 — the pilot, and the real test

One city, two services, end to end, including field capture

This phase is not about the pages. It is about whether technicians actually document jobs on site. Run the full capture-moderate-approve-publish loop with real crews on real jobs.

Gate: projects arrive from the field without being chased, and the median capture takes minutes. If it does not clear, fix the capture workflow before scaling anything, and re-cut the page count to match true throughput.
Phase 5 — rollout in sprints

Remaining intersections, location hubs, problem pages

Order by revenue per job, descending. Each sprint ends with an internal-link audit and a Core Web Vitals regression check.

Gate per sprint: zero orphans in the nightly report, no CWV regression, every new intersection carrying its project.
Phase 6 — the decay cycle turns on

Triggered once the published set passes roughly 100 pages

A quarterly audit against Search Console. Any page down more than 30% in clicks year over year gets flagged for refresh. At this size, pages start dying quietly and nobody notices without the job running.

Gate: the first decay crawl runs and produces a refresh queue with named owners.

11. Content maintenance

ContentRefresh trigger
Service pillarsAnnual review, or whenever the service scope itself changes.
Problem pagesEach major equipment generation, or when Search Console shows decay.
Intersection pagesA new project replaces the featured one as soon as a better job comes in from that city.
Location hubsQuarterly project refresh at minimum.
Comparison contentWhen efficiency standards, rebate programs, or pricing economics move materially.
Technician profilesWhen certifications change, and immediately when a technician leaves. A byline on a departed employee's project stays, with the profile marked accordingly.