agentleFS
greglas75greglas75/zuvo/skills/geo-audit/SKILL.md

geo-audit

Skill7 starsChanged 28 days agoMITView on GitHub

GEO (Generative Engine Optimization) readiness audit. Scans source code for AI citation signals across 12 dimensions: AI crawler access, schema graph connectivity, llms.txt, SSR rendering, freshness, chunkability, canonicalization, sitemap, BLUF structure, heading quality, citation signals, and anti-patterns. Produces tiered report (A/B/C/D) with evidence-backed findings and JSON output for geo-fix consumption.

20 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use geo, seo-local and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • ORBWEVASkill

    geo

    Generative Engine Optimization — optimize content for AI search engines (ChatGPT, Perplexity, Google AI Overviews, Gemini). Use when user wants to rank in AI search, improve AI citability, audit content for LLM visibility, or explicitly invokes /geo commands. Triggers on "GEO", "generative engine", "AI search", "AI overviews", "citability", "Perplexity", "ChatGPT search", or /geo commands.

    Also in 6 projects that use this

  • mangollcSkill

    seo-local

    Local SEO optimization: Google Business Profile post generation, 30-day posting calendars, NAP consistency checks, local citation audits, review response templates, local keywords.

    Also in 3 projects that use this

  • megastepSkill

    seo-schema

    Detect, validate, and generate Schema.org structured data. JSON-LD format preferred. Use when user says "schema", "structured data", "rich results", "JSON-LD", or "markup".

    Also in 3 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

