seo-tech-audit
Judge the technical SEO facts produced by seo-crawl and return falsifiable findings + a health score. This is pure analysis — it does no network I/O and only reasons over the crawl JSON.
When to use
- The diagnose flow has a
seo-crawlresult and needs technical findings + a health score before writing the report. - Re-running after an
applyedit to confirm a finding cleared (the leading_indicator/failure_criterion drive the recheck).
When NOT to use
- Acquiring page data — that is
seo-crawl(this skill consumes its output). - Content quality / E-E-A-T / GEO citability scoring — separate skills.
- Rendering the dashboard or writing the action plan — that is the report skill.
Preconditions
- A
seo-crawlJSON object (its{ "data": { site, pages } }shape, or the baredata). - Python 3.9+ (stdlib only).
How to call
"$ORKAS_NODE" "$ORKAS_PC_DIR/bin/run-skill.cjs" seo-tech-audit audit -- --input <crawl.json> [--out <audit.json>]
--inputpath to theseo-crawlJSON (omit or-to read stdin).--outoptional path to also write the audit JSON.
Expected output
JSON on stdout:
{ "ok": true, "data": {
"health_score": 0,
"assessed_dimensions": [ "content_meta", "structure" ],
"not_assessed": [ { "dimension": "security", "check": "https",
"reason": "no request was made (local file crawl)" } ],
"dimension_scores": { "security": null, "indexability": 100, "content_meta": 100,
"structure": 100, "schema": 100, "i18n": 100, "media": 100,
"mobile": 100, "crawlability": 100 },
"summary": { "critical": 0, "high": 0, "medium": 0, "low": 0, "total": 0 },
"findings": [ {
"id": "title_missing", "dimension": "content_meta", "severity": "critical",
"title": "...", "evidence": "<fact from crawl>", "recommendation": "...",
"leading_indicator": "<metric that should move if fixed>",
"failure_criterion": "<how we know it did NOT work>", "data_tier": "Measured"
} ]
} }
Findings are sorted critical→low. Failure: {"ok": false, "error": "..."} (e.g. crawl JSON had no pages).
Scoring
health_score = clamp(100 − Σ severity weights, 0, 100) with weights critical=25, high=12, medium=6, low=2; the same weights drive per-dimension subscores. Scoring is deterministic so two runs over the same crawl are identical (drift-comparable).
A check whose input the crawl never measured does not run: its dimension scores null and is listed in not_assessed, and health_score covers only the checks that did run. This is why a local-file crawl cannot report a clean security or indexability result — scoring deducts for findings, so a check that silently does not fire would otherwise read as a pass.