seo-social-cards
seo-social-cards (M8)
Social cards control how a URL renders when shared on social, chat, and AI assistants — a CTR/sharing signal, not a ranking one (see Honesty). Open Graph metadata is the same <head>-level hygiene tier as the structured-data work in references/schema-tier1.md, so audit it alongside that module.
Inputs
Work from the PageSnapshot named in your dispatch envelope: read parsed from <run_dir>/pages/<slug>.json (metas[] for og:*/twitter:*, canonicals[], images[]); Grep pages/<slug>.html for verbatim evidence; site artifacts live in <run_dir>/site/{robots.json,sitemaps.json,discovery.json}. Deterministic findings already emitted by audit.mjs are listed in <run_dir>/findings.deterministic.json — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs" and use the printed snapshot path.
Audits
Working from the PageSnapshot (parsed_rendered when render.used is not none, else parsed):
- OG content:
og:title,og:description,og:url,og:typepresent and non-empty;og:urlmatches the canonical;og:typesane for the page (e.g.articlevswebsite). - OG image:
og:imagepresent, ~1200x630 (1.91:1) and >=200x200, and reachable (HTTP 200, image content-type, absolute URL). - Twitter/X:
twitter:card(prefersummary_large_image);twitter:title,twitter:description,twitter:image. Twitter falls back to OG when these are absent — note when it is relying on OG. - Fallback behavior: when OG is absent entirely, record what crawlers would derive (
<title>, meta description, first content<img>) so the finding shows the actual degraded card, not a guess.
Fixes
- AUTO (
fixable: auto): inject a complete OG + Twitter card block derived from the page's<title>/<h1>, meta description, canonical URL, and first in-content image. Additive, deterministic, emitted as a diff forfix. - The tool cannot create an image asset. It wires
og:image/twitter:imageonce a real path exists; until then it leaves a clearly-markedTODOplaceholder (no invented URL or dimensions). Never fabricate title/description text — derive from on-page content or leave the user aTODO.