Link-Rot Detection
Concept of the skill
What it is: The scheduled audit mechanism that scans markdown content for external links that no longer resolve correctly.
Mental model: Treat link checking as a batch health report, not a live page-rendering feature.
Why it exists: Broken or redirected links erode trust quietly, so the project needs a repeatable scan with clear evidence.
What it is NOT: It is not a one-off production incident investigation or UI design for broken-link badges.
Adjacent concepts: Markdown link extraction, status-code classification, soft-404 detection, polite crawling.
One-line analogy: It is a periodic inspection report for all outbound references.
Common misconception: HTTP 200 means healthy; soft-404s, unrelated redirects, and rate-limit behavior also need interpretation.
Coverage
- Markdown link extraction — pulling every
[text](url) and [text][ref] reference link out of every .md and .mdx file in the content tree
- External vs internal classification — what counts as external (different host) and what gets skipped (relative path, anchor link, mailto, tel)
- Status-code interpretation — 200 is healthy, 301/308 is a redirect (record the new URL but don't fail), 302/307 is transient (re-check next run), 404/410 is dead (flag), 5xx is transient (retry with backoff before flagging)
- Soft-404 detection — pages that return HTTP 200 but render an "unknown page" interstitial (compare body length / content against known soft-404 patterns)
- Rate-limiting and politeness — concurrent request budget per origin host, honoring
robots.txt crawl-delay, exponential backoff on 429
- Reporting shape — the scanner's output is a structured report (JSON + markdown summary), not a live alert; the report is the audit artifact
Philosophy of the skill
External links rot. Every site that's older than two years has at least a few. The choice is between knowing about them on a schedule or finding out from a user. A periodic scan with a published report turns link health into a maintenance task instead of an emergency. The discipline is to be conservative — distinguish persistent failures (404 across 3 runs) from transient ones (one 503 on a Tuesday) — and to never let the scanner itself become a denial-of-service vector against the targets it's checking.
Verification
Before merging any change to the link-rot scanner or its config:
Do NOT Use When
| Use instead |
When |
| (a frontend a11y / UX skill) |
The task is rendering a "this link may be broken" badge in the live UI |
debugging |
A specific link broke for a specific user and you need to reproduce |
documentation |
The task is writing the link-health policy doc, not the scanner |
refactor |
The task is restructuring the scanner without changing the detection contract |
1---2# name: stable skill identifier. Match the skill directory name or the final namespace segment.3# Lowercase letters/numbers with hyphen, slash, or colon separators.4name: link-rot-detection5# description: routing-facing summary of what the skill covers and when it activates.6# Include concrete triggers and an explicit negative boundary; keep routing semantics out of prose-only ambiguity.7description: "Use when authoring or reviewing a periodic-scan job that walks every external link in a markdown content set and flags 404s, redirects to unrelated content, and connection failures. Activate this skill whenever the task says 'check our links' or mentions a link-rot scan, broken-link audit, or link-health report. Do NOT use for live runtime link checking inside the rendered page (use a frontend a11y / UX skill) or for chasing a specific broken-link incident from a user report (use debugging)."89# === v8 Classification (subject + public; polyhierarchy via subjects[]) — see ADR-0017 ===10# subject: primary browse shelf — what the skill teaches. One of twelve closed values:11# backend-engineering / frontend-engineering / software-architecture / data-engineering / agent-ops / ai-engineering /12# quality-assurance / design / reasoning-strategy / software-engineering-method / knowledge-organization / product-domain.13subject: quality-assurance14# public: publishability/private-data gate. Boolean.15# true = publishable/shareable; false = private and excluded from public export.16# Project anchoring is carried separately by non-empty `project[]` plus `grounding`.17public: true18# taxonomy_domain: optional hierarchical sub-path within `subject`. Slash-delimited19# lowercase kebab-case segments. rename of the original v8 `domain`. Remove when the flat20# `subject` is sufficient.21taxonomy_domain: content/maintenance22# scope: free-text PRD-style statement of what the skill teaches and what it excludes.23# (v8 required; not an enum). Mirrors Coverage + Do NOT Use When at frontmatter level.24scope: "Portable workflow skill for periodic link-rot detection across markdown content sets. Excludes live runtime link checking, one-off incident debugging, and UI treatment of broken-link states."25# stability: lifecycle marker. One of:26# experimental (active development) / stable (production-ready) /27# frozen (no further changes expected) / deprecated.28# When `deprecated`, schema's allOf REQUIRES `superseded_by: <real-skill-name>`.29stability: experimental30# license: SPDX license identifier (e.g., MIT, Apache-2.0).31license: MIT32# compatibility: runtime compatibility object. Prefer structured fields33# (`agent_runtimes`, `node_version`) over free-text `notes`.34compatibility:35 agent_runtimes:36 - node37 node_version: ">=20"38 notes: "Portable across any markdown-content project; no codebase grounding required."39allowed-tools: Read Grep Bash40# keywords: semantic phrases for fuzzy router activation. v8 cap: max 10.41# Keep terms a user would actually type when starting a task in this skill's domain.42keywords:43 - link rot44 - link-rot detection45 - broken link audit46 - link health47 - dead link scan48 - 404 audit49 - external link check50 - periodic scan51 - markdown link extraction52 - link-rot report53# triggers: explicit-match activation phrases the router fires on literally.54# Use when label-based routing is intended; usually keywords + examples are enough.55triggers:56 - link-rot-detection57# examples: 2-5 realistic user prompts the skill SHOULD activate for.58# Written in the user's voice. Improves retrieval recall beyond keywords alone.59examples:60 - "design a periodic link-rot scan that runs nightly and posts a report"61 - "review the link-rot detector — does it handle redirects correctly?"62 - "explain how to test the link-rot scanner against a fixture set without hitting the network"63 - "should the scan retry on transient 5xx, or only flag persistent failures?"64# anti_examples: near-miss prompts that should route ELSEWHERE.65# Pair with relations.suppresses (or legacy boundary alias) to name the confusable territory's owner.66anti_examples:67 - "validate this internal route in the live app"68 - "the link to example.com just broke for one user"69 - "design the rendering of broken-link badges in the UI"70# relations: typed graph edges to sibling skills. Current fields:71# related (adjacency for browse / co-routing expansion) /72# suppresses (exclude listed skills from co-routing when THIS skill wins; write reason73# as "I own this exclusively over X", not "use X instead") /74# boundary (DEPRECATED alias of suppresses, retained for unmigrated skills) /75# verify_with (cross-check; co-loaded as one-hop expansion) /76# depends_on (composition; transitive — A→B→C loads all three) /77# broader / narrower (SKOS-style generalization) /78# disjoint_with (mutual exclusion for incompatible ownership).79relations:80 related:81 - documentation82 - debugging83 - refactor84 verify_with:85 - testing-strategy86---8788# Link-Rot Detection8990## Concept of the skill9192**What it is:** The scheduled audit mechanism that scans markdown content for external links that no longer resolve correctly.93**Mental model:** Treat link checking as a batch health report, not a live page-rendering feature.94**Why it exists:** Broken or redirected links erode trust quietly, so the project needs a repeatable scan with clear evidence.95**What it is NOT:** It is not a one-off production incident investigation or UI design for broken-link badges.96**Adjacent concepts:** Markdown link extraction, status-code classification, soft-404 detection, polite crawling.97**One-line analogy:** It is a periodic inspection report for all outbound references.98**Common misconception:** HTTP 200 means healthy; soft-404s, unrelated redirects, and rate-limit behavior also need interpretation.99100## Coverage101102- Markdown link extraction — pulling every `[text](url)` and `[text][ref]` reference link out of every `.md` and `.mdx` file in the content tree103- External vs internal classification — what counts as external (different host) and what gets skipped (relative path, anchor link, mailto, tel)104- Status-code interpretation — 200 is healthy, 301/308 is a redirect (record the new URL but don't fail), 302/307 is transient (re-check next run), 404/410 is dead (flag), 5xx is transient (retry with backoff before flagging)105- Soft-404 detection — pages that return HTTP 200 but render an "unknown page" interstitial (compare body length / content against known soft-404 patterns)106- Rate-limiting and politeness — concurrent request budget per origin host, honoring `robots.txt` crawl-delay, exponential backoff on 429107- Reporting shape — the scanner's output is a structured report (JSON + markdown summary), not a live alert; the report is the audit artifact108109## Philosophy of the skill110111External links rot. Every site that's older than two years has at least a few. The choice is between knowing about them on a schedule or finding out from a user. A periodic scan with a published report turns link health into a maintenance task instead of an emergency. The discipline is to be conservative — distinguish persistent failures (404 across 3 runs) from transient ones (one 503 on a Tuesday) — and to never let the scanner itself become a denial-of-service vector against the targets it's checking.112113## Verification114115Before merging any change to the link-rot scanner or its config:116117- [ ] The scanner extracts every link in `[text](url)` and `[text][ref]` form from `.md` and `.mdx` files; reference links resolve their targets before classification118- [ ] Internal links (relative paths, same-host absolute, anchors, mailto, tel) are explicitly excluded — confirmed by a fixture test with mixed link types119- [ ] Persistent failures are distinguished from transient ones — a link is only flagged after N consecutive failures across separate runs (default N=3)120- [ ] The scanner respects per-host concurrency limits and honors `robots.txt` crawl-delay; a single host getting many requests in tight sequence is a bug121- [ ] Soft-404 detection has a defined pattern set and an explicit "unknown" bucket for cases that don't match any known soft-404122- [ ] The scanner produces both a JSON report (machine-readable) and a markdown summary (human-readable) with link, status code, last-seen-OK timestamp, and recommendation123124## Do NOT Use When125126| Use instead | When |127|---|---|128| (a frontend a11y / UX skill) | The task is rendering a "this link may be broken" badge in the live UI |129| `debugging` | A specific link broke for a specific user and you need to reproduce |130| `documentation` | The task is writing the link-health policy doc, not the scanner |131| `refactor` | The task is restructuring the scanner without changing the detection contract |