Guardrail a docs site's links
Start a harness with the easiest possible check, because the point of the first one is to learn the loop, not to solve a hard problem.
Write check_links.py (or check-links.mjs) that walks a folder of markdown
pages and exits 0 only when every internal link resolves.
Rules to encode
- A markdown link
[text](target)whose target is relative must resolve to a file that exists. Pages are authored as.mdand served as.html, so../ui/atom.htmlresolves againstui/atom.md. - A target ending in
/resolves against that directory’sindex.md. - A fragment (
page.html#section) must match a heading on the target page, slugified the way the site generator does it. http:,https:,mailto:and template expressions are skipped — and the report says how many were skipped, so “no failures” cannot mean “nothing was checked”.- Links inside fenced code blocks are not links.
Then run it. Break four links in four different ways — a typo, a moved page, a dead anchor, a link that only exists inside a code sample — and let an agent fix them with your checker’s output as the only instruction.
Deliverables
- The checker script.
- A fixture site with the four broken links.
- A log: what the checker said, what the agent changed, and how many rounds.
Eval
Scored by the rubric in this page’s front matter at a threshold of 0.75. Read it first — it is the spec.
Rule 4 is the one that carries the lesson. A link checker that quietly skips everything it does not understand reports success on a completely broken site, and you will believe it. Making the skip count visible is the difference between a guardrail and a decoration.
How to think about it
Rules 1 and 2 are twenty lines and will catch most real breakage. Write them, run them, and look at what they say about a site you already believe is fine — that first report is the most informative thing that will happen in this exercise.
Rule 5 is where naive implementations fail, and the failure is instructive: a regex over the whole file finds links inside code samples, reports them as broken, and now your agent “fixes” a code example. A guardrail’s false positives are more expensive than its misses, because an agent cannot tell one from the other — it just tries to satisfy whatever you printed.
Rule 3 is the first one that needs you to model the generator’s behaviour rather than the file system, which is the moment a checker stops being generic and starts being about your site. That is normal and fine; write down the slugification rule you assumed, because it will be wrong once.
Trade-offs
Checking anchors requires parsing every target page, which turns an O(links) check into O(pages). On a site of a few hundred pages that is still under a second; at ten thousand it wants an index built once and reused.
Skipping external links keeps the check fast, offline and deterministic — and means a dead external link ships. That is the right default: an external checker belongs in a nightly job, not in the loop that gates a change, because its failures are not caused by the change.
Related
- Reading: Links · SEO
- Playbook: The agent harness
- Agent Skill:
server-links