fix(seo,geo): I5 — disclose sampling coverage instead of implying an audit

Both agents sample (seo-analyzer.md:403 "sample 5-15 key pages",
geo-analyzer.md:460 "Sample 5-10 key pages") and neither states it. The
report says "audit". On a 500-page site a 12-page sample is 2.4%, and the
reader cannot know that unless it is printed. /client-handover gates on
these scores.

The denominator was already within reach: STEP 4 fetches sitemap.xml. Count
its URLs and the coverage ratio is free — same data C1 will use for
sitemap-driven crawl later.

- Mandatory COVERAGE line in both scoring blocks: N of M sitemap URLs (P%),
  or "total UNKNOWN" when no sitemap. Never omitted, never rounded up.
- seo: <25% coverage repeats in §0 as a major alert. Sample by risk (one
  page per template + GSC position 4-10 quick wins), name skipped templates
  — an un-sampled template is an un-audited template.
- geo: scoped honestly rather than blanket — COVERAGE bounds the per-page
  axes (Content Shape, page-level Schema.org) but NOT the site-wide ones
  (AI Crawlers Policy, llms.txt are single files, fully read). One ratio
  should not discredit axes it does not govern.

Verified: make test 35 GREEN / 0 RED.
This commit is contained in:
Bastien Chanot
2026-07-16 16:50:19 +02:00
parent 4ea2fb8c37
commit 64f175f01d
2 changed files with 35 additions and 0 deletions
+13
View File
@@ -459,6 +459,12 @@ Load: `~/.claude/agents/resources/content-shape-for-ai.md`
Sample 5-10 key pages (homepage + top service/blog pages). For each: Sample 5-10 key pages (homepage + top service/blog pages). For each:
**Record the denominator.** This samples; the report says "audit". Count the
URLs in `sitemap.xml` for the coverage ratio, and carry it into the GEO
SCORING block. No sitemap → total UNKNOWN, say so. Content shape is the
axis most damaged by silent sampling: it is judged per page, so a 6-page
sample of a 300-page site says nothing about the other 294.
### Checks ### Checks
1. **Definition Lead** — does the first sentence (or H1) follow 1. **Definition Lead** — does the first sentence (or H1) follow
@@ -586,6 +592,7 @@ Score each axis. Use concrete findings from STEP 2-9.
``` ```
GEO SCORING (<depth>) GEO SCORING (<depth>)
COVERAGE : <N> of <M> sitemap URLs (<P>%) | <N> pages, total UNKNOWN
AI Crawlers Policy : XX/20 <justification> AI Crawlers Policy : XX/20 <justification>
llms.txt : XX/20 <justification> llms.txt : XX/20 <justification>
Schema.org for AI : XX/20 <justification> Schema.org for AI : XX/20 <justification>
@@ -596,6 +603,12 @@ AI Visibility (live) : XX/20 | N/A (LOCAL)
GEO GLOBAL (weighted) : XX.X/20 (<depth>) GEO GLOBAL (weighted) : XX.X/20 (<depth>)
``` ```
**COVERAGE is mandatory, never omitted, never rounded up.** It bounds the
per-page axes — Content Shape above all, and the page-level share of
Schema.org. Site-wide axes (AI Crawlers Policy, llms.txt) are unaffected:
robots.txt and llms.txt are single files, fully read. Say which is which
rather than letting one ratio discredit the whole report.
Per user instruction: **GEO weight in combined SEO+GEO report = 20% for Per user instruction: **GEO weight in combined SEO+GEO report = 20% for
local, 25% for national/SaaS/content.** local, 25% for national/SaaS/content.**
+22
View File
@@ -400,8 +400,22 @@ Fetch rendered HTML. Extract and analyze:
## STEP 5 — ON-PAGE AUDIT `[both]` ## STEP 5 — ON-PAGE AUDIT `[both]`
**Record the denominator BEFORE sampling.** This step samples; the report
says "audit". Count the URLs in `sitemap.xml` (fetch it in full — the
`head -50` in STEP 4 is a preview, not a count). That count is the coverage
denominator, and it feeds the mandatory COVERAGE line in STEP 9. No sitemap
→ denominator unknown: say so, never let silence imply full coverage. On a
500-page site a 12-page sample is 2.4% — the On-page score is an
extrapolation from it, and the reader cannot know that unless you print it.
### Meta tags per page (sample 5-15 key pages) ### Meta tags per page (sample 5-15 key pages)
Sample by risk, not convenience: homepage + top templates (one per page
type: service, city, blog, product, legal) + any page GSC flags as a
position 4-10 quick win. Same template audited twice buys nothing; an
un-sampled template is an un-audited template — name the templates you
skipped.
For each sampled page: For each sampled page:
``` ```
PAGE: <path> PAGE: <path>
@@ -738,6 +752,8 @@ misroutes the client-handover gate and the user's effort.
``` ```
SEO SCORING (<depth>) SEO SCORING (<depth>)
COVERAGE : <N> of <M> sitemap URLs (<P>%) — templates skipped: <list|none>
| <N> pages, total UNKNOWN (no sitemap)
Technical : XX/20 <justification> Technical : XX/20 <justification>
On-page : XX/20 <justification> On-page : XX/20 <justification>
SEO Local : XX/20 | N/A SEO Local : XX/20 | N/A
@@ -749,6 +765,12 @@ Legal : XX/20 <justification>
SEO GLOBAL (weighted): XX.X/20 (<depth>) SEO GLOBAL (weighted): XX.X/20 (<depth>)
``` ```
**COVERAGE is mandatory, never omitted, never rounded up.** It is the
honesty bound on every page-level axis: On-page and the on-page share of
Technical are extrapolations from the sample. If coverage < 25%, repeat it
in §0 as a major alert — a 17/20 drawn from 3% of a site is not a 17/20, and
`/client-handover` gates on these numbers.
Per user instruction: this score represents **80% of the combined Per user instruction: this score represents **80% of the combined
final score for local B2C (20% for GEO), or 75% for SaaS/national final score for local B2C (20% for GEO), or 75% for SaaS/national
(25% for GEO)**. The `/seo` dispatcher combines SEO and GEO scores. (25% for GEO)**. The `/seo` dispatcher combines SEO and GEO scores.