53 rules. 0 are common, 25 are distinctive, and 28 are very specific to this project.

  • 0

    Common

    Most of the 19 edited versions we read keep these.

  • 25

    Distinctive

    This author's own choices. Worth a look.

  • 28

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read these files before any work begins:

    line 59
  2. Distinctive:

    If any file is missing, STOP.

    line 88
  3. Very specific:

    Read ../../shared/includes/env-compat.md for agent dispatch patterns, path resolution, and progress tracking across all supported platforms.

    line 92
  4. Very specific:

    Use the deterministic preload helper FIRST: Run ~/.zuvo/compute-preload geo-audit "$PWD".

    line 114
  5. Very specific:

    Read ../../shared/includes/codesift-setup.md for the full initialization sequence.

    line 120
  6. Very specific:

    Summary: Run the CodeSift setup from codesift-setup.md at skill start.

    line 122
  7. Very specific:

    If --live-url is provided, follow the live probe consent protocol from env-compat.md.

    line 143
  8. Distinctive:

    Default: code-only audit of current directory, auto-detect profile and CMS.

    line 160
  9. Distinctive:

    marketing: no content directory, few pages, landing page patterns (fallback default)

    line 210
  10. Distinctive:

    Import overlapping findings from the most recent seo-audit JSON output to avoid re-auditing what seo-audit already checked.

    line 246
  11. Very specific:

    If found, select the file with the lexicographically greatest filename (ISO date + optional -N suffix ensures correct ordering)

    line 249
  12. Distinctive:

    If the seo-audit JSON is >48h old: emit warning "seo-audit import is stale (>48h old).

    line 262
  13. Very specific:

    If no seo-audit-*.json found: "No seo-audit JSON found.

    line 263
  14. Distinctive:

    Claude Code: Use the Agent tool to run all three in parallel:

    line 296
  15. Distinctive:

    Cursor: No agent dispatch. Execute each agent's analysis sequentially yourself, maintaining identical output format.

    line 326
  16. Distinctive:

    If native agent dispatch is unavailable, run the three agent analyses sequentially yourself, preserve the same report sections, and note …

    line 329
  17. Distinctive:

    If an agent fails or times out:

    line 335
  18. Very specific:

    If retry fails: log the error, proceed with results from successful agents, mark the failed agent's dimensions as INSUFFICIENT DATA

    line 337
  19. Very specific:

    Read ../../shared/includes/geo-check-registry.md as the single source of truth for owner_agent, layer, enforcement, and evidence_mode.

    line 345
  20. Very specific:

    Only blocking checks may produce overall FAIL or PROVISIONAL.

    line 351
  21. Very specific:

    Use ../../shared/includes/seo-bot-registry.md for canonical bot taxonomy

    line 356
  22. Very specific:

    When Cloudflare/WAF/CDN is detected AND --live-url is not provided, G1 robots.txt checks are capped at PARTIAL with note: "WAF detected -- …

    line 358
  23. Distinctive:

    Check required + recommended fields per type (Organization, Article, Person, FAQPage, WebSite)

    line 363
  24. Distinctive:

    Never produce blocking gate failures

    line 372
  25. Distinctive:

    All heuristics are regex-based and deterministic, not LLM-subjective

    line 374
  26. Distinctive:

    Use these specific queries for GEO-relevant searches:

    line 378
  27. Distinctive:

    After all agents complete:

    line 398
  28. Very specific:

    If an imported seo-audit finding conflicts with geo-audit's own deeper check, geo-audit's result takes precedence.

    line 412
  29. Very specific:

    If any dimension is entirely missing (agent failed): mark all its checks as INSUFFICIENT DATA in scoring.

    line 422
  30. Distinctive:

    All 4 gates MUST have explicit status:

    line 452
  31. Very specific:

    Only blocking checks and critical gates control FAIL vs PROVISIONAL.

    line 470
  32. Distinctive:

    Before generating the report, verify:

    line 526
  33. Very specific:

    Critical Gate Completeness: All 4 gates have explicit PASS/FAIL/INSUFFICIENT DATA with evidence.

    line 530
  34. Distinctive:

    Evidence Completeness: Every FAIL finding has file:line or INSUFFICIENT DATA note.

    line 531
  35. Very specific:

    Finding ID Uniqueness: All stable IDs ({dimension}-{check_slug}) are unique.

    line 532
  36. Distinctive:

    Dimension Coverage: All 12 dimensions (G1-G12) have at least one check result (even if INSUFFICIENT DATA or N/A).

    line 535
  37. Distinctive:

    After the audit report is generated, run cross-model validation to catch score inflation and gate inconsistency.

    line 543
  38. Very specific:

    Record the verdicts: after triage: every finding is fixed, rejected (a false positive) or deferred — record each by its ID: in ONE call, …

    line 554
  39. Very specific:

    If adversarial-review is not in PATH: ~/.zuvo/adversarial-review (stable.

    line 556
  40. Distinctive:

    Wait for complete output. Then:

    line 558
  41. Very specific:

    If seo-audit was imported: Imported [N] findings from seo-audit ([date]).

    line 584
  42. Distinctive:

    All 12 dimensions with score, weight, weighted contribution, check count, and status:

    line 588
  43. Very specific:

    All 4 gates with explicit PASS/FAIL/INSUFFICIENT DATA and evidence:

    line 600
  44. Distinctive:

    Every finding in the execution plan uses this structure:

    line 611
  45. Very specific:

    Before generating JSON, read ../../shared/includes/audit-output-schema.md for the schema contract.

    line 670
  46. Very specific:

    After saving the markdown report, also save structured JSON findings for downstream consumption by zuvo:geo-fix and CI pipelines.

    line 672
  47. Very specific:

    All 12 dimensions (G1-G12) must appear in score.dimensions with their respective weights.

    line 727
  48. Very specific:

    After the findings are recorded through backlog-add, suggest the next skill to run:

    line 760
  49. Very specific:

    If gate_status = FAIL → VERDICT = INCOMPLETE.

    line 793
  50. Distinctive:

    Run: <ISO-8601-Z> geo-audit <project> <N-critical> <N-total> <VERDICT> - <N>-dimensions <NOTES> <BRANCH> <SHA7> <INCLUDES> <TIER>

    line 799
  51. Very specific:

    Follow the retrospective protocol from retrospective.md.

    line 804
  52. Distinctive:

    If gate check skips: print "RETRO: skipped (trivial session)" and proceed.

    line 806
  53. Very specific:

    After printing this block, append the Run: line value (without the Run: prefix) to the log file path resolved per run-logger.md.

    line 808

“Common” is measured: kept by at least 6 in 10 of the 19 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

zuvo:geo-audit -- GEO Readiness Audit

Deep code-level audit for Generative Engine Optimization. Examines source code, templates, and config files across 12 dimensions to determine how well a site is structured for AI citation by Google AI Overviews, ChatGPT Browse, Perplexity, and Gemini.

Scope: GEO readiness assessment, AI citation signal audit, schema graph analysis, content chunkability evaluation, AI crawler policy review. Out of scope: Traditional SEO (zuvo:seo-audit), code quality (zuvo:code-audit), security vulnerabilities (zuvo:security-audit), live URL crawling beyond --live-url flag.

Mandatory File Loading

Read these files before any work begins:

  1. ../../shared/includes/codesift-setup.md -- CodeSift discovery and tool selection
  2. ../../shared/includes/env-compat.md -- Agent dispatch and environment adaptation
  3. ../../shared/includes/seo-bot-registry.md -- Canonical AI/search bot taxonomy and live-probe scope
  4. ../../shared/includes/seo-page-profile-registry.md -- Profile-aware thresholds and downgrades
  5. ../../shared/includes/geo-check-registry.md -- Canonical check slugs for G1-G12
  6. ../../shared/includes/geo-fix-registry.md -- Fix type IDs, safety tiers, framework templates
  7. ../../shared/includes/audit-output-schema.md -- JSON output contract
  8. ../../shared/includes/backlog-protocol.md -- Backlog persistence contract
  9. ../../shared/includes/run-logger.md -- Run logging contract
  10. ../../shared/includes/retrospective.md -- Retrospective protocol

Print the checklist:

CORE FILES LOADED:
  1. codesift-setup.md             -- [READ | MISSING -> STOP]
  2. env-compat.md                 -- [READ | MISSING -> STOP]
  3. seo-bot-registry.md           -- [READ | MISSING -> STOP]
  4. seo-page-profile-registry.md  -- [READ | MISSING -> STOP]
  5. geo-check-registry.md         -- [READ | MISSING -> STOP]
  6. geo-fix-registry.md           -- [READ | MISSING -> STOP]
  7. audit-output-schema.md        -- [READ | MISSING -> STOP]
  8. backlog-protocol.md           -- [READ | MISSING -> STOP]
  9. run-logger.md                 -- [READ | MISSING -> STOP]
  10. retrospective.md                 -- [READ | MISSING -> STOP]

If any file is missing, STOP.

Environment Compatibility

Read ../../shared/includes/env-compat.md for agent dispatch patterns, path resolution, and progress tracking across all supported platforms.

MANDATORY TOOL CALLS — GEO Audit Validity Gate

INVALID if any tool below is skipped. "DEFERRED", "N/A" NOT valid reasons.

ToolTriggerSkip allowed?
get_file_treeAlwaysNO — locate llms.txt, robots.txt, sitemap.xml
search_textAlwaysNO — AI crawler directives, BLUF markers, JSON-LD
search_patternsAlwaysNO — GEO anti-patterns (paywalled SSR, weak headings)
audit_scanAlwaysNO — compound check
nextjs_metadata_auditNext.js detectedNO when Next.js

Forbidden: nextjs_metadata_audit: skipped (when Next), codesift: unavailable (when deferred), retrospective: skipped — REJECTED.

POSTAMBLE: report on disk → retro appended → ~/.zuvo/append-runlog exit 0. Every GEO finding needs path/to/file.ext:LINE (verify-audit gate).

Mandatory-tools-acknowledgment: I will run get_file_tree + search_text + search_patterns + audit_scan + nextjs_metadata_audit (when Next.js) + framework tools for this GEO audit. Every finding will cite a `path/to/file.ext:LINE` resolving in the current tree.

Use the deterministic preload helper FIRST. Run ~/.zuvo/compute-preload geo-audit "$PWD". Math gate enforced.


CodeSift Integration

Read ../../shared/includes/codesift-setup.md for the full initialization sequence.

Summary: Run the CodeSift setup from codesift-setup.md at skill start. Use CodeSift for schema discovery, bot policy search, and template analysis when available. Fall back to Grep/Read/Glob/Bash if unavailable.


Safety Gates (NON-NEGOTIABLE)

GATE 1 -- Read-Only Audit

This audit is read-only against source code.

Allowed write targets:

  • zuvo/audits/ for the report file (.md and .json)
  • memory/backlog.md and memory/findings/ (written by backlog-add) only when --persist-backlog is explicitly enabled

FORBIDDEN:

  • Writing to any source/production file
  • Modifying application code (suggest fixes in report, do not apply)
  • Installing packages or modifying dependencies

GATE 2 -- Live Audit Consent

If --live-url is provided, follow the live probe consent protocol from env-compat.md. Code-only mode is the default and requires no consent.


Phase 0: Parse $ARGUMENTS and Detection

0.1 Arguments

ArgumentBehavior
[path]Scope to directory or file (default: .)
--profileOverride auto-detection: blog, docs, ecommerce, marketing, app-shell
--cms / --no-cmsOverride CMS detection
--live-url URLEnable live checks (G1 HTTP status, content negotiation)
--lang LANGAnti-pattern language: en (default), pl
--persist-backlogRecord findings through backlog-add (Phase 6)

Default: code-only audit of current directory, auto-detect profile and CMS.

0.2 Stack Detection

Detect the web framework from project config files:

# Portable web framework detection (BSD/GNU compatible)
ASTRO=$(find . -maxdepth 3 -name "astro.config.*" 2>/dev/null | wc -l)
NEXT=$(find . -maxdepth 3 -name "next.config.*" 2>/dev/null | wc -l)
HUGO=$(find . -maxdepth 2 \( -name "hugo.toml" -o -name "hugo.yaml" \) 2>/dev/null | wc -l)
NUXT=$(find . -maxdepth 3 -name "nuxt.config.*" 2>/dev/null | wc -l)
SVELTEKIT=$(find . -maxdepth 3 -name "svelte.config.*" 2>/dev/null | wc -l)
GATSBY=$(find . -maxdepth 3 -name "gatsby-config.*" 2>/dev/null | wc -l)
WP=$(find . -maxdepth 3 -name "wp-config.php" 2>/dev/null | wc -l)
DOCUSAURUS=$(find . -maxdepth 3 -name "docusaurus.config.*" 2>/dev/null | wc -l)
VITEPRESS=$(find . -maxdepth 3 -path "*/.vitepress/config.*" 2>/dev/null | wc -l)
REMIX=$(find . -maxdepth 3 -name "remix.config.*" 2>/dev/null | wc -l)

Also detect:

SEO/GEO files:

  • robots.txt -- check public/robots.txt, static/robots.txt, or framework route (src/pages/robots.txt.ts, app/robots.ts)
  • sitemap -- check public/sitemap*.xml, framework config (@astrojs/sitemap in config, app/sitemap.ts), or generated output
  • llms.txt -- check public/llms.txt, static/llms.txt
  • llms-full.txt -- check public/llms-full.txt, static/llms-full.txt

Deploy platform:

  • vercel.json or .vercel/ -> Vercel
  • netlify.toml or _headers + _redirects -> Netlify
  • wrangler.toml or _worker.js -> Cloudflare
  • .htaccess -> Apache
  • nginx.conf or /etc/nginx/ refs -> Nginx
  • None detected -> Unknown

Store results:

DETECTED_STACK = [astro | nextjs | hugo | nuxt | sveltekit | gatsby | wordpress | docusaurus | vitepress | remix | html]
DETECTED_PLATFORM = [vercel | netlify | cloudflare | apache | nginx | unknown]

0.3 Profile Auto-Detection

Detect the site profile from codebase signals. Override with --profile flag.

Detection heuristics:

  • blog: markdown/MDX files in content/, src/content/, posts/, or blog route patterns
  • docs: docs/ directory, Docusaurus/Starlight/VitePress config, or docs route patterns
  • ecommerce: product/cart/checkout components, Shopify/Snipcart/Stripe product config
  • marketing: no content directory, few pages, landing page patterns (fallback default)
  • app-shell: SPA framework with no content routes, dashboard patterns

Fallback: marketing (safest default -- fewest content quality expectations).

Store result:

DETECTED_PROFILE = [blog | docs | ecommerce | marketing | app-shell]

0.4 CMS Auto-Detection

Detect CMS from code signals. Override with --cms or --no-cms flag.

Detection signals:

  • WordPress: wp-config.php, wp-content/, PHP WordPress function calls
  • Contentful: @contentful/rich-text-* imports, contentful SDK usage
  • Sanity: @sanity/client imports, sanity.config.*
  • Strapi: @strapi/* imports, Strapi API calls
  • Prismic: @prismicio/* imports

Headless CMS heuristic: GraphQL queries to known CMS endpoints (*.contentful.com, *.sanity.io, *.prismic.io, *.strapi.*). Generic GraphQL usage (analytics, feature flags) does NOT trigger CMS detection.

When CMS detected:

  • G9-G12 -> INSUFFICIENT DATA
  • Executive summary shows: SCOPE NOTICE: "CMS-backed site detected ([type]). Content quality dimensions (G9-G12) cannot be assessed from source code. Use --live-url for partial live verification."

This is a first-class status in the report header, not a footnote.

Store result:

CMS_DETECTED = [wordpress | contentful | sanity | strapi | prismic | none]

0.5 seo-audit Import Protocol

Import overlapping findings from the most recent seo-audit JSON output to avoid re-auditing what seo-audit already checked.

  1. Scan zuvo/audits/ for seo-audit-*.json files
  2. If found, select the file with the lexicographically greatest filename (ISO date + optional -N suffix ensures correct ordering)
  3. Extract findings with layer: geo from dimensions D3, D5, D9, D10. Also extract critical_gates.CG5 status from the JSON root object (CG5 is a gate value, not a dimension finding -- handled separately).
  4. Map to geo-audit dimensions:
    • D5 robots-ai-policy -> G1
    • D5 llms-txt-present -> G3
    • D3 JSON-LD checks -> G2
    • D3 CG5 (SSR) -> G4
    • D9 heading-structure -> G10
    • D9 answer-first -> G9
    • D10 freshness -> G5
    • D10 semantic-html -> G6
    • D10 eeat-signals -> G2/G5
  5. Tag imported findings [IMPORTED:seo-audit] and use the seo-audit's evidence
  6. If the seo-audit JSON is >48h old: emit warning "seo-audit import is stale (>48h old). Results may not reflect current codebase state."
  7. If no seo-audit-*.json found: "No seo-audit JSON found. Running all checks from scratch."

Dependency direction constraint: geo-audit MAY read seo-audit output. seo-audit MUST NOT read geo-audit output. This prevents circular dependency between the two skills.

Print:

Stack: [framework] | Platform: [platform] | Profile: [profile] | CMS: [detected or none]
GEO files: robots.txt [found/MISSING] | sitemap [found/MISSING] | llms.txt [found/MISSING] | llms-full.txt [found/MISSING]
seo-audit import: [filename and date | "none -- running all checks from scratch"]

Phase 1: Parallel Agent Dispatch

Dispatch follows ../../shared/includes/execution-policy.md through env-compat. Reuse existing authorization within that policy; session restrictions take precedence. Run each required gate and report its actual independence or an unmet requirement.

Dispatch 3 agents in parallel. Each agent evaluates its assigned dimensions independently and returns raw check statuses only.

Dimension Grouping

GroupAgentFileDimensions
A (Crawl & Access)Crawl & Access Agentagents/geo-crawl-access.mdG1, G7, G8
B (Schema & Render)Schema & Render Agentagents/geo-schema-render.mdG2, G4, G5
C (Content Signals)Content Signals Agentagents/geo-content-signals.mdG3, G6, G9, G10, G11, G12

Agent Dispatch

Refer to ../../shared/includes/env-compat.md for dispatch patterns per environment.

Claude Code: Use the Agent tool to run all three in parallel:

Agent 1: GEO Crawl & Access (Group A)
  model: "sonnet"
  type: "Explore"
  instructions: read agents/geo-crawl-access.md
  input: detected_stack, detected_platform, detected_profile, cms_detected,
         imported_findings (G1/G7/G8 subset), file_paths, codesift_repo, lang

Agent 2: GEO Schema & Render (Group B)
  model: "sonnet"
  type: "Explore"
  instructions: read agents/geo-schema-render.md
  input: detected_stack, detected_profile, cms_detected,
         imported_findings (G2/G4/G5 subset), file_paths, codesift_repo, lang

Agent 3: GEO Content Signals (Group C)
  model: "sonnet"
  type: "Explore"
  instructions: read agents/geo-content-signals.md
  input: detected_stack, detected_profile, cms_detected,
         imported_findings (G3/G6/G9-G12 subset), file_paths, codesift_repo, lang
<!-- PLATFORM:CODEX -->

Codex: Define TOML agents per env-compat.md patterns. Each agent runs in read-only sandbox.

<!-- /PLATFORM:CODEX --><!-- PLATFORM:CURSOR -->

Cursor: No agent dispatch. Execute each agent's analysis sequentially yourself, maintaining identical output format.

<!-- /PLATFORM:CURSOR -->

If native agent dispatch is unavailable, run the three agent analyses sequentially yourself, preserve the same report sections, and note the fallback mode in the final audit header.

Waiting for Results

Collect all 3 agent reports before proceeding to Phase 2. Pass to each agent: detected_stack, detected_platform, detected_profile, cms_detected, imported_findings (subset for that agent's dimensions), file_paths, codesift_repo, lang.

If an agent fails or times out:

  • Retry once with same inputs
  • If retry fails: log the error, proceed with results from successful agents, mark the failed agent's dimensions as INSUFFICIENT DATA

Agent vs main scoring boundary: Agents return raw check statuses (PASS/PARTIAL/FAIL/INSUFFICIENT DATA) per check. The main agent calculates all numeric scores in Phase 3. Agents do NOT calculate dimension scores themselves.

Dimension Constraints (normative -- agents MUST follow)

Enforcement Model (normative)

Read ../../shared/includes/geo-check-registry.md as the single source of truth for owner_agent, layer, enforcement, and evidence_mode.

  • blocking: can produce overall FAIL or PROVISIONAL
  • scored: affects dimension and overall scores, but cannot alone flip the overall result
  • advisory: prioritized and reported, but excluded from pass/fail logic

Only blocking checks may produce overall FAIL or PROVISIONAL. Heuristic, advisory, or content-inaccessible findings must never create a blocking result without direct evidence.

For fix_type identifiers and safety classifications, agents MUST use ../../shared/includes/geo-fix-registry.md as the canonical source.

G1 -- AI Crawler Access:

  • Use ../../shared/includes/seo-bot-registry.md for canonical bot taxonomy
  • Training vs retrieval bot distinction is explicit: blocking GPTBot (training) while allowing ChatGPT-User (retrieval) is scored as PASS, not FAIL
  • When Cloudflare/WAF/CDN is detected AND --live-url is not provided, G1 robots.txt checks are capped at PARTIAL with note: "WAF detected -- robots.txt PASS cannot be confirmed without live verification"
  • WAF detection: scan for _headers, wrangler.toml, Cloudflare Pages config, Vercel vercel.json firewall rules

G2 -- Schema Graph:

  • Attribute richness matters: generic minimally-populated schema underperforms no schema (41.6% vs 59.8% citation rate per Growth Marshal research)
  • Check required + recommended fields per type (Organization, Article, Person, FAQPage, WebSite)
  • @graph pattern preferred over scattered inline schemas
  • Wikidata/Wikipedia sameAs links for entity disambiguation

G4 -- SSR & Rendering:

  • FAIL patterns: useEffect + JSON-LD injection (Next.js), client:load island containing schema (Astro), document.head.appendChild with schema script
  • PASS patterns: inline <script type="application/ld+json"> in layout/page component rendered server-side

G9-G12 -- Content Dimensions (advisory only):

  • Never produce blocking gate failures
  • On CMS-detected sites: INSUFFICIENT DATA
  • All heuristics are regex-based and deterministic, not LLM-subjective

CodeSift Query Patterns (when available)

Use these specific queries for GEO-relevant searches:

search_text(repo, "application/ld+json", file_pattern="*.{astro,tsx,html,php}")
search_text(repo, "@id", file_pattern="*.{astro,tsx,html,json}")
search_text(repo, "sameAs", file_pattern="*.{astro,tsx,html,json}")
search_text(repo, "canonical", file_pattern="*.{astro,tsx,html,php}")
search_text(repo, "robots", file_pattern="*.{txt,ts,js,toml,yaml}")
search_text(repo, "sitemap", file_pattern="*.{ts,js,mjs,toml,yaml}")
search_text(repo, "llms.txt")
search_text(repo, "dateModified", file_pattern="*.{astro,tsx,html,json}")
search_text(repo, "datePublished", file_pattern="*.{astro,tsx,html,json}")
search_text(repo, "client:load", file_pattern="*.astro")
search_text(repo, "use client", file_pattern="*.{tsx,jsx}")

Phase 2: Merge & Assign IDs

After all agents complete:

2.1 Concatenate Findings

Merge findings arrays from all 3 agents into a single ordered list.

2.2 Assign Stable IDs

Assign stable finding IDs using format {dimension}-{check_slug} (e.g., G2-schema-id-disconnected, G1-retrieval-bots-blocked). These IDs are deterministic across runs for the same codebase.

Also assign display-order numbers (F1, F2, ...) for human-readable reports. The stable ID is used for --finding filtering in geo-fix.

2.3 Conflict Resolution (imported vs own checks)

If an imported seo-audit finding conflicts with geo-audit's own deeper check, geo-audit's result takes precedence. Note in the finding's confidence_reason field:

"Overrides imported seo-audit finding [id] which reported [status]."

geo-audit's checks are deeper (e.g., @id graph connectivity goes beyond D3's basic schema presence check), so geo-audit's own result is authoritative.

2.4 Missing Dimensions

If any dimension is entirely missing (agent failed): mark all its checks as INSUFFICIENT DATA in scoring.


Phase 3: Scoring

3.1 Check Status -> Numeric Value

StatusValueNotes
PASS1.0Evidence confirms check passes
PARTIAL0.5Partially satisfied or minor issues
FAIL0.0Evidence confirms check fails
INSUFFICIENT DATAexcludedNot counted in denominator

3.2 Dimension Scores

Per dimension: score = (sum of check values / count of non-excluded checks) * 100

N/A rules per dimension (exclude from overall score):

DimensionN/A when
G9 (BLUF)No content corpus (no markdown/blog/pages directories)
G10 (Heading Structure)No content corpus
G11 (Citation Signals)docs or ecommerce profile
G12 (Anti-patterns)No content corpus

3.3 Critical Gates

All 4 gates MUST have explicit status:

GCG1: Retrieval bots not blocked in robots.txt        -- from G1
GCG2: Schema has @id field                             -- from G2
GCG3: JSON-LD is SSR-rendered (not client-only)        -- from G4
GCG4: Canonical tags present in layout                 -- from G7

Critical gate statuses:

  • PASS -- evidence confirms gate is satisfied
  • FAIL -- evidence confirms gate is not satisfied
  • INSUFFICIENT DATA -- static analysis is inconclusive and no live verification is available

Scoring rules:

  • Any blocking critical gate = FAIL -> overall result = FAIL regardless of score
  • Any blocking critical gate = INSUFFICIENT DATA -> overall result = PROVISIONAL until live/source verification is completed
  • PROVISIONAL does not block CI gates (it is not a FAIL) but flags incomplete assurance
  • Only blocking checks and critical gates control FAIL vs PROVISIONAL. scored and advisory findings may lower scores, but they never override the overall result on their own.

GCG2 detail: Schema present WITHOUT @id = G2 dimension PARTIAL score but GCG2 FAIL. Schema absent entirely = both G2 FAIL and GCG2 FAIL.

3.4 Weighted Overall Score

Dimension weights (sum to 100%):
  G1:  15%   G2:  18%   G3:   8%   G4:  12%
  G5:  10%   G6:   7%   G7:  10%   G8:   5%
  G9:   5%   G10:  4%   G11:  3%   G12:  3%

Active weights = sum of weights where dimension is NOT N/A
Overall = sum(dimension_score * weight) / active_weights * 100

3.5 Tier Assignment

A (>= 85): GEO-ready. Strong AI citation signals across all dimensions.
B (70-84): Good foundation. Optimization opportunities identified.
C (50-69): Significant gaps. Prioritized fixes needed for AI visibility.
D (< 50):  Major issues. Site unlikely to be cited by AI systems.

Result overrides:
- Any blocking critical gate = FAIL -> result = "FAIL" (regardless of tier)
- Any blocking critical gate = INSUFFICIENT DATA -> result = "PROVISIONAL"
Tier is always calculated from score: A/B/C/D.

3.6 3D Priority Calculation

For each finding:

SEO Impact:      HIGH(3) / MEDIUM(2) / LOW(1)
Business Impact: HIGH(3) / MEDIUM(2) / LOW(1)
Fix Effort:      EASY(1) / MEDIUM(2) / HARD(3)

Priority = (SEO_impact * 0.4) + (Business_impact * 0.4) + ((4 - Effort) * 0.2)
Range: 1.0 - 3.0

Quick Win = Priority >= 2.0 AND Effort = EASY

Assignment rubric (to ensure consistent scoring):

Factor3 (HIGH)2 (MEDIUM)1 (LOW)
SEO ImpactBlocks AI citation, schema graph, or crawlability (GCG fail)Degrades citation signals or content discoverabilityCosmetic or minor optimization
Business ImpactAffects homepage, landing pages, or money pagesAffects secondary pages or non-revenue contentAffects low-traffic or internal pages
Fix Effort1-2 files, config change or additive insert3-5 files, template modification, testing needed6+ files, architecture change, or content creation

Phase 4: Validation

Before generating the report, verify:

  1. Count Consistency: Total checks = sum of all dimension checks. No check counted twice.
  2. Score Math: Recalculate overall from dimension scores * weights. Must match within 0.1.
  3. Critical Gate Completeness: All 4 gates have explicit PASS/FAIL/INSUFFICIENT DATA with evidence.
  4. Evidence Completeness: Every FAIL finding has file:line or INSUFFICIENT DATA note.
  5. Finding ID Uniqueness: All stable IDs ({dimension}-{check_slug}) are unique. No duplicates.
  6. Display ID Sequence: F-IDs are sequential (F1, F2, ...) with no gaps or duplicates.
  7. JSON Schema Compliance: Output matches audit-output-schema.md v1.1 with "skill": "geo-audit".
  8. Dimension Coverage: All 12 dimensions (G1-G12) have at least one check result (even if INSUFFICIENT DATA or N/A).

Fix any discrepancies before presenting to user.


Phase 4b: Adversarial Review on Audit Report (MANDATORY -- do NOT skip)

After the audit report is generated, run cross-model validation to catch score inflation and gate inconsistency. Runs on ALL audits.

Point --files at the report you just wrote — a literal [date] placeholder matches nothing and burns the pass.

# The report auto-increments (-2.md, -3.md) on same-day reruns — take the newest match.
REPORT=$(ls -t zuvo/audits/geo-audit-$(date +%F)*.md 2>/dev/null | head -1)
[ -n "$REPORT" ] && ~/.zuvo/adversarial-review --mode audit --files "$REPORT" \
  || echo "adversarial: report not found — write it first"

Record the verdicts after triage: every finding is fixed, rejected (a false positive) or deferred — record each by its ID: in ONE call, ~/.zuvo/adversarial-review --record-disposition ID-1 fixed --record-disposition ID-2 rejected …, per ../../shared/includes/adversarial-loop.md Step 4.9 (the review lists its IDs on stderr).

If adversarial-review is not in PATH: ~/.zuvo/adversarial-review (stable; the versioned cache path breaks after any release)

Wait for complete output. Then:

  • CRITICAL (FAIL gate not in verdict, severity mismatch, score inflation) -> fix in report before delivery
  • WARNING (N/A abuse, skipped check, INSUFFICIENT DATA overuse) -> append to Known Gaps section
  • INFO -> ignore

Phase 5: Report

5.1 Executive Summary

GEO AUDIT -- [project name]
----
GEO Score: [N]/100 -- Tier [A/B/C/D]
Result:    [PASS / FAIL / PROVISIONAL]
Profile:   [detected or overridden profile]
CMS:       [detected type or "none"]
----
Critical Gates: [N PASS] / [N FAIL] / [N INSUFFICIENT DATA]

Health scale: HEALTHY (80+), NEEDS ATTENTION (60-79), AT RISK (40-59), CRITICAL (<40).

Include scope notices (CMS, WAF advisories) immediately after the summary header, not buried in findings.

If seo-audit was imported: Imported [N] findings from seo-audit ([date]). [M] overridden by deeper geo-audit checks.

5.2 Dimension Score Table

All 12 dimensions with score, weight, weighted contribution, check count, and status:

| Dimension | Score | Weight | Weighted | Checks | Status |
|-----------|-------|--------|----------|--------|--------|
| G1: AI Crawler Access | 85 | 15% | 12.8 | 4 | PASS |
| G2: Schema Graph | 50 | 18% | 9.0 | 6 | PARTIAL |
| ... | ... | ... | ... | ... | ... |

5.3 Critical Gates

All 4 gates with explicit PASS/FAIL/INSUFFICIENT DATA and evidence:

GCG1: Retrieval bots not blocked  -- [PASS/FAIL/INSUFFICIENT DATA] -- [evidence]
GCG2: Schema has @id              -- [PASS/FAIL/INSUFFICIENT DATA] -- [evidence]
GCG3: JSON-LD is SSR-rendered     -- [PASS/FAIL/INSUFFICIENT DATA] -- [evidence]
GCG4: Canonical tags present      -- [PASS/FAIL/INSUFFICIENT DATA] -- [evidence]

5.4 Findings Detail

Every finding in the execution plan uses this structure:

[F-ID] [Dimension] [Severity] [Confidence]
  Issue: [one-line description]
  Evidence: [file:line or "code-only inference"]
  Why it matters: [GEO/citation impact in one sentence]
  Fix: [actionable instruction]
  Priority: [N.N] (SEO=[1-3] x Biz=[1-3] x Effort=[1-3])
  Enforcement: [blocking | scored | advisory]
  Layer: [technical-geo | content-geo]
  ETA: [minutes or "n/a"]

Confidence scale:
  HIGH   = direct source evidence (file:line confirms the finding)
  MEDIUM = inferred from config or indirect signals
  LOW    = heuristic or absence-based (e.g., file not found)
See also `../../shared/includes/geo-fix-registry.md` for the canonical confidence definitions.

Findings sorted by:

  1. Quick Wins first (Priority >= 2.0 AND Effort = EASY)
  2. Then by priority descending

5.5 Fix Coverage Summary

Count of findings by fix category:

Fixable (SAFE):      [N] findings -- auto-applied by geo-fix
Fixable (MODERATE):  [N] findings -- applied with validation by geo-fix --auto
Fixable (DANGEROUS): [N] findings -- requires confirmation per fix
Out of Scope:        [N] findings -- content scaffolds only, no auto-fix
Advisory:            [N] findings -- no fix template

5.6 Next-Action Routing

Audit ResultProposed ActionWhy
Any GCG = FAILFix critical gate firstGCG failures block all AI citation optimization
GEO Score < 50Run zuvo:geo-fix for quick winsTechnical fixes have highest ROI
G2 < 50 (Schema)Add/fix JSON-LD with @id connectivitySchema graph is the strongest GEO signal
G1 < 50 (Crawlers)Fix robots.txt AI bot policyNo access = no citation
G3 < 50 (llms.txt)Generate llms.txtLow effort, future-proofing
Tier A (>= 85)Periodic re-audit or add --live-url for verificationMaintain and measure

5.7 Save Report

mkdir -p audit-results

Save to: zuvo/audits/geo-audit-YYYY-MM-DD.md — at the project root (zuvo/ resolves via git rev-parse --show-toplevel; override $ZUVO_OUTPUT_DIR. See ../../shared/includes/report-output-location.md).

Auto-increment if a report for today already exists: geo-audit-YYYY-MM-DD-2.md, geo-audit-YYYY-MM-DD-3.md, etc.

5.8 JSON Output

Before generating JSON, read ../../shared/includes/audit-output-schema.md for the schema contract. For fix_type values and safety classifications, reference ../../shared/includes/geo-fix-registry.md.

After saving the markdown report, also save structured JSON findings for downstream consumption by zuvo:geo-fix and CI pipelines.

File: zuvo/audits/geo-audit-YYYY-MM-DD.json

Auto-increment with -N suffix if same-day file exists (same convention as .md).

Schema: See ../../shared/includes/audit-output-schema.md for the full schema definition.

Serialize from Phase 3 scoring results. Top-level structure:

{
  "version": "1.1",
  "skill": "geo-audit",
  "timestamp": "[ISO 8601]",
  "project": "[absolute path]",
  "args": "[arguments]",
  "stack": "[detected stack]",
  "profile": "[detected/overridden profile]",
  "cms_detected": "[CMS type or null]",
  "seo_audit_imported": "[seo-audit date or null]",
  "result": "[PASS | FAIL | PROVISIONAL]",
  "score": {
    "overall": "[0-100]",
    "tier": "[A/B/C/D]",
    "dimensions": {
      "G1": { "score": "[0-100]", "weight": 15, "checks": "[N]", "excluded": "[N]" }
    }
  },
  "critical_gates": [
    { "id": "GCG1", "name": "Retrieval bots not blocked", "dimension": "G1", "status": "[PASS|FAIL|INSUFFICIENT DATA]", "evidence": "..." }
  ],
  "findings": [
    {
      "id": "G2-schema-id-disconnected", "display_id": "F3", "dimension": "G2",
      "check": "schema-id-disconnected", "status": "FAIL", "severity": "HIGH",
      "enforcement": "scored", "layer": "technical-geo",
      "seo_impact": 3, "business_impact": 3, "effort": 2, "priority": 2.8,
      "confidence_reason": "...", "evidence": "src/layouts/BaseLayout.astro:42",
      "file": "src/layouts/BaseLayout.astro", "line": 42,
      "fix_type": "schema-id-link", "fix_safety": "MODERATE",
      "fix_params": { "file": "src/layouts/BaseLayout.astro" },
      "eta_minutes": 20, "imported_from": null
    }
  ],
  "scope_notices": [],
  "advisories": [],
  "summary": {
    "findings_count": { "total": 0, "critical": 0, "high": 0, "medium": 0, "low": 0 },
    "quick_wins": 0,
    "fixable": { "safe": 0, "moderate": 0, "dangerous": 0, "out_of_scope": 0, "no_template": 0 }
  }
}

All 12 dimensions (G1-G12) must appear in score.dimensions with their respective weights. All 4 critical gates must appear in critical_gates array.

Nullability: findings[].fix_type, findings[].fix_safety, and findings[].fix_params are nullable. Set to null for findings that have no auto-fix template (content quality, advisory checks, etc.). Consumers MUST check for null before using these fields.

The findings[].fix_type, findings[].fix_safety, and findings[].fix_params fields enable zuvo:geo-fix to apply automated fixes without re-scanning the codebase.

Extension fields (tolerated under v1.1 unknown-key rules): profile, cms_detected, seo_audit_imported, scope_notices, advisories.

CI-Parseable Summary:

GEO-AUDIT-RESULT: PASS|FAIL|PROVISIONAL score=NN tier=X critical=none|GCG-N

Phase 6: Backlog & Next Steps

Backlog Persistence (optional)

Activated with --persist-backlog flag.

Record each finding that meets at least one of these conditions through backlog-add, one call per finding (../../shared/includes/backlog-protocol.md "Filing from a skill"; the conditions are this audit's own selection, while the bars, the dedupe and the confidence routing are the protocol's and are not restated here):

  • Priority >= 2.0
  • Any Critical Gate = FAIL

A Generic write per finding: ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source geo-audit --file "$F" --line <n> [--anchor "$A"] [--evidence "$E"] --title "$T" when a repository file causes it (a template, a config, a content file). A finding on a live page or host with no repository file causing it (a response header, a status code, a rendered tag) takes --target "$U" --evidence "$E" in place of --file, --line and --anchor, whatever the class: ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source geo-audit --target "$U" --evidence "$E" --title "$T", U the page URL or the host. A live defect that a repository file causes is filed by that file (the file form), with the URL in --evidence "$E"; the target form is only for a finding with no repository file causing it. <N> is your confidence that the finding is real. This audit files CQ or BUG only: <C> is CQ for a failed check and BUG only when the finding names the input and the wrong output; BUG takes --anchor "$A", copied from the line that shows the defect ("The location rule"). T starts with the finding's check ID, <check-id>: <title> (e.g. schema-id-disconnected), so a later run can find the entry. A check that fails for the whole file is a whole-file finding: --line 1, a "Whole file: ..." title (after the check ID), and no --anchor; a whole-file BUG is filed at the line that shows the defect instead, with --anchor "$A" copied from it, never at line 1. Assign "$R" and "$T" before each call, "$F" only for a file finding, "$U" and "$E" only for a target finding (or "$E" alone when a file record carries the URL as evidence), "$A" only when you pass --anchor, as "Free text goes through a variable" in that section says; an empty value you pass is a bug, and backlog-add refuses a missing required field (REFUSED missing-field:<f>). Report the B- or F-id each call prints and Recorded: N (backlog: a, ledger: b) ("The report line" in backlog-protocol.md), plus each NOT PERSISTED (<reason>) by name; on a non-zero exit act on the protocol's exit table, and never append by hand.

An open entry is closed, never removed (Closing, not removing), when this run re-read its cited code and found it fixed. Close each open entry with the same file (or target) and the same title check ID whose cited code this run re-read and found fixed (for a file entry, its line and anchor); leave every other entry with that file and check ID open. A target (live-page) entry is closed only by a later live audit that re-reads the page, never by a source-file patch alone. A legacy entry written in the old {file}|{dimension}|{check} fingerprint format is matched by that fingerprint's file and check segments.

Recording findings through backlog-add commits and stages nothing. This audit lists the wrote= paths backlog-add printed as Backlog writes: <path>, ... (Backlog writes: none when there are none), in its report for the user when it runs standalone, and in its report for the calling skill's closing commit to carry when another skill runs it (backlog-protocol.md "Filing from a skill", Committing what backlog-add wrote).

Next-Action Suggestion

After the findings are recorded through backlog-add, suggest the next skill to run:

Suggested next step: zuvo:geo-fix
  [N] fixable findings detected ([M] SAFE, [K] MODERATE).
  Run `zuvo:geo-fix` to apply automated fixes, or `zuvo:geo-fix --dry-run` to preview.

GEO-AUDIT COMPLETE

Overall: [N]/100 -- Tier [A/B/C/D] | Result: [PASS/FAIL/PROVISIONAL] Profile: [profile] | CMS: [type or none] Critical gates: [N PASS] / [N FAIL] / [N INSUFFICIENT DATA] Findings: [N critical] / [N total] | Quick wins: [N]

Validity Gate (REQUIRED — print BEFORE Run line, AFTER retro append + append-runlog)

VALIDITY GATE
  required_tool_calls:
    get_file_tree: [<N> | NOT_CALLED]
    search_text: [<N> | NOT_CALLED]
    search_patterns: [<N> | NOT_CALLED]
    audit_scan: [<N> | NOT_CALLED]
    nextjs_metadata_audit: [<N> | not_required | NOT_CALLED]
  postamble:
    retros_log_appended: [yes(bytes_added=N) | NOT_APPENDED]
    retros_md_appended: [yes(entry_count=N) | NOT_APPENDED]
    verify_audit_pass: [yes(<verified>/<total>) | NOT_RUN | REJECTED]
  gate_status: [PASS | FAIL]

If gate_status = FAIL → VERDICT = INCOMPLETE. Append the Run line via the retro-gated wrapper (NOT direct >> runs.log):

printf '%b\n' "$RUN_LINE" | ~/.zuvo/append-runlog

Run: <ISO-8601-Z> geo-audit <project> <N-critical> <N-total> <VERDICT> - <N>-dimensions <NOTES> <BRANCH> <SHA7> <INCLUDES> <TIER>

Retrospective (REQUIRED)

Follow the retrospective protocol from retrospective.md. Gate check → structured questions → TSV emit → markdown append. If gate check skips: print "RETRO: skipped (trivial session)" and proceed.

After printing this block, append the Run: line value (without the Run: prefix) to the log file path resolved per run-logger.md.

VERDICT: PASS (0 critical findings), WARN (1-3 critical), FAIL (4+ critical).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.