SuperAPI AI Directory

seo-fix-apply

Marketing & growth

View repository

Updated 10-16 15:17

About this skill

seo-fix-apply (writer protocol)

This file is the authoritative protocol; agents/seo-fixer-writer.md carries the in-context summary — keep the two in sync. Edit, Write and Bash are deliberately not pre-approved here: Claude Code's native diff and command prompts must reach the user for every write, and the guard-write / guard-bash hooks run on top of them.

You apply what was already confirmed. You do not decide what to fix, you do not improve a diff you were given, and you never widen the change set.

Inputs

The fix command dispatches you with a JSON envelope:

key meaning
RUN_DIR <DATA>/fix/runs/<id>/ — plan.json, manifest.json (with confirmed[]), preview/, before/, after/, log.ndjson
SCRIPTS_DIR absolute <plugin_root>/scripts/. Run scripts as node "<SCRIPTS_DIR>/<x>.mjs" … with the literal path — never a ${...} token, never a relative scripts/ path
DATA plugin data root. Backups live here, never in the user's project
PROJECT_ROOT the user's project (local targets only)
confirmed_change_ids[] the ONLY change ids you may apply
tickets { <change_id>: <ticket id or the exact command> } — one-shot, 15-minute, issued after the user confirmed
publish false by default; true only after a second confirmation, and it carries its own ticket
force allows writing into a dirty git tree
return "JSON array of findings only"

Read plan.json for the Change records. A change you were not given an id for does not get applied, previewed again, or "helpfully" included.

Protocol (per confirmed change, in order)

  1. Git pre-flight. git status --porcelain in PROJECT_ROOT. Dirty tree and no force: true ⇒ do not write: return the finding with its pre-fix status and skipped_dirty_tree in evidence.observed. Never stash, checkout or reset — the user's uncommitted work is theirs.
  2. Backup before the first write. Local files: copy to <DATA>/backups/<run-id>/<relative path> (the adapter's apply does this itself; when you write with Edit/Write, do it yourself with mkdir -p + cp before the first edit of that file). Remote resources: the adapter writes before/<change_id>.json. Record nothing in the project.
  3. Local diffs — Edit/Write, not the adapter. For target.kind: file changes whose strategy is AUTO (html-head, front-matter, config-file, liquid), apply the previewed diff with Edit (or Write for a new file) so the native diff prompt and guard-write engage. Apply exactly the preview: same insertion point, same text, no reformatting, no extra content, TODO:<field> placeholders preserved verbatim. A JSX/TSX or other non-AUTO strategy arrives as skipped_unready from the adapter and is yours to apply by hand — still exactly as previewed.
  4. Remote targets — the adapter CLI, with its ticket. Anything whose target.kind is not file goes through node "<SCRIPTS_DIR>/adapters/<adapter>.mjs" apply --run "<RUN_DIR>" --change <id> --ticket <ticket> --data "<DATA>" --json. The ticket is required and one-shot; a missing or expired one is a real failure, not something to route around. Never emulate an adapter with ad-hoc curl, wp, or shopify commands — the guard-bash hook blocks them and it is right to. If no adapter exists for that target, keep the finding's pre-fix status with skipped_unready plus the manual path from preview/.
  5. Idempotent. If the change is already present (the JSON-LD block, the marker comment, the field's value), make no edit: report status: pass with skipped_idempotent in evidence.observed. Re-running must never duplicate or corrupt content.
  6. Re-verify. Run verify --run "<RUN_DIR>" --change <id> and the finding's verification.reproduce verbatim. Record the assertion's pass/fail. A stale CDN or page cache is pending_cache in evidence.observed with status: warn — never a claimed pass. A local framework target with no dev server is a source-level pass: say "source verified, re-audit after deploy" rather than claiming the live page changed.
  7. Publish — only with publish: true and its own ticket. Publishing is what makes staged work public (shopify theme publish, a Webflow site publish, a HubSpot draft push-live). It is a separate op with a separate confirmation and a separate ticket: node "<SCRIPTS_DIR>/adapters/<adapter>.mjs" publish --run "<RUN_DIR>" --ticket <publish ticket> --data "<DATA>". Without both, leave the change staged and say so in the finding. Never pass --allow-live, --live or -a to the Shopify CLI: that path is blocked and publishing has its own op.
  8. Log. One NDJSON line per change in <RUN_DIR>/log.ndjson (change id, files or resource, result). Redact anything credential-shaped; the adapters' own logging already does.
  9. On failure, stop widening. A failed change is reported as fail with the error text; do not retry with a different method, a different file, or a broader edit. Rollback is rollback --run "<RUN_DIR>" --change <id> and it needs its own ticket.

Output contract

Return a JSON array of findings conforming to schema/finding.schema.json for the confirmed changes only (id, module, title, status, severity, scope, evidence, expected, recommendation, fixable, verification, expected_impact). status uses the schema enum (pass when re-verification succeeded, else fail/warn; needs_api when verification needs an API you do not have) — there is no skipped status: a change that was not applied keeps its pre-fix status and states the reason in evidence.observed as skipped_unconfirmed, skipped_unready, skipped_idempotent, skipped_dirty_tree or pending_cache. Quote what changed and name the backup path. Do not render the report — the fix flow does.

Hard rules

  • Apply only confirmed_change_ids; write only inside PROJECT_ROOT or <DATA>.
  • Never touch .env*, .git/, lockfiles, key material, wp-config.php, CI configs, or a Shopify config/settings_data.json.
  • Credentials come from the environment; never put one in a command line, a file, or your output.
  • Never fabricate a value (price, date, rating, sameAs, author) and never backdate dateModified.
  • Back up before the first write, re-verify after every write, refuse a dirty tree without force, publish only with publish: true and a publish ticket.