/service — Service page generator
Creates a production-ready service page for any service-business site managed in sites/.
Refusal up front
If sites/[site]/site-info.md → Business → Service business: false, the skill refuses to run and points the user to: "Use /blog for informational content, or generate a custom landing page outside the toolkit. Service pages are scoped to service businesses only."
Inputs
Required (asked at start if not given):
- Site — must match a folder under
sites/[name]/ and have service-business: true.
- Service — the service to feature (e.g., "Emergency Plumbing", "Dental Implants", "AI Consulting").
- Location (conditional on footprint):
single-location → not asked; uses the one location in site-info.md.
multi-location → asked; must match a location in site-info.md.
service-area → asked; must be a city the business actually serves (per site-info.md).
national-online → not asked; no city.
Optional (per-run overrides):
- Primary keyword — defaults to picked-from-
service-keywords.csv matching [service] [city].
- Image source — overrides default.
- Conversion template — overrides default if
notes.md documents a conversion winner.
Workflow
Step 0 — Cron-mode detection (if invoked with --cron)
If the invocation contains --cron, this skill runs in cron mode (no user available). Required behavior:
Idempotency check: see this skill's "Routine versioning + idempotency contract" section. If today's output already exists, exit cleanly with exit: "idempotent-skip". Write one line to the audit log via scripts/lib/audit-log.mjs appendRun({ exit: "idempotent-skip", ... }).
Escalation contract: any decision that would normally prompt the user (missing required file, voice anti-AI failure, schema validation failure, keyword cannibalization, etc.) MUST be escalated by writing an item to sites/{site}/_inbox/ via scripts/lib/cron-mode.mjs writeInboxItem(...). After writing, exit cleanly with exit: "escalated". Do NOT use AskUserQuestion in cron mode.
Defaults: when a choice would normally be asked, default to site-info.md / goals.md values. If both are silent on the required choice, escalate per (2).
Audit log: ALWAYS write one line to the audit log on exit — success (shipped), escalation (escalated), idempotent skip (idempotent-skip), or failure (failed).
Backoff: at the start of every cron-mode run, call checkBackoff({ routine }) from scripts/lib/audit-log.mjs. If true, the routine has hit the 3-strike threshold — write _inbox/routine-disabled-{name}.md, run scripts/sync-schedules.mjs --pause-routine={name}, and exit.
Step 1 — Read context (Rule 1)
Same as /blog Step 1, plus sites/[site]/service-keywords.csv and (if present) sites/[site]/architecture.md.
Step 1.5 — Architecture check (SEO_GUIDE.md Section 2.6)
Count rows in service-keywords.csv. If > 3 commercial keywords AND architecture.md is missing:
- Refuse to ship a new service page until
architecture.md exists.
- Output: "This site has [N] commercial keywords but no
architecture.md. Service pages need a planned hierarchy at this volume to avoid sprawl. Use templates/architecture.md as the starting point. Would you like me to draft the architecture from service-keywords.csv now? (yes/no/skip-this-once-with-reason)"
- If user picks "skip-this-once-with-reason" → require an explicit reason recorded in
notes.md, then continue. The next /service run on this site re-prompts.
If architecture.md exists, the new page must already be planned in it OR the user must add the row to the architecture before generating. Off-architecture pages are how sites accumulate cannibalizing service pages.
Step 2 — Confirm footprint + scope
- Read
site-info.md → Geographic footprint.
- Confirm service + location combination is valid:
single-location → service from site-info.md services list.
multi-location → service available at that location.
service-area → the city is in the service area list AND the service is offered.
national-online → service from site-info.md services list, no city.
- If invalid, ask user before proceeding.
Step 3 — Pick the keyword
- If user supplied → use it.
- Otherwise: pick the highest-CPC commercial keyword from
service-keywords.csv matching the service + location, excluding any in used-keywords.md.
- Seasonality check (soft) —
SEO_GUIDE.md Section 2.5. If the picked keyword has peak_months and/or seasonality populated and today is out of the peak window for a seasonal / holiday-spike term, OR seasonality = declining, flag and ask before proceeding (same prompt format as /blog Step 2). Service pages in particular benefit from being live ~3 months before peak demand to give Google time to index and rank. Soft only — never refuse on seasonality alone.
- Architecture-fit check. If
architecture.md exists, confirm this keyword has a planned row in it. If not, ask the user to add it to the architecture before generating (a one-line append, then continue).
Step 4 — Determine URL pattern (Section 4.1 of SEO_GUIDE.md)
| Footprint |
URL pattern |
single-location |
/services/[service-slug] |
multi-location |
/locations/[city-slug]/[service-slug] (or /services/[service-slug]/[city-slug]) |
service-area |
/services/[service-slug]-[city-slug] |
national-online |
/services/[service-slug] |
catalog |
Hierarchical per architecture: /[category]/[subcategory]/[product-or-page]/ (Section 4.1.1). Path matches the architecture row's location in the tree. |
For catalog footprint sites, the URL is determined by where the keyword sits in architecture.md — Level 1 → /[category]/, Level 2 → /[category]/[subcategory]/, Level 3 → /[category]/[subcategory]/[leaf]/. If architecture.md shows the row uses a distribution page (Section 2.6.4), insert the axis marker: /[category]/[axis]/[value]/.
Step 5 — SERP analysis
Same workflow as /blog Step 4, but on the commercial keyword. Extract: word count (typically 1500+ for primary service pages, 800+ for city variants), H2/H3 outline, conversion elements (CTAs, trust signals, pricing, testimonials), schema patterns.
Step 5.5 — Wireframe (mandatory, per CLAUDE.md "Wireframe before content" rule)
Before generating prose, produce a wireframe doc at sites/[site]/_drafts/[slug]/wireframe.md. Mandatory contents:
- Layout zones: hero (H1 + value prop), trust signals strip, problem/pain section, solution sections (one per H2), social proof, FAQ, CTA + form, NAP block (per footprint).
- Heading map (H1 / H2 / H3) — H1 = primary commercial keyword; H2s = sub-aspects (what's included, who it's for, pricing, FAQ); H3s = items inside each H2.
- Internal linking pattern slots — for a service / leaf page, patterns 1 (header dropdown via template), 3 (footer via template), 4 (breadcrumb), 6 (related-services). Plus optional pattern 7 inbound from blog posts.
- Conversion element placement — above-fold CTA, sticky mobile CTA, click-to-call phone, trust signals position, multiple CTA placements.
- Image plan — hero + NAP + work photos (real client work for E-E-A-T) + city-specific hero for
service-area zipper pages.
- NAP block placement — header / footer / contact section per
SEO_GUIDE.md Section 4.3 (Local NAP rules by footprint).
- Catalog-footprint addition: if the site is
catalog, also map the page's parent + sibling links per the architecture row (so internal-linking patterns 5 and 6 land cleanly).
Optional (if user opts in): hand-sketch / draw.io → AI design mockup for client presentation.
Show the wireframe to the user. Wait for explicit approval. Approved wireframes get archived in _drafts/[slug]/; rejected ones get revised and re-shown.
Step 6 — Plan + approval (Rule 2)
Present:
- Footprint + URL pattern + slug
- Primary keyword + cluster
- Required NAP for this page (per footprint, from
site-info.md)
- H1, H2 outline, FAQ
- Conversion elements list
- Image source for this run
- Language fan-out (multilingual sites only) — list of languages this service page will be produced in (default: all declared in
site-info.md).
Wait for approval.
Step 6.5 — Resolve language fan-out (multilingual sites only)
Read site-info.md Languages section.
Multilingual: false → skip; continue to Step 7 as a single-language run.
Multilingual: true:
- Default coverage = every language declared in the Languages table. Service pages almost always cover all languages — opt-out is rare and requires explicit confirmation.
- For each declared language:
- Resolve voice files: root
references/voice.md + per-site references/voice.[lang].md (REQUIRED). Service pages bias toward conversion-driven copy, lighter on stories — but still resolve all files.
- Format URL using the language's URL pattern from the Languages table (substitute
[slug]).
- Reserve a draft folder:
sites/[site]/_drafts/[slug]/[lang]/.
- Generate the hreflang link cluster from the resolved language set (self-reference + all siblings +
x-default).
- Schema
inLanguage is set per-language draft. Service schema gets inLanguage matching <html lang>. Service.availableLanguage may list ALL languages the actual service is delivered in (a separate field — language of the page vs. languages the service is offered in).
- Per-language nav coordination. The Lovable / CMS prompt for each language must include the new service page in that language's header dropdown / footer / sitemap. The English nav lists English service pages; the Spanish nav lists Spanish service pages.
The skill produces N drafts (one per language), each in its own _drafts/[slug]/[lang]/ subfolder. Each passes its own Tier 1 + conversion + voice anti-AI checks using its language's voice files.
Step 7 — Generate the page
Apply in order:
Voice — resolved references/, but biased toward conversion-driven copy (less story, more credibility).
Tier 1 on-page — all 16 items.
Anti-doorway-page rules (Section 4.1 of SEO_GUIDE.md) — for service-area only:
- Unique 200+ word opening specific to that city
- Local landmarks/neighborhoods mentioned
- 1–2 city-specific FAQs
- Real local NAP (city-specific area code if possible)
- Real testimonials from customers in that city
- Different hero image (not the same stock photo across cities)
If the page can't pass all 6, refuse to publish.
NAP per footprint (Section 4.3 of SEO_GUIDE.md) — render correctly:
single-location → site's one NAP, header + footer + contact section.
multi-location → that branch's NAP, with embedded map of that branch.
service-area → HQ address may be hidden; show service-area map; phone is local.
national-online → no LocalBusiness; only Organization contactPoint.
Conversion elements (Section 4.4 of SEO_GUIDE.md + on-page-seo.md Category 14) — every item:
- Above-fold CTA
- Sticky mobile CTA
- Phone with
tel: link
- Trust signals above fold (rating, license, years)
- Multiple CTA placements
- Specific testimonials (names, photos, neighborhoods if
service-area)
- Pricing transparency
- Service area / hours
- 4–8 FAQ
- Embedded map (per footprint)
Schema (JSON-LD) — Service schema (with all required properties from on-page-seo.md 9.3) + LocalBusiness (per footprint, from 9.2) + BreadcrumbList + FAQPage + Organization site-wide. Plus, IFF the site's site-info.md byline policy enables person-author mode AND the page's author: frontmatter is set to a real-person slug (per /blog skill Step 6 item 9 routing logic), include Person schema in the @graph and inject the bio block from sites/{site}/author-{slug}.md in the service page's author footer. Service pages traditionally don't carry author bylines, but with the partial-EEAT-byline policies (e.g., a site that adopted partial-EEAT-byline), service pages MAY carry a named author IF the service category is in the site's coi-categories.md → personal_eligible list. Refer to /blog skill for the full author-routing logic — it's shared between the two skills.
Length — primary service pages 1500+ words; city variants 800+ words minimum.
AI-search-friendly: Q+A density. Beyond the dedicated FAQ section (conversion element item 9), structure 3+ in-body sections as explicit question→answer pairs (the H2 or H3 IS a question; the paragraph below IS the answer in the first sentence). LLMs preferentially cite passages that stand alone as direct answers. Examples for service pages: ## How much does AI consulting cost for a 20-person team? (not ## Pricing); ### Do you work with companies outside Houston? (not ### Geographic coverage).
AI-search-friendly: citation-friendly chunking. Paragraphs max 3 sentences. Lead each paragraph with the topic sentence — the main claim or value prop. Front-load named entities (the service name, location, key benefit) in the first sentence rather than burying them in subordinate clauses. Service pages have higher commercial intent and benefit even more from LLM-citable chunks.
AI-search-friendly: self-contained facts. Every paragraph must stand on its own. NO "as mentioned above," "as discussed earlier," "see the section above," "we'll explain below." LLMs lose context between paragraphs — pretend each paragraph is the only one cited.
AI-search-friendly: verifiable claims. Every statistic, customer count, response-time claim, or numeric assertion either (a) cites a source link, OR (b) is internally verifiable ("23 clients served in 2024" — provable via Stripe/GBP review counts/internal records, not McKinsey-style). Bare unsourced numbers read as fabricated. If you can't source or self-verify, drop the number.
Step 8 — Fetch images
Same as /blog Step 7, but with stricter rules:
- Hero image must be unique per city for
service-area zipper pages (can't reuse stock photo across all 50 city pages).
- Prefer real work photos over stock when source is
client-supplied or site-library.
Step 9 — Validate (Rule 4)
Same as /blog Step 8, plus:
- ✅ Anti-doorway-page rules (all 6, for
service-area pages)
- ✅ NAP consistency check (page NAP matches
site-info.md exactly)
- ✅ All required
Service schema properties present
- ✅ Conversion elements all present (every checkbox in
on-page-seo.md Category 14 applicable)
Step 10 — Ship per publishing method
Same as /blog Step 9.
Step 11 — Update tracker
Append to used-keywords.md with Service page type.
Step 12 — Volume check (Section 4.2 of SEO_GUIDE.md)
After shipping, check site's total service-page count vs. cap per footprint. Flag if approaching the cap; recommend consolidation if multiple thin pages exist.
Routine versioning + idempotency contract
This skill participates in cruise-control via --cron mode. When invoked by cron:
Stamp routine_version in every output produced (the notes.md audit entry header, _inbox/ item frontmatter, draft folder metadata, audit log line written via scripts/lib/audit-log.mjs). Current routine_version: 1.0. Bump when the skill's behavior meaningfully changes.
Idempotency: this skill MUST be safe to run twice in a row on the same day without producing duplicate work. Implementation: check _drafts/{YYYY-MM-DD}-* at the start of every cron-mode run; if today's slug already exists, exit cleanly with exit: "idempotent-skip".
Reference: docs/specs/2026-05-16-agents-cruise-control-design.md § Operational hardening O3.
Refusal conditions
In addition to base refusals (missing files, etc.):
service-business: false in site-info.md.
- Service or location not in
site-info.md.
- Anti-doorway-page rules can't be satisfied (for
service-area).
- Volume cap (Section 4.2) would be exceeded.
service-keywords.csv has > 3 rows AND architecture.md is missing (SEO_GUIDE.md Section 2.6) — refuse until the architecture exists, OR user records an explicit one-time skip reason in notes.md.
1---2name: service3description: Generate a service page (commercial-intent landing page) for a chosen managed site. Footprint-aware (single-location / multi-location / service-area / national-online), enforces anti-doorway-page rules, applies real NAP per Section 4.3 of SEO_GUIDE.md, includes mandatory conversion elements per Section 4.4, validates schema. Use when the user types `/service` or asks for a service page.4---56# `/service` — Service page generator78Creates a production-ready service page for any service-business site managed in `sites/`.910## Refusal up front1112If `sites/[site]/site-info.md` → Business → `Service business: false`, the skill refuses to run and points the user to: "Use `/blog` for informational content, or generate a custom landing page outside the toolkit. Service pages are scoped to service businesses only."1314## Inputs1516Required (asked at start if not given):17- **Site** — must match a folder under `sites/[name]/` and have `service-business: true`.18- **Service** — the service to feature (e.g., "Emergency Plumbing", "Dental Implants", "AI Consulting").19- **Location** (conditional on footprint):20 - `single-location` → not asked; uses the one location in `site-info.md`.21 - `multi-location` → asked; must match a location in `site-info.md`.22 - `service-area` → asked; must be a city the business actually serves (per `site-info.md`).23 - `national-online` → not asked; no city.2425Optional (per-run overrides):26- **Primary keyword** — defaults to picked-from-`service-keywords.csv` matching `[service] [city]`.27- **Image source** — overrides default.28- **Conversion template** — overrides default if `notes.md` documents a conversion winner.2930## Workflow3132### Step 0 — Cron-mode detection (if invoked with `--cron`)3334If the invocation contains `--cron`, this skill runs in cron mode (no user available). Required behavior:35361. **Idempotency check:** see this skill's "Routine versioning + idempotency contract" section. If today's output already exists, exit cleanly with `exit: "idempotent-skip"`. Write one line to the audit log via `scripts/lib/audit-log.mjs` `appendRun({ exit: "idempotent-skip", ... })`.37382. **Escalation contract:** any decision that would normally prompt the user (missing required file, voice anti-AI failure, schema validation failure, keyword cannibalization, etc.) MUST be escalated by writing an item to `sites/{site}/_inbox/` via `scripts/lib/cron-mode.mjs` `writeInboxItem(...)`. After writing, exit cleanly with `exit: "escalated"`. Do NOT use `AskUserQuestion` in cron mode.39403. **Defaults:** when a choice would normally be asked, default to `site-info.md` / `goals.md` values. If both are silent on the required choice, escalate per (2).41424. **Audit log:** ALWAYS write one line to the audit log on exit — success (`shipped`), escalation (`escalated`), idempotent skip (`idempotent-skip`), or failure (`failed`).43445. **Backoff:** at the start of every cron-mode run, call `checkBackoff({ routine })` from `scripts/lib/audit-log.mjs`. If true, the routine has hit the 3-strike threshold — write `_inbox/routine-disabled-{name}.md`, run `scripts/sync-schedules.mjs --pause-routine={name}`, and exit.4546### Step 1 — Read context (Rule 1)4748Same as `/blog` Step 1, plus `sites/[site]/service-keywords.csv` and (if present) `sites/[site]/architecture.md`.4950### Step 1.5 — Architecture check (`SEO_GUIDE.md` Section 2.6)5152Count rows in `service-keywords.csv`. If **> 3** commercial keywords AND `architecture.md` is missing:5354- Refuse to ship a new service page until `architecture.md` exists.55- Output: "This site has [N] commercial keywords but no `architecture.md`. Service pages need a planned hierarchy at this volume to avoid sprawl. Use `templates/architecture.md` as the starting point. Would you like me to draft the architecture from `service-keywords.csv` now? (yes/no/skip-this-once-with-reason)"56- If user picks "skip-this-once-with-reason" → require an explicit reason recorded in `notes.md`, then continue. The next `/service` run on this site re-prompts.5758If `architecture.md` exists, the new page must already be planned in it OR the user must add the row to the architecture before generating. Off-architecture pages are how sites accumulate cannibalizing service pages.5960### Step 2 — Confirm footprint + scope6162- Read `site-info.md` → Geographic footprint.63- Confirm service + location combination is valid:64 - `single-location` → service from `site-info.md` services list.65 - `multi-location` → service available at that location.66 - `service-area` → the city is in the service area list AND the service is offered.67 - `national-online` → service from `site-info.md` services list, no city.68- If invalid, ask user before proceeding.6970### Step 3 — Pick the keyword7172- If user supplied → use it.73- Otherwise: pick the highest-CPC commercial keyword from `service-keywords.csv` matching the service + location, excluding any in `used-keywords.md`.74- **Seasonality check (soft) — `SEO_GUIDE.md` Section 2.5.** If the picked keyword has `peak_months` and/or `seasonality` populated and today is out of the peak window for a `seasonal` / `holiday-spike` term, OR `seasonality = declining`, flag and ask before proceeding (same prompt format as `/blog` Step 2). Service pages in particular benefit from being live ~3 months before peak demand to give Google time to index and rank. Soft only — never refuse on seasonality alone.75- **Architecture-fit check.** If `architecture.md` exists, confirm this keyword has a planned row in it. If not, ask the user to add it to the architecture before generating (a one-line append, then continue).7677### Step 4 — Determine URL pattern (Section 4.1 of SEO_GUIDE.md)7879| Footprint | URL pattern |80|-----------|-------------|81| `single-location` | `/services/[service-slug]` |82| `multi-location` | `/locations/[city-slug]/[service-slug]` (or `/services/[service-slug]/[city-slug]`) |83| `service-area` | `/services/[service-slug]-[city-slug]` |84| `national-online` | `/services/[service-slug]` |85| `catalog` | Hierarchical per architecture: `/[category]/[subcategory]/[product-or-page]/` (Section 4.1.1). Path matches the architecture row's location in the tree. |8687For `catalog` footprint sites, the URL is determined by where the keyword sits in `architecture.md` — Level 1 → `/[category]/`, Level 2 → `/[category]/[subcategory]/`, Level 3 → `/[category]/[subcategory]/[leaf]/`. If `architecture.md` shows the row uses a distribution page (Section 2.6.4), insert the axis marker: `/[category]/[axis]/[value]/`.8889### Step 5 — SERP analysis9091Same workflow as `/blog` Step 4, but on the commercial keyword. Extract: word count (typically 1500+ for primary service pages, 800+ for city variants), H2/H3 outline, conversion elements (CTAs, trust signals, pricing, testimonials), schema patterns.9293### Step 5.5 — Wireframe (mandatory, per `CLAUDE.md` "Wireframe before content" rule)9495Before generating prose, produce a wireframe doc at `sites/[site]/_drafts/[slug]/wireframe.md`. Mandatory contents:9697- **Layout zones**: hero (H1 + value prop), trust signals strip, problem/pain section, solution sections (one per H2), social proof, FAQ, CTA + form, NAP block (per footprint).98- **Heading map** (H1 / H2 / H3) — H1 = primary commercial keyword; H2s = sub-aspects (what's included, who it's for, pricing, FAQ); H3s = items inside each H2.99- **Internal linking pattern slots** — for a service / leaf page, patterns 1 (header dropdown via template), 3 (footer via template), 4 (breadcrumb), 6 (related-services). Plus optional pattern 7 inbound from blog posts.100- **Conversion element placement** — above-fold CTA, sticky mobile CTA, click-to-call phone, trust signals position, multiple CTA placements.101- **Image plan** — hero + NAP + work photos (real client work for E-E-A-T) + city-specific hero for `service-area` zipper pages.102- **NAP block placement** — header / footer / contact section per `SEO_GUIDE.md` Section 4.3 (Local NAP rules by footprint).103- **Catalog-footprint addition:** if the site is `catalog`, also map the page's parent + sibling links per the architecture row (so internal-linking patterns 5 and 6 land cleanly).104105Optional (if user opts in): hand-sketch / draw.io → AI design mockup for client presentation.106107Show the wireframe to the user. Wait for explicit approval. Approved wireframes get archived in `_drafts/[slug]/`; rejected ones get revised and re-shown.108109### Step 6 — Plan + approval (Rule 2)110111Present:112- Footprint + URL pattern + slug113- Primary keyword + cluster114- Required NAP for this page (per footprint, from `site-info.md`)115- H1, H2 outline, FAQ116- Conversion elements list117- Image source for this run118- **Language fan-out** *(multilingual sites only)* — list of languages this service page will be produced in (default: all declared in `site-info.md`).119120Wait for approval.121122### Step 6.5 — Resolve language fan-out (multilingual sites only)123124Read `site-info.md` Languages section.125126- **`Multilingual: false`** → skip; continue to Step 7 as a single-language run.127- **`Multilingual: true`**:128 1. Default coverage = every language declared in the Languages table. Service pages almost always cover all languages — opt-out is rare and requires explicit confirmation.129 2. For each declared language:130 - Resolve voice files: root `references/voice.md` + per-site `references/voice.[lang].md` (REQUIRED). Service pages bias toward conversion-driven copy, lighter on stories — but still resolve all files.131 - Format URL using the language's URL pattern from the Languages table (substitute `[slug]`).132 - Reserve a draft folder: `sites/[site]/_drafts/[slug]/[lang]/`.133 3. Generate the hreflang link cluster from the resolved language set (self-reference + all siblings + `x-default`).134 4. Schema `inLanguage` is set per-language draft. `Service` schema gets `inLanguage` matching `<html lang>`. `Service.availableLanguage` may list ALL languages the actual service is delivered in (a separate field — language of the page vs. languages the service is offered in).135 5. **Per-language nav coordination.** The Lovable / CMS prompt for each language must include the new service page in that language's header dropdown / footer / sitemap. The English nav lists English service pages; the Spanish nav lists Spanish service pages.136137The skill produces N drafts (one per language), each in its own `_drafts/[slug]/[lang]/` subfolder. Each passes its own Tier 1 + conversion + voice anti-AI checks using its language's voice files.138139### Step 7 — Generate the page140141Apply in order:1421. **Voice** — resolved `references/`, but biased toward conversion-driven copy (less story, more credibility).1432. **Tier 1 on-page** — all 16 items.1443. **Anti-doorway-page rules (Section 4.1 of SEO_GUIDE.md)** — for `service-area` only:145 - Unique 200+ word opening specific to that city146 - Local landmarks/neighborhoods mentioned147 - 1–2 city-specific FAQs148 - Real local NAP (city-specific area code if possible)149 - Real testimonials from customers in that city150 - Different hero image (not the same stock photo across cities)151152 **If the page can't pass all 6, refuse to publish.**1531544. **NAP per footprint (Section 4.3 of SEO_GUIDE.md)** — render correctly:155 - `single-location` → site's one NAP, header + footer + contact section.156 - `multi-location` → that branch's NAP, with embedded map of that branch.157 - `service-area` → HQ address may be hidden; show service-area map; phone is local.158 - `national-online` → no `LocalBusiness`; only `Organization` contactPoint.1591605. **Conversion elements (Section 4.4 of SEO_GUIDE.md + `on-page-seo.md` Category 14)** — every item:161 - Above-fold CTA162 - Sticky mobile CTA163 - Phone with `tel:` link164 - Trust signals above fold (rating, license, years)165 - Multiple CTA placements166 - Specific testimonials (names, photos, neighborhoods if `service-area`)167 - Pricing transparency168 - Service area / hours169 - 4–8 FAQ170 - Embedded map (per footprint)1711726. **Schema (JSON-LD)** — `Service` schema (with all required properties from `on-page-seo.md` 9.3) + `LocalBusiness` (per footprint, from 9.2) + `BreadcrumbList` + `FAQPage` + `Organization` site-wide. Plus, IFF the site's `site-info.md` byline policy enables person-author mode AND the page's `author:` frontmatter is set to a real-person slug (per `/blog` skill Step 6 item 9 routing logic), include `Person` schema in the `@graph` and inject the bio block from `sites/{site}/author-{slug}.md` in the service page's author footer. Service pages traditionally don't carry author bylines, but with the partial-EEAT-byline policies (e.g., a site that adopted partial-EEAT-byline), service pages MAY carry a named author IF the service category is in the site's `coi-categories.md` → `personal_eligible` list. Refer to `/blog` skill for the full author-routing logic — it's shared between the two skills.1731747. **Length** — primary service pages 1500+ words; city variants 800+ words minimum.1751768. **AI-search-friendly: Q+A density.** Beyond the dedicated FAQ section (conversion element item 9), structure 3+ in-body sections as explicit question→answer pairs (the H2 or H3 IS a question; the paragraph below IS the answer in the first sentence). LLMs preferentially cite passages that stand alone as direct answers. Examples for service pages: `## How much does AI consulting cost for a 20-person team?` (not `## Pricing`); `### Do you work with companies outside Houston?` (not `### Geographic coverage`).1771789. **AI-search-friendly: citation-friendly chunking.** Paragraphs max 3 sentences. Lead each paragraph with the topic sentence — the main claim or value prop. Front-load named entities (the service name, location, key benefit) in the first sentence rather than burying them in subordinate clauses. Service pages have higher commercial intent and benefit even more from LLM-citable chunks.17918010. **AI-search-friendly: self-contained facts.** Every paragraph must stand on its own. NO "as mentioned above," "as discussed earlier," "see the section above," "we'll explain below." LLMs lose context between paragraphs — pretend each paragraph is the only one cited.18118211. **AI-search-friendly: verifiable claims.** Every statistic, customer count, response-time claim, or numeric assertion either (a) cites a source link, OR (b) is internally verifiable ("23 clients served in 2024" — provable via Stripe/GBP review counts/internal records, not McKinsey-style). Bare unsourced numbers read as fabricated. If you can't source or self-verify, drop the number.183184### Step 8 — Fetch images185186Same as `/blog` Step 7, but with stricter rules:187- Hero image must be unique per city for `service-area` zipper pages (can't reuse stock photo across all 50 city pages).188- Prefer real work photos over stock when source is `client-supplied` or `site-library`.189190### Step 9 — Validate (Rule 4)191192Same as `/blog` Step 8, plus:193- ✅ Anti-doorway-page rules (all 6, for `service-area` pages)194- ✅ NAP consistency check (page NAP matches `site-info.md` exactly)195- ✅ All required `Service` schema properties present196- ✅ Conversion elements all present (every checkbox in `on-page-seo.md` Category 14 applicable)197198### Step 10 — Ship per publishing method199200Same as `/blog` Step 9.201202### Step 11 — Update tracker203204Append to `used-keywords.md` with `Service` page type.205206### Step 12 — Volume check (Section 4.2 of SEO_GUIDE.md)207208After shipping, check site's total service-page count vs. cap per footprint. Flag if approaching the cap; recommend consolidation if multiple thin pages exist.209210## Routine versioning + idempotency contract211212This skill participates in cruise-control via `--cron` mode. When invoked by cron:2132141. **Stamp `routine_version`** in every output produced (the `notes.md` audit entry header, `_inbox/` item frontmatter, draft folder metadata, audit log line written via `scripts/lib/audit-log.mjs`). Current `routine_version`: **1.0**. Bump when the skill's behavior meaningfully changes.2152162. **Idempotency:** this skill MUST be safe to run twice in a row on the same day without producing duplicate work. Implementation: check `_drafts/{YYYY-MM-DD}-*` at the start of every cron-mode run; if today's slug already exists, exit cleanly with `exit: "idempotent-skip"`.217218Reference: `docs/specs/2026-05-16-agents-cruise-control-design.md` § Operational hardening O3.219220## Refusal conditions221222In addition to base refusals (missing files, etc.):223- `service-business: false` in `site-info.md`.224- Service or location not in `site-info.md`.225- Anti-doorway-page rules can't be satisfied (for `service-area`).226- Volume cap (Section 4.2) would be exceeded.227- **`service-keywords.csv` has > 3 rows AND `architecture.md` is missing** (`SEO_GUIDE.md` Section 2.6) — refuse until the architecture exists, OR user records an explicit one-time skip reason in `notes.md`.