skills: add SEO/AEO audit and grounded-JSON-LD skills from citeworthyio/seo-agent
Evaluated citeworthyio/seo-agent for reusable process knowledge. Relevant to this fleet's WordPress content sites (granja travel news, content-agent pipeline): a crawl-diagnose-approve-recrawl-measure loop for SEO metadata changes, a three-layer AI-crawler accessibility audit (robots policy, bot delivery, readable content), and a fail-closed method for generating and safely injecting Article JSON-LD. Keyword research and citation-probe pieces of the source repo were skipped as underdeveloped or too infra-coupled to be reusable here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,15 @@ than assuming the delegate can fetch it itself.
|
||||
- `finishing-development-branches` -- verify the exact branch tip, confirm
|
||||
its base, and require an explicit integration choice before merge, Gitea
|
||||
push/PR, or cleanup.
|
||||
- `seo-change-lifecycle` -- audit the metadata crawlers actually receive, move
|
||||
fixes through approval and recrawl verification, then evaluate delayed
|
||||
page-level Search Console signals without claiming causation.
|
||||
- `ai-crawler-accessibility-audit` -- distinguish answer-engine bots from
|
||||
training crawlers, detect robots/CDN blocks and misleading llms.txt soft
|
||||
404s, and verify that non-JS fetchers receive readable page content.
|
||||
- `grounded-article-jsonld` -- generate Article structured data only from
|
||||
observed page facts, validate it at every boundary, and serialize it safely
|
||||
for additive script injection.
|
||||
|
||||
## Provenance
|
||||
|
||||
@@ -115,6 +124,7 @@ each skill's frontmatter:
|
||||
- [Leonxlnx/taste-skill](https://github.com/Leonxlnx/taste-skill) (MIT)
|
||||
- [JuliusBrussee/caveman](https://github.com/JuliusBrussee/caveman) (MIT;
|
||||
evaluated, no skill retained)
|
||||
- [citeworthyio/seo-agent](https://github.com/citeworthyio/seo-agent) (MIT)
|
||||
|
||||
## Vetting external skills
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: ai-crawler-accessibility-audit
|
||||
description: Use when auditing whether a site can be crawled, read, and cited by AI answer engines, especially for JavaScript-rendered sites, CDN bot controls, robots.txt policy, llms.txt, or unexplained absence from AI citations.
|
||||
license: MIT
|
||||
source: adapted from https://github.com/citeworthyio/seo-agent/blob/b78bb487f2819c3c7cc2ba6f3f5626ea0b841840/src/aeo.ts
|
||||
---
|
||||
|
||||
# AI Crawler Accessibility Audit
|
||||
|
||||
Audit three separate layers: policy, fetch delivery, and readable content. A
|
||||
healthy `<head>` does not make a client-rendered page citable because AI crawlers
|
||||
and user-fetchers generally do not execute the site's JavaScript.
|
||||
|
||||
## 1. Separate answer-engine bots from training bots
|
||||
|
||||
Read the live `/robots.txt` and evaluate its actual groups using longest-match
|
||||
robots rules; an exact user-agent group takes precedence over `*`, and `Allow`
|
||||
wins equal-length ties.
|
||||
|
||||
Treat blocking answer/search fetchers as a discoverability defect. Check at
|
||||
least OAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot,
|
||||
Perplexity-User, Googlebot, Bingbot, Meta-WebIndexer, DuckAssistBot,
|
||||
MistralAI-User, Amazonbot, and Applebot. Googlebot and Bingbot also feed AI
|
||||
answer surfaces.
|
||||
|
||||
Treat training-only crawlers separately: GPTBot, ClaudeBot, CCBot,
|
||||
Google-Extended, Applebot-Extended, meta-externalagent, and Bytespider. Allowing
|
||||
or denying these is a content-policy choice; do not claim that blocking them
|
||||
alone removes the site from citations. Explicit groups are preferable to an
|
||||
accidental allow-all because they document intent and survive managed
|
||||
`robots.txt` defaults.
|
||||
|
||||
## 2. Validate agent-facing text resources by content
|
||||
|
||||
Fetch `/llms.txt` and, if advertised, `/llms-full.txt`. A `404` is an honest
|
||||
absence. A `200` containing an HTML application shell or `text/html` is a soft
|
||||
404 and worse: an agent asked for a text map and received misleading content.
|
||||
Serve a real plain-text/Markdown resource or a clean absence. Treat these files
|
||||
as cheap insurance, not as a proven ranking lever.
|
||||
|
||||
## 3. Compare ordinary and AI-user-agent delivery
|
||||
|
||||
From successful content pages, rotate a small daily sample rather than checking
|
||||
the same first URLs forever. Fetch each sampled URL once normally and once with
|
||||
the relevant AI user agent.
|
||||
|
||||
- If the normal request is `200` but the AI request is `403`, `429`, or `451`,
|
||||
investigate CDN/WAF/bot-management policy. The controlled user-agent
|
||||
difference is stronger evidence than either status alone.
|
||||
- If the AI response succeeds, strip scripts, styles, and markup and inspect the
|
||||
visible body. Fewer than roughly 200 characters, without full content in
|
||||
`Article.articleBody`, is a useful empty-shell tripwire; inspect borderline
|
||||
pages rather than treating the number as a universal quality score.
|
||||
- Record transient fetch errors as unknown/check errors. Do not turn a failed
|
||||
measurement into a claim that the site is blocked or healthy.
|
||||
|
||||
## 4. Fix the serving layer, then verify it
|
||||
|
||||
Prefer server rendering or static rendering. If that is not viable, serve a
|
||||
real, fetchable HTML or Markdown content lane from the same source data, with
|
||||
correct content negotiation and `Vary` headers. `articleBody` can be a fallback,
|
||||
but visible server-delivered content is stronger.
|
||||
|
||||
Re-run both the ordinary and AI-user-agent requests against the public URL.
|
||||
Only suppress an empty-body finding when there is positive evidence that an
|
||||
alternate readable twin is live for that exact path. A configured twin that
|
||||
cannot actually be fetched must not hide a true failure.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: grounded-article-jsonld
|
||||
description: Use when generating, reviewing, storing, or injecting Article JSON-LD from page content. Prevent fabricated machine-readable claims and script-context injection while preserving existing structured data.
|
||||
license: MIT
|
||||
source: adapted from https://github.com/citeworthyio/seo-agent/blob/b78bb487f2819c3c7cc2ba6f3f5626ea0b841840/README.md#structured-data-json-ld and src/overrides.ts
|
||||
---
|
||||
|
||||
# Grounded Article JSON-LD
|
||||
|
||||
Structured data is a factual publication, not decorative copy. An invented
|
||||
author or date becomes a machine-readable claim under the site's name and may
|
||||
be consumed without a reader ever seeing and correcting it.
|
||||
|
||||
## Generate only from observed page facts
|
||||
|
||||
Give the generator only the page content actually fetched: title, description,
|
||||
canonical, visible article content, and explicit author/date/image fields. Tell
|
||||
it to omit anything it cannot point to in that material. Never infer an author,
|
||||
publisher, organization, date, image, rating, price, or other plausible default.
|
||||
|
||||
For an article page missing structured data, a minimal correct `Article` object
|
||||
with `@context`, `@type`, and a grounded headline is better than a rich-looking
|
||||
object containing guesses. Keep automated generation narrow to page-level types
|
||||
such as `Article`; organization, product, rating, and similar business claims
|
||||
need their own authoritative data source and review process.
|
||||
|
||||
## Validate at every boundary
|
||||
|
||||
Use the same fail-closed validator for generated drafts, manual proposals, and
|
||||
the final publishing write:
|
||||
|
||||
1. Parse as JSON.
|
||||
2. Accept one object or a non-empty array of objects, never primitives or null.
|
||||
3. Require every node to have an `@context` referencing `schema.org` and a
|
||||
non-empty `@type`.
|
||||
4. Enforce a bounded canonical serialization (the source implementation uses
|
||||
8,192 characters).
|
||||
5. Reject the value rather than storing it if any check fails.
|
||||
|
||||
Using one validator matters: otherwise a draft can pass review and fail at
|
||||
publish time, or a manual path can bypass the protections applied to AI output.
|
||||
|
||||
## Canonicalize for safe script embedding
|
||||
|
||||
Store JSON text without a `<script>` wrapper. After parsing, serialize it again
|
||||
and replace literal `<` and `>` in the serialized bytes with JSON escapes
|
||||
`\u003c` and `\u003e`. A JSON parser reconstructs the original characters, but
|
||||
the stored bytes cannot close an enclosing script element or open an HTML
|
||||
comment—even when the input expressed `<` through a JSON escape.
|
||||
|
||||
Do not use HTML entities such as `<` inside JSON: they change the data rather
|
||||
than safely encoding JSON for a script context. At injection time, reject any
|
||||
stored value that unexpectedly contains a literal `<`; it did not pass the
|
||||
canonical write path.
|
||||
|
||||
Add the validated block alongside existing JSON-LD instead of replacing it.
|
||||
Multiple blocks are valid, while overwriting an origin block can silently erase
|
||||
publisher-authored facts. Verify the public response contains a parseable block
|
||||
of the intended type; presence proves less than byte-for-byte comparison, so do
|
||||
not overstate what that check establishes.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
name: seo-change-lifecycle
|
||||
description: Use when auditing a content site's SEO, proposing metadata changes, or evaluating whether an applied SEO change helped. Treat findings as measured conditions and close the loop through approval, delivered-output verification, and delayed Search Console comparison.
|
||||
license: MIT
|
||||
source: adapted from https://github.com/citeworthyio/seo-agent/blob/b78bb487f2819c3c7cc2ba6f3f5626ea0b841840/README.md (How it works, Findings lifecycle, Analytics)
|
||||
---
|
||||
|
||||
# SEO Change Lifecycle
|
||||
|
||||
An SEO edit is not complete when source content changes. It is complete only
|
||||
after the served page is recrawled and the outcome has had time to emerge in
|
||||
search data. Use this loop for metadata and technical on-page changes.
|
||||
|
||||
## 1. Snapshot what crawlers actually receive
|
||||
|
||||
Start from the sitemap and fetch every listed final URL. Record the delivered
|
||||
status, title, meta description, canonical, robots directive, OpenGraph image,
|
||||
and JSON-LD types. Do not audit templates or CMS fields in place of the response:
|
||||
an edge rewrite, stale cache, redirect, or failed deployment can make the served
|
||||
page disagree with its source.
|
||||
|
||||
If no usable sitemap exists, use a bounded, same-origin link crawl and label the
|
||||
result incomplete. A link crawl cannot find orphan pages, so absence from that
|
||||
sample is not evidence that a page does not exist.
|
||||
|
||||
## 2. Diagnose deterministic conditions
|
||||
|
||||
Prefer repeatable rules whose evidence can be preserved and checked again. At a
|
||||
minimum, inspect:
|
||||
|
||||
- sitemap URLs returning errors or redirects;
|
||||
- missing titles, duplicate titles, and titles whose core exceeds 60 characters
|
||||
or whose delivered total exceeds 80;
|
||||
- missing descriptions, descriptions shorter than 70 or longer than 160
|
||||
characters, and descriptions ending in `...` because upstream truncation can
|
||||
hide an over-long source value;
|
||||
- missing or off-page canonicals;
|
||||
- `noindex` pages still present in the sitemap;
|
||||
- missing `og:image` and missing `Article` JSON-LD on known article routes.
|
||||
|
||||
Decode HTML entities before length checks so `&` counts as the one character a
|
||||
reader sees. Key each finding by `(path, rule)`: keep it open while the condition
|
||||
persists and auto-resolve it only when a fresh crawl no longer reproduces it.
|
||||
This makes findings sensors, not tickets someone can close without fixing the
|
||||
underlying page.
|
||||
|
||||
## 3. Draft a constrained proposal
|
||||
|
||||
Give a drafter the page's observed content, the exact failed rule, the current
|
||||
served value, and the site's positive style guidance. Require the smallest field
|
||||
change that can clear the finding. Use deterministic surgery when there is one
|
||||
right answer (for example, removing a duplicated brand suffix or replacing a
|
||||
canonical with the page's own normalized URL); a model adds risk without value.
|
||||
|
||||
Validate model output outside the model. Reject an unchanged value, forbidden
|
||||
terms, a title that includes a suffix the publishing layer will append, or copy
|
||||
outside the agreed length and sentence constraints. An invalid proposal is no
|
||||
proposal, not a reason to relax the gate.
|
||||
|
||||
## 4. Review, apply, journal, and recrawl
|
||||
|
||||
Require human approval by default. Record the old value, approved value, field,
|
||||
path, timestamp, and a reversible change identifier. After applying, fetch the
|
||||
public URL again and compare the delivered field with the approved value. Keep
|
||||
the finding in an `applied, awaiting recrawl` state until that comparison passes.
|
||||
|
||||
This extra verification catches the failures source inspection misses: a wrong
|
||||
cache key, route gap, stale CDN response, lost edge binding, or deployment that
|
||||
never reached the serving path. A successful write to a CMS or override store is
|
||||
not proof that crawlers see the change.
|
||||
|
||||
## 5. Measure impact without claiming causation
|
||||
|
||||
Use page-level Search Console data, not site-wide totals. Leave a three-day
|
||||
settling gap after the change, then compare equal 14-day before/after windows;
|
||||
repeat with 28-day windows when enough data exists. Compare clicks and
|
||||
impressions as per-day rates and CTR/position as impression-weighted averages.
|
||||
Do not calculate a verdict if either window is absent or the two windows total
|
||||
fewer than 50 impressions.
|
||||
|
||||
Treat a relative CTR movement of about 15% or an average-position movement of
|
||||
about 1.0 as a review signal, with impression shifts checked for confounding.
|
||||
Call the result correlation, never causation: seasonality, algorithm changes,
|
||||
competitors, and query-mix changes remain plausible explanations. A negative
|
||||
signal should open a review/revert decision, not trigger an automatic rollback.
|
||||
|
||||
Reference in New Issue
Block a user