seo-fix
Skill7 starsChanged 27 days agoMITView on GitHub
Apply fixes from seo-audit findings. Reads audit JSON, classifies fixes by safety tier (SAFE/MODERATE/DANGEROUS), applies templates per framework. Supports Astro, Next.js, Hugo. Modes: default (SAFE only), --auto (SAFE+MODERATE), --all (all tiers, requires confirmation), --dry-run, --finding F1,F3, --fix-type sitemap-add,robots-fix,schema-cleanup.
3 other projects use a version of this.
Adopted by
Most recently taken up by
- 6t9xstar/one-step-seotheir own version, first seen Oct 8, 2026
- chajahmed/claude-seo-agenttheir own version, first seen Jul 19, 2026
- matthewcsimpson/agentic-playbookstheir own version, first seen Jun 24, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- 6t9xstar/one-step-seo★ 5same name
- chajahmed/claude-seo-agent★ 0same name
- matthewcsimpson/agentic-playbooks★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository58 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- a11y-auditSkill
- agent-benchmarkSkill
- api-auditSkill
- architectureSkill
- backlogSkill
- benchmarkSkill
- brainstormSkill
- buildSkill
- canarySkill
- ci-auditSkill
- code-auditSkill
- container-auditSkill
- content-auditSkill
- content-expandSkill
- content-fixSkill
- content-migrationSkill
- context-auditSkill
- db-auditSkill
- debugSkill
- dependency-auditSkill
- deploySkill
- design-reviewSkill
- designSkill
- docsSkill
- env-auditSkill
- executeSkill
- fix-testsSkill
- geo-auditSkill
- geo-fixSkill
- incidentSkill
- infra-auditSkill
- leadsSkill
- mutation-testSkill
- pentestSkill
- performance-auditSkill
- planSkill
- presentationSkill
- profile-sessionSkill
- receive-reviewSkill
- refactor-radarSkill
- refactorSkill
- release-docsSkill
- retroSkill
- reviewSkill
- security-auditSkill
- seo-auditSkill
- shipSkill
- skill-evalSkill
- structure-auditSkill
- test-auditSkill
- tests-performanceSkill
- ui-design-teamSkill
- using-zuvoSkill
- worktreeSkill
- write-articleSkill
- write-e2eSkill
- write-testsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
55 rules. 0 are common, 26 are distinctive, and 29 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
26
Distinctive
This author's own choices. Worth a look.
29
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 48
Read seo-audit JSON findings.
- Distinctive: line 55
Read these files before any work begins:
- Distinctive: line 92
If any file is missing, STOP.
- Distinctive: line 94
If native agent dispatch is unavailable, execute the workflow sequentially yourself, preserve the same validation checkpoints, and note the …
- Distinctive: line 118
Wait for explicit positive confirmation from user
- Very specific: line 123
If audit JSON timestamp is >24h old:
- Distinctive: line 125
Default mode (SAFE only): proceed with warning
- Distinctive: line 130
After applying fixes, run build verification.
- Distinctive: line 133
If no build command found: skip build verification, note in report
- Very specific: line 137
Treat exit code != 0 as a build failure even if logs contain optimistic lines such as Completed, Built, or similar
- Very specific: line 139
Only report build_result: PASS when the detected build exits with code 0
- Very specific: line 145
Before modifying any file, check for uncommitted changes in that file (git diff --name-only).
- Very specific: line 167
Run the knowledge prime protocol from knowledge-prime.md:
- Very specific: line 180
If [json-path] provided: use that file
- Very specific: line 182
If no JSON found: "No audit JSON found.
- Very specific: line 186
Version handshake: Check version field. Supported versions: "1.0", "1.1" (minor bumps are backward compatible).
- Distinctive: line 188
Required fields: If any missing, STOP with "Invalid audit JSON: missing field [X]":
- Very specific: line 200
PROVISIONAL audit handling: If result = "PROVISIONAL" (audit has INSUFFICIENT DATA gates):
- Very specific: line 201
Default mode and --dry-run: proceed normally, SAFE fixes are still safe regardless of incomplete gates
- Very specific: line 206
Read timestamp field. Calculate age.
- Distinctive: line 208
If >24h: apply GATE 3 (stale audit protection)
- Very specific: line 246
Read ../../shared/includes/seo-fix-registry.md for the canonical:
- Very specific: line 251
Read ../../shared/includes/seo-bot-registry.md when planning or validating robots-fix.
- Distinctive: line 269
All other fix_types: target file is deterministic from framework (single obvious location per the shared registry).
- Distinctive: line 321
If no content files found: skip llms-full.txt, note in report "No content files to aggregate for llms-full.txt"
- Distinctive: line 332
Before splitting, compress: remove duplicate content, strip boilerplate (repeated headers/footers), keep summaries only for low-value pages …
- Very specific: line 336
Add X-Robots-Tag: noindex for all generated llms*.txt paths (step 4)
- Very specific: line 342
When a framework exposes a static asset directory (public/ or static/), write both files there.
- Very specific: line 350
If _headers exists: append X-Robots-Tag: noindex rules for /llms.txt, /llms-full.txt, and any category files (/llms-*.txt)
- Very specific: line 352
If _headers does not exist AND headers-add is also being applied: include the rules in the new _headers file
- Very specific: line 354
If _headers does not exist AND no headers-add: create a minimal _headers with only the X-Robots-Tag rules
- Very specific: line 357
Do NOT use robots.txt Disallow — it blocks crawling but not indexing, and the URL can still appear in SERPs via external links
- Distinctive: line 379
Before modifying any file:
- Distinctive: line 381
Check GATE 5 (dirty file check)
- Distinctive: line 382
If multiple findings target the same file: parse existing structure first, then batch all patches into one coherent edit
- Distinctive: line 393
If existing related content found → upgrade to MODERATE (context-aware safety)
- Very specific: line 411
Apply: If all 3 layers pass, apply patch.
- Very specific: line 442
Check package.json scripts for build, or framework-specific: astro build, next build
- Distinctive: line 444
If no build command detected: skip, note "No build verification available" in report
- Distinctive: line 446
Run the detected build command and record:
- Very specific: line 459
If a built artifact is missing, empty, or the endpoint returns 404, the action cannot stay VERIFIED
- Distinctive: line 465
Before each file modification, snapshot is saved (Phase 2.0)
- Distinctive: line 466
If build fails: identify which file(s) caused the failure
- Very specific: line 468
If build passes but a required artifact/endpoint check fails (for example /llms-full.txt returns 404 or the built file is absent), rollback …
- Very specific: line 471
If still failing: rollback all files from current batch, mark remaining findings as NEEDS_REVIEW
- Distinctive: line 472
If build passes after selective rollback: keep successful fixes, report rolled-back ones
- Very specific: line 523
Record the verdicts: after triage: every finding is fixed, rejected (a false positive) or deferred — record each by its ID: in ONE call, …
- Very specific: line 525
If adversarial-review is not in PATH: ~/.zuvo/adversarial-review (stable.
- Distinctive: line 527
Wait for complete output. Handle findings by severity:
- Distinctive: line 532
Do NOT discard findings based on confidence alone.
- Distinctive: line 549
Do NOT simulate benefits of unverified fixes
- Very specific: line 625
Follow the retrospective protocol from retrospective.md.
- Distinctive: line 627
If gate check skips: print "RETRO: skipped (trivial session)" and proceed.
- Very specific: line 629
Append via wrapper (REQUIRED): Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this …
- Very specific: line 643
After work is complete, run the knowledge curation protocol from knowledge-curate.md:
“Common” is measured: kept by at least 6 in 10 of the 3 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.
Files
zuvo:seo-fix — Apply SEO Audit Fixes
Read seo-audit JSON findings. Plan patches per finding. Validate each patch. Apply by safety tier. Verify build. Report.
Scope: Post-audit fix application for SEO findings. Out of scope: Content writing, image generation, WordPress plugin config, React SPA fixes, redirects, noindex management, hreflang management. Out-of-scope content findings may still emit an advisory content scaffold.
Mandatory File Loading
Read these files before any work begins:
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.
../../shared/includes/codesift-setup.md-- CodeSift discovery and tool selection../../shared/includes/env-compat.md-- Agent dispatch and environment adaptation../../shared/includes/backlog-protocol.md-- Filing findings throughbacklog-add../../shared/includes/seo-fix-registry.md-- Canonical fix_type, safety, eta, and manual verification rules../../shared/includes/fix-output-schema.md-- JSON report contract../../shared/includes/seo-bot-registry.md-- Canonical AI/search bot policy taxonomy for robots fixes../../shared/includes/run-logger.md-- Run logging contract../../shared/includes/retrospective.md-- Retrospective protocol../../shared/includes/verification-protocol.md-- Fresh-evidence rules for build and endpoint verification../../shared/includes/knowledge-prime.md-- Project knowledge priming../../shared/includes/knowledge-curate.md-- Learning extraction after work../../shared/includes/no-pause-protocol.md-- HARD rule: no mid-batch pauses
Print the checklist:
CORE FILES LOADED:
1. codesift-setup.md -- [READ | MISSING -> STOP]
2. env-compat.md -- [READ | MISSING -> STOP]
3. backlog-protocol.md -- [READ | MISSING -> STOP]
4. seo-fix-registry.md -- [READ | MISSING -> STOP]
5. fix-output-schema.md -- [READ | MISSING -> STOP]
6. seo-bot-registry.md -- [READ | MISSING -> STOP]
7. ../../shared/includes/run-logger.md -- [READ | MISSING -> STOP]
8. ../../shared/includes/retrospective.md -- [READ | MISSING -> STOP]
9. ../../shared/includes/verification-protocol.md -- [READ | MISSING -> STOP]
10. ../../shared/includes/knowledge-prime.md -- READ/MISSING
11. ../../shared/includes/knowledge-curate.md -- READ/MISSING
12. ../../shared/includes/no-pause-protocol.md -- READ/MISSING (HARD: no mid-batch pauses)If any file is missing, STOP.
If native agent dispatch is unavailable, execute the workflow sequentially yourself, preserve the same validation checkpoints, and note the fallback mode in the final fix report.
Safety Gates (NON-NEGOTIABLE)
GATE 1 -- Write Scope
Allowed write targets:
- Files listed in template registry target paths (source/config files)
zuvo/audits/for the fix report (.mdand.json)memory/backlog.md(closing a resolved entry: tick it and its resolution marker; the archive helper moves it) andmemory/findings/(written bybacklog-add)
FORBIDDEN:
- Installing packages without explicit user confirmation (e.g.,
@astrojs/sitemap). If a fix requires dependency installation, escalate to NEEDS_REVIEW with install instructions. - Writing to files not referenced by template registry or finding evidence
- Deleting files
GATE 2 -- DANGEROUS Fix Confirmation
DANGEROUS fixes (canonical-fix) are NEVER auto-applied. Procedure:
- Show the exact diff that would be applied
- Explain the risk and expected SEO impact
- List target files
- Wait for explicit positive confirmation from user
- Without confirmation: mark as
MANUALand move to next finding
GATE 3 -- Stale Audit Protection
If audit JSON timestamp is >24h old:
--dry-runmode: proceed with warning- Default mode (SAFE only): proceed with warning
--autoor--allmode: require user confirmation before mutating. Without confirmation, fall back to--dry-run.
GATE 4 -- Build Verification
After applying fixes, run build verification. Detect build command from project:
package.jsonscripts:build,astro build,next build- Hugo:
hugobinary - If no build command found: skip build verification, note in report
Build verification claims follow verification-protocol.md:
- Capture stdout/stderr and the process exit code
- Treat
exit code != 0as a build failure even if logs contain optimistic lines such asCompleted,Built, or similar - Only report
build_result: PASSwhen the detected build exits with code0
Build failure → rollback (see Phase 3 rollback model).
GATE 5 -- Dirty File Check
Before modifying any file, check for uncommitted changes in that file (git diff --name-only). If the file has uncommitted changes, mark finding as NEEDS_REVIEW instead of modifying.
Arguments
| Argument | Behavior |
| (default) | Read latest JSON, apply SAFE fixes, recommend MODERATE + DANGEROUS |
--auto | Apply SAFE + MODERATE fixes automatically (skip DANGEROUS) |
--all | Apply all fixes including DANGEROUS (requires confirmation per fix) |
--dry-run | Show what would be fixed, change nothing |
--finding D4-sitemap-exists,D3-json-ld-ssr | Fix specific findings by stable ID (also accepts F1,F3 display IDs) |
--fix-type sitemap-add,json-ld-add | Fix specific fix_type categories |
[json-path] | Use specific JSON file instead of latest |
Note: --fix-type matches against findings[].fix_type field. For convenience aliases: sitemap = sitemap-add, json-ld = json-ld-add, og = meta-og-add, robots = robots-fix, headers = headers-add, canonical = canonical-fix.
Knowledge Prime
Run the knowledge prime protocol from knowledge-prime.md:
WORK_TYPE = "implementation"
WORK_KEYWORDS = <keywords from user request>
WORK_FILES = <files being touched>Phase 0: Load Findings
0.1 Locate audit JSON
- If
[json-path]provided: use that file - Otherwise: glob
zuvo/audits/seo-audit-*.json, parsetimestampfrom each, select most recent by timestamp (not filename) - If no JSON found: "No audit JSON found. Run
zuvo:seo-auditfirst." STOP.
0.2 Validate schema and version
Version handshake: Check version field. Supported versions: "1.0", "1.1" (minor bumps are backward compatible). If major version differs (e.g., "2.0"): STOP with "Unsupported audit schema version [X]. Update zuvo:seo-fix."
Required fields. If any missing, STOP with "Invalid audit JSON: missing field [X]":
Required: version, skill, timestamp, result, score.overall
Required array: findings[]
Required per finding: id, dimension, check, status, fix_type, fix_safety, fix_paramsNote: id should be the stable finding ID ({dimension}-{check}, e.g., D4-sitemap-exists). Sequential display IDs (F1, F2) are also accepted but --finding filtering prefers stable IDs.
0.3 Check result and freshness
PROVISIONAL audit handling: If result = "PROVISIONAL" (audit has INSUFFICIENT DATA gates):
- Default mode and
--dry-run: proceed normally, SAFE fixes are still safe regardless of incomplete gates --automode: restrict to SAFE fixes only (do not auto-apply MODERATE). Warn: "Audit is PROVISIONAL — restricting to SAFE fixes. Re-run seo-audit with --live-url for full coverage."--allmode: require confirmation per fix (same as DANGEROUS gate)
Freshness check:
Read timestamp field. Calculate age.
- If <=24h: proceed
- If >24h: apply GATE 3 (stale audit protection)
0.4 Print summary
AUDIT: seo-audit 2026-03-28 (2h ago) | Score: 53/100 (C) | 16 findings
SAFE: 5 findings (auto-fixable)
MODERATE: 5 findings (fixable with validation)
DANGEROUS: 1 finding (manual only)
OUT_OF_SCOPE: 2 findings (no template — content/E-E-A-T)
NO_TEMPLATE: 1 finding (fix_type not in registry)
INSUFFICIENT DATA: 1 finding (require live audit for verification)Phase 1: Detect Framework & Targets
1.1 Stack detection (self-contained)
Detect framework directly — do NOT depend on seo-audit Phase 0.2 at runtime:
# Inline stack detection (same logic as seo-audit, self-contained)
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)Cross-check with stack field in audit JSON. If mismatch: warn "Stack changed since audit. Consider re-running zuvo:seo-audit."
Detect output directory for static files:
- Astro/Next.js/React:
public/ - Hugo:
static/ - Unknown: search for
public/orstatic/, default topublic/
1.2 Fix Registry (shared contract)
Read ../../shared/includes/seo-fix-registry.md for the canonical:
- Template registry (fix_type → framework → target file priority)
- Safety classification (per framework, with context-aware upgrade rules)
- Fix parameters schema (required and optional params per fix_type)
Read ../../shared/includes/seo-bot-registry.md when planning or
validating robots-fix. Read ../../shared/includes/fix-output-schema.md
before writing the final JSON report.
The shared registry is the single source of truth. Do not override safety classifications locally.
Template target priorities (framework-specific):
The shared registry lists fix_types and safety. The TARGET FILE PRIORITY (which file to modify first) is skill-specific context that lives here:
| fix_type | astro target priority | nextjs target priority | hugo target priority |
json-ld-add | Existing layout <head> | Sitewide: app/layout.tsx. Page-level: specific page.tsx | layouts/partials/json-ld.html |
schema-cleanup | Existing layout or page-level JSON-LD block with duplicate/spam evidence | Existing app/layout.tsx or conflicting page-level JSON-LD block | Existing partial or page template with duplicate JSON-LD evidence |
meta-og-add | Existing BaseHead/head component | 1. generateMetadata(). 2. metadata export. 3. app/layout.tsx | 1. opengraph.html partial. 2. head.html |
headers-add | Cloudflare: public/_headers. Vercel: existing config first. Netlify: _headers or netlify.toml | Same, prefer existing mechanism | Same |
llms-txt-add | public/llms.txt + public/llms-full.txt | public/llms.txt + public/llms-full.txt | static/llms.txt + static/llms-full.txt |
All other fix_types: target file is deterministic from framework (single obvious location per the shared registry).
Additional seo-fix rules (not in shared registry):
- Context-aware safety upgrade: if target file has existing related config, upgrade one tier (SAFE→MODERATE, MODERATE→DANGEROUS)
- Dependency-touching fixes (e.g.,
sitemap-addon Astro needs@astrojs/sitemap): classify as NEEDS_REVIEW regardless of base safety lang-attr-add: if locale not derivable → NEEDS_REVIEW (do not default to en)alt-text-add: only images with decorative signals (role="presentation", aria-hidden, class icon/decoration). Others → NEEDS_REVIEWviewport-add: dedup scan before adding. Never duplicate.robots-fix: if Cloudflare, WAF, CDN, or another edge/network provider is detected or strongly suspected, do not auto-claim success from a file-level patch alone. Escalate toNEEDS_REVIEWwithnetwork_override_risk=true. Generated policy must cover all canonical bot keys fromseo-bot-registry.md, grouped by tier with explanatory comments: training defaults toDisallow, search/retrieval/user-proxy defaults toAllow, unless the source audit explicitly recommends a different conscious policy.json-ld-add: if existing schema is duplicated, spam-like, or already dense, reroute toschema-cleanuporNEEDS_REVIEWbefore adding more JSON-LD.schema-cleanup: exact duplicates may be cleaned automatically; conflicting non-identical blocks remainNEEDS_REVIEW.meta-og-add: normalizeog:typeby page class while preserving intentional page-specific overrides when evidence is clear.sitemap-add: if an existing sitemap has stale or uniformlastmodvalues, treat it asNEEDS_REVIEWinstead of silently preserving a misleading sitemap.headers-add: use the baseline header set from the shared registry and prefer report-only CSP unless a stronger existing CSP is already present. Baseline means HSTS,X-Content-Type-Options,Referrer-Policy,Permissions-Policy, andContent-Security-Policy-Report-Onlyunless the target platform already exposes a stronger equivalent.
llms-txt-add generation logic (two files + noindex header):
llms.txt(index): Generate from project metadata:
Sources for pages:# {site_name} > {one-line description from package.json or README first paragraph} ## Metadata - Last updated: {ISO date} - Language: {lang code from html lang attr or config} - Total pages: {count of indexed pages} ## Docs - [{title}]({path}): {first sentence of file}docs/*.md,content/**/*.md,README.md, sitemap routes. Each entry = title + path + first meaningful sentence.llms-full.txt(aggregated content): Concatenate actual content from existing files:- Scan:
README.md,docs/*.md,content/**/*.md,pages/**/*.md(ordered by importance) - For each file: extract title (H1 or frontmatter title) + full markdown body
- Join with
---separator between sections - Prepend same header as llms.txt (
# {site_name}\n> {description}\n\n## Metadata\n...) - If no content files found: skip llms-full.txt, note in report "No content files to aggregate for llms-full.txt"
- Max size per file: 500KB (truncate with "... [truncated, see full docs at {url}]")
- Size strategy by corpus:
- < 100 pages (corpus < 500KB): single
llms-full.txt - 100–300 pages (500KB–2MB): single
llms-full.txtcapped at 500KB, prioritize key content (homepage, landing pages, recent articles). Note excluded content in fix report. - 300+ pages (corpus > 2MB): split into category files linked from
llms.txtindex (e.g.llms-guides.txt,llms-blog.txt,llms-faq.txt). Each capped at 500KB. Derive categories from site nav, content dirs, or CMS taxonomy. - Before splitting, compress: remove duplicate content, strip boilerplate (repeated headers/footers), keep summaries only for low-value pages (changelogs, legal), drop embedded code blocks from non-technical content.
- < 100 pages (corpus < 500KB): single
- Add
X-Robots-Tag: noindexfor all generatedllms*.txtpaths (step 4)
- Scan:
- Validation: After generating:
- Both files must be valid markdown (no broken syntax)
- llms.txt links must point to paths that exist in the project
- llms-full.txt must have substantive content (not just headers)
- When a framework exposes a static asset directory (
public/orstatic/), write both files there. Do not prefer a route file such assrc/pages/llms-full.txt.tswhen the static target exists.
- X-Robots-Tag: noindex (MANDATORY): Prevent search engines from indexing
llms*.txt while keeping them crawlable for AI bots:
- Detect platform header config:
_headers(Cloudflare/Netlify),vercel.json(Vercel), or existing host config - If
_headersexists: appendX-Robots-Tag: noindexrules for/llms.txt,/llms-full.txt, and any category files (/llms-*.txt) - If
_headersdoes not exist ANDheaders-addis also being applied: include the rules in the new_headersfile - If
_headersdoes not exist AND noheaders-add: create a minimal_headerswith only theX-Robots-Tagrules - For Vercel: merge into
vercel.jsonheadersarray - Do NOT use
robots.txt Disallow— it blocks crawling but not indexing, and the URL can still appear in SERPs via external links - Record the header config file in
files_modified
- Detect platform header config:
Not in registry (manual only):
hreflang-add,noindex-change,redirect-add-- listed in shared registry as non-fixable
Advisory content scaffold:
- For out-of-scope content quality findings, seo-fix may emit a structured
content scaffold instead of mutating content files:
- suggested H2/H3 outline
- target word-count band
- answer-first opener template
Phase 2: Apply Fixes (plan → validate → apply)
For each finding, execute three stages: plan the patch, validate the patch, apply the patch. Group multiple fixes targeting the same file into a single batch edit.
2.0 Pre-flight per file
Before modifying any file:
- Save a snapshot of the original content (in memory, for rollback)
- Check GATE 5 (dirty file check)
- If multiple findings target the same file: parse existing structure first, then batch all patches into one coherent edit
2.1 SAFE fixes (auto-applied)
For each SAFE finding:
- Plan: Determine target file and insertion point per template registry priority
- Validate:
- File parse: target file is readable and has expected structure
- Dedup: the fix content does not already exist in the file (e.g., viewport meta already present)
- For
headers-add, preserve stronger existing policy and append only the missing baseline headers - If existing related content found → upgrade to MODERATE (context-aware safety)
- Apply: Edit the file. One edit per file (batch if multiple findings target same file).
- Record:
{ finding_id, action, file, status: "FIXED", eta_minutes, manual_checks, risk_notes, network_override_risk }
2.2 MODERATE fixes (applied with 3-layer validation)
For each MODERATE finding:
- Plan: Determine target file per priority list. Read target + surrounding context.
- Validate (3 layers):
- File parse: target file is syntactically valid (JSON, JSX, TOML, HTML)
- Framework convention: fix follows framework idiom (e.g., Next.js uses Metadata API, not manual
<meta>tags) - Finding-specific check:
- Sitemap:
siteURL is configured in framework config - Sitemap: if an existing sitemap has stale
lastmodvalues, downgrade toNEEDS_REVIEW - JSON-LD: schema properties match schema.org required fields for declared type and do not layer on top of duplicate/spam-like blocks
schema-cleanup: remove exact duplicates only; conflicting blocks or spam-like long descriptions stayNEEDS_REVIEW- Meta tags: image URLs are absolute (not relative), og:image dimensions noted,
og:typematches page class - Robots.txt: Googlebot is NOT blocked after fix, AI policy is explicit for relevant bot keys, and Cloudflare/network overrides are surfaced as review risk
- Apply: If all 3 layers pass, apply patch. If any layer fails: revert, mark
NEEDS_REVIEW. - Record:
{ finding_id, action, file, status: "FIXED" | "NEEDS_REVIEW", validation_result, eta_minutes, manual_checks, risk_notes, network_override_risk }
2.3 INSUFFICIENT DATA findings
Split into two categories:
- Cannot confirm bug (e.g., "HTTPS active" inconclusive in code-only):
INSUFFICIENT_DATA. Cannot fix what is not confirmed broken. - Cannot determine params (e.g., locale unknown but fix is otherwise safe):
offer
--dry-runsuggestion with placeholder params. Mark asNEEDS_PARAMSwith explicit manual checks.
2.4 DANGEROUS fixes (gated by GATE 2)
For each DANGEROUS finding:
- Plan: Generate the exact diff that would be applied
- Present to user:
- Show diff
- Explain specific risk (e.g., "Wrong canonical URL can deindex pages. A single misconfiguration caused 95% traffic loss in documented cases.")
- List all target files
- Show expected SEO impact if fix is correct
- Wait for confirmation: positive response → apply with 3-layer validation. No response or negative → mark
MANUAL, move to next finding. - Record:
{ finding_id, action, file, status: "FIXED" | "MANUAL", diff, risk }
Phase 3: Verify
3.1 Build verification
Detect project build command:
- Check
package.jsonscripts forbuild, or framework-specific:astro build,next build - Hugo: check for
hugobinary - If no build command detected: skip, note "No build verification available" in report
Run the detected build command and record:
- Exact command executed
- Full exit code
- Whether verification is build-only or build + artifact/endpoint checks
Hard rules:
build_result: PASSrequiresexit code = 0build_result: FAILfor any non-zero exit code, even if the log contains success-looking markers- For fixes that create or expose public artifacts/endpoints (
llms.txt,llms-full.txt,robots.txt,sitemap.xml), do a post-build existence check. Prefer inspecting the built output directory; if that is not deterministically available, use a local preview/HTTP probe instead. - If a built artifact is missing, empty, or the endpoint returns
404, the action cannot stayVERIFIED
3.2 Rollback model
Per-finding rollback (not just "last fix"):
- Before each file modification, snapshot is saved (Phase 2.0)
- If build fails: identify which file(s) caused the failure
- Rollback that file to snapshot, re-run build
- If build passes but a required artifact/endpoint check fails (for example
/llms-full.txtreturns404or the built file is absent), rollback the related fix or downgrade it toNEEDS_REVIEWwithverification="FAILED" - If still failing: rollback all files from current batch, mark remaining findings as
NEEDS_REVIEW - If build passes after selective rollback: keep successful fixes, report rolled-back ones
3.3 Gate re-check
For each fix_type applied, run a targeted mini-check (not full cross-file audit):
| fix_type | Re-check |
llms-txt-add | Confirm llms.txt exists at the static target, and if llms-full.txt was generated, verify the built artifact or local preview response for /llms-full.txt is non-empty and not 404. Confirm X-Robots-Tag: noindex is configured in platform headers (_headers, vercel.json, or equivalent) for all llms*.txt paths |
sitemap-add | Verify sitemap config exists in framework config (CG1 proxy) and note whether lastmod strategy still needs manual review |
json-ld-add | Grep for application/ld+json in target file (CG5 proxy) and confirm raw-source visibility is plausible |
schema-cleanup | Confirm duplicate schema blocks were reduced and no exact duplicates remain |
robots-fix | Parse robots.txt, confirm Googlebot not blocked (CG2 proxy), and keep network_override_risk=true when Cloudflare or another edge layer may still override behavior |
meta-og-add | Confirm og:image is absolute and og:type matches the resolved page class |
headers-add | Confirm the baseline header set is present in the chosen host config |
canonical-fix | Grep for rel="canonical" or alternates.canonical (CG4 proxy) |
| Others | Grep for injected content in target file |
Verification semantics:
VERIFIED: build exited0(when a build exists) and the targeted re-check passed, including artifact/endpoint checks for generated public filesESTIMATED: source-level mutation looks correct, but no deterministic artifact/endpoint check was possible after a successful buildFAILED: the re-check or artifact/endpoint check failed; do not leave the action reported as a clean success
3.4 Adversarial Review (MANDATORY — do NOT skip)
# Scoped review patch on stdout — the git index is NEVER touched (no staging).
# PATH args = the files the applied actions touched (3.3 target list).
# Quote each SEPARATELY — never one space-joined string or a bare $FILES: zsh does
# not word-split an unquoted expansion, so the helper gets the whole list as ONE
# path, matches nothing and exits 2. Use "${FILES[@]}" for an array.
# With NO PATH args the helper reviews the WHOLE dirty tree, untracked files
# included, and that content is sent to the external providers — always scope it.
# `|| _prc=$?` (never `; _prc=$?`): under `set -e` the plain form aborts the shell
# at the assignment, so exit 3 and the BLOCKED branch would never be reached.
# The BLOCKED branch ends in `false`, so the block's own exit status is non-zero:
# printing alone lets a `set -e` / `if ! …` caller sail past a review that never
# ran. `false`, not `exit`, so an inlining caller's shell is not killed.
if [ -x "$HOME/.zuvo/build-review-patch" ]; then
_prc=0; _patch=$("$HOME/.zuvo/build-review-patch" "<fixed-file-1>" "<fixed-file-2>") || _prc=$?
if [ "$_prc" -eq 3 ]; then echo "adversarial review: skipped (no changes)"
elif [ "$_prc" -ne 0 ]; then echo "BLOCKED: build-review-patch failed (rc=$_prc). Adversarial review did NOT run; do NOT proceed to commit and do NOT report this skill complete" >&2; false
else printf '%s\n' "$_patch" | ~/.zuvo/adversarial-review --mode code; fi
else
~/.zuvo/adversarial-review --mode code --files "<changed files>"
fiRecord 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. Handle findings by severity:
- CRITICAL — confirm it first: two or more lanes in its
.agreementgroup, or your own check (a failing test, a reproduced input, the cited code read and the defect traced). Confirmed → fix immediately. A single-lane CRITICAL you cannot confirm is triaged as a WARNING and reported as[CRITICAL→WARNING single-lane](adversarial-loop.mdStep 4). - WARNING — fix if localized (< 10 lines). Otherwise record it with
~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source seo-fix --file "$F" --line <n> [--anchor "$A"] --title "$T"(backlog-protocol.md"Filing from a skill", Adversarial review;<C>isCQorBUGand<N>the confidence, as for the Phase 5.1 write; pass--anchor "$A"only when the class requires one, BUG, SEC or DATA, or the finding carries one, never an invented one (--anchoris required on a BUG, SEC or DATA file record); assign"$R","$F"and"$T"before the call, and"$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, andbacklog-addrefuses a missing required field (REFUSED missing-field:<f>)). Report the B- or F-id it prints; a finding still not persisted is listed underNOT PERSISTED (<reason>), never appended by hand. - INFO — known concerns (max 3, one line each).
Do NOT discard findings based on confidence alone. Confidence measures how sure the reviewer is, not how important the issue is. A CRITICAL with low confidence means "verify this — if true, it's serious."
"Pre-existing" is NOT a reason to skip a finding. If the issue is in a file you are already editing, fix it now. If not, record it with ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source drive-by:seo-fix --file "$F" --line <n> [--anchor "$A"] --title "$T" (drive-by-findings.md; backlog-protocol.md "Filing from a skill", Adversarial review; <C> is the drive-by class the finding qualifies for under drive-by-findings.md (SEC, BUG, SIZE, DUP, CPLX, PERF, DEAD or READ) and <N> the confidence; the anchor as for the WARNING command above (--anchor is required on a BUG, SEC or DATA file record); assign "$R", "$F" and "$T" before the call, and "$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 it prints; a finding still not persisted is listed under NOT PERSISTED (<reason>), never appended by hand. The adversarial review found a real problem — don't dismiss it just because it existed before your changes.
Phase 4: Report
4.1 Estimated score calculation
estimated_after_score is calculated by:
- Take all findings from audit JSON
- For findings confirmed FIXED: change status from FAIL to PASS
- For findings with
NEEDS_REVIEW,MANUAL,OUT_OF_SCOPE,NO_TEMPLATE, orINSUFFICIENT_DATA: keep original status - For
INSUFFICIENT DATAfindings: keep excluded - Recalculate dimension scores and overall using same weights as seo-audit Phase 4
- Do NOT simulate benefits of unverified fixes
4.2 Report template
Estimated effort rubric: EASY = <30min, MEDIUM = 1-4h, HARD = 1+ day.
Report outputs must carry the expanded v1.1 semantics:
estimated_timefor each action or roll-upmanual_checksfor remaining platform validationpolicy_notesfor strategy or platform caveatsadvisory_scaffoldsfor non-mutating content follow-up
SEO FIX REPORT -- [project name]
----
Findings: 16 total | 6 fixed | 2 needs review | 1 needs params | 1 manual | 2 out of scope | 1 no template | 1 insufficient data
Score: 53 -> 74 (estimated from confirmed fixes only)
Build: [PASS | FAIL (rolled back N fixes) | NOT VERIFIED]
----
FIXED (auto-applied):
F2: Added llms.txt + llms-full companion public/llms.txt + public/llms-full.txt (new) [VERIFIED] ~10 min
F5: Added baseline security headers public/_headers (new) [VERIFIED] ~15 min
F8: Added font-display: swap src/styles/global.css:14 [VERIFIED] ~5 min
FIXED (validated):
F1: Added @astrojs/sitemap integration astro.config.mjs:3,8 [VERIFIED] ~20 min
F3: Added JSON-LD (WebSite + Organization) src/layouts/Layout.astro:12 [VERIFIED] ~30 min
F4: Cleaned duplicate Article schema src/layouts/Layout.astro:18 [VERIFIED] ~35 min
NEEDS REVIEW:
F5: robots.txt fix public/robots.txt
Reason: Cloudflare or another edge layer may override robots.txt
Manual checks: Dashboard AI bot controls, curl -A 'GPTBot' -I, curl -A 'Googlebot' -I
F6: OG image meta src/layouts/Layout.astro
Reason: og:image uses relative path or `og:type` does not match page class
F9: Existing sitemap metadata public/sitemap.xml
Reason: `lastmod` values are stale/uniform — review generation strategy before trusting the file
F10: lang attribute src/layouts/Layout.astro
Reason: locale not derivable from config — specify locale
NEEDS_PARAMS:
F11: Locale-dependent sitemap target astro.config.mjs
Reason: `site_url` or locale mapping must be supplied before mutation
MANUAL (DANGEROUS — user action required):
F12: Canonical URL configuration
Risk: wrong canonical can deindex pages
Suggested diff: [exact diff]
OUT OF SCOPE:
F13: Content quality gap Requires human writing
F14: E-E-A-T author information Requires real author data
ADVISORY CONTENT SCAFFOLD:
F13: Suggested H2 outline, target word band, and answer-first opener template
Reason: content scaffold is advisory only and does not mutate content files
NO TEMPLATE:
F15: hreflang tags DANGEROUS — requires locale strategy
INSUFFICIENT DATA:
F16: HTTPS verification Requires live audit
Run: <ISO-8601-Z> seo-fix <project> - - <VERDICT> - <DURATION> <NOTES> <BRANCH> <SHA7> <INCLUDES> <TIER>
NEXT STEPS:
1. Review NEEDS_REVIEW items above
2. Supply missing params for NEEDS PARAMS items, then rerun with `--fix-type` or `--finding`
3. Apply MANUAL fixes if appropriate: zuvo:seo-fix --finding F12 --all
4. Re-audit for exact score: zuvo:seo-auditRetrospective (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.
Append via wrapper (REQUIRED). Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this run. Order: retro bash executed → wrapper invoked → completion claimed.
printf '%b\n' "$RUN_LINE" | ~/.zuvo/append-runlogExpected stdout: OK: appended to runs.log (retro verified for <skill> on <project>). If exit 2 with RETRO_REQUIRED — go execute the retro bash from retrospective.md first; never bypass with ZUVO_SKIP_RETRO_GATE=1. After the wrapper succeeds, print a Logs: evidence line (tail -1 ~/.zuvo/retros.log, grep -c "^<!-- RETRO -->" ~/.zuvo/retros.md, tail -1 ~/.zuvo/runs.log) before claiming completion. Printing the markdown retro section without executing the bash leaves all three log files empty.
<DURATION>: use N-fixes (number of findings fixed).
Knowledge Curation
After work is complete, run the knowledge curation protocol from knowledge-curate.md:
WORK_TYPE = "implementation"
CALLER = "zuvo:seo-fix"
REFERENCE = <git SHA or relevant identifier>Phase 5: Update Backlog
5.1 Backlog operations
Per shared/includes/backlog-protocol.md "Filing from a skill":
| Finding status | Backlog action |
| FIXED | No record; the matching open entries are closed once the fix is committed (Closing, not removing; the rule below the table) |
| NEEDS_REVIEW | Record (backlog-add; --file "$F" when a repo file causes it, --target "$U" for a live page), E starting seo-review:. The helper bumps a re-sighting. |
| MANUAL | Record, as for NEEDS_REVIEW, E starting seo-manual: |
| OUT_OF_SCOPE | Record, as for NEEDS_REVIEW, E starting seo-manual: |
| NO_TEMPLATE | Record, as for NEEDS_REVIEW, E starting seo-manual: |
| INSUFFICIENT DATA | Do not add (unconfirmed issue) |
FIXED matches 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); every other entry with that file and check ID stays open. A target (live-page) entry is closed only by a later live audit that re-reads the page; a source-file patch alone never closes it. seo-fix does not commit, so there is no <sha7> to write: an entry FIXED matches stays open, the report lists its B-id on one line, Close after commit: B-…, B-… | none, and whoever commits the fix closes each one it lists (backlog-protocol.md "How to Persist Findings" step 3: tick - [x] and append [FIXED <sha7>]). A legacy entry written in the old {file}|{dimension}|{check} fingerprint format is matched by that fingerprint's file and check segments. E is the prefix from the table plus what was observed, the evidence that the finding is real.
A Generic write per recorded finding: ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source seo-fix --file "$F" --line <n> [--anchor "$A"] --evidence "$E" --title "$T" when a repository file causes it. A finding on a live page or host with no repository file causing it 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 seo-fix --target "$U" --evidence "$E" --title "$T". 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 skill 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 stable check ID, <check-id>: <title> (e.g. sitemap-exists, json-ld-ssr), NOT the fix_type, so FIXED can close the entry later. A whole-file finding is --line 1 with 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", "$E" and "$T" before each call, "$F" only for a file finding, "$U" only for a target finding, "$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.
Recording findings through backlog-add commits and stages nothing. This skill lists the wrote= paths backlog-add printed as Backlog writes: <path>, ... (Backlog writes: none when there are none) in its final summary, whether it runs standalone or inside another skill, for the calling skill's closing commit to carry when there is one (backlog-protocol.md "Filing from a skill", Committing what backlog-add wrote). With --dry-run make no backlog-add call and tick no entry: list in the report what would be recorded (status, file or target, check ID) and what would be closed.
5.2 Save report
- Save markdown report to
zuvo/audits/seo-fix-YYYY-MM-DD.mdAuto-increment:-2.md,-3.mdif same-day file exists. - Save fix JSON to
zuvo/audits/seo-fix-YYYY-MM-DD.json(schema:../../shared/includes/fix-output-schema.md):
{
"version": "1.1",
"skill": "seo-fix",
"timestamp": "[current ISO 8601]",
"project": "[working directory]",
"args": "[arguments]",
"source_audit": "zuvo/audits/seo-audit-2026-03-28.json",
"result": "PARTIAL",
"score": {
"before": 53,
"estimated_after": 74,
"method": "confirmed-fixes-only"
},
"summary": {
"total": 13,
"fixed": 7,
"needs_review": 2,
"manual": 1,
"out_of_scope": 1,
"no_template": 1,
"insufficient_data": 1
},
"actions": [
{
"finding_id": "D4-sitemap-exists",
"fix_type": "sitemap-add",
"status": "FIXED",
"file": "astro.config.mjs",
"verification": "VERIFIED",
"eta_minutes": 20,
"estimated_time": "<30 minutes",
"manual_checks": null,
"policy_notes": [
"Keep sitemap host aligned with the canonical root"
],
"advisory_scaffolds": null,
"risk_notes": [],
"network_override_risk": false
}
],
"manual_checks": [
"Confirm edge-layer bot controls do not override file-level crawler policy"
],
"estimated_time": {
"easy": 1,
"medium": 0,
"hard": 0
},
"policy_notes": [
"Training bots may remain blocked while user-proxy bots stay allowed"
],
"advisory_scaffolds": [
"Provide a human-written outline for thin content findings instead of auto-writing copy"
],
"files_modified": ["astro.config.mjs", "src/layouts/Layout.astro", "public/llms.txt"],
"build_result": "PASS"
}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
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.

