Skip to content

feat(check-docs): assert every relative Markdown link resolves - #1193

Merged
Gerrrt merged 3 commits into
mainfrom
feat/check-docs-link-checker
Oct 14, 2026
Merged

Gerrrt merged 3 commits into
mainfrom
feat/check-docs-link-checker

Conversation

@Gerrrt

@Gerrrt Gerrrt commented Oct 14, 2026

Copy link
Copy Markdown
Owner

What changed

Adds a twelfth assertion to scripts/check_docs.py: check_relative_links(). Every inline Markdown link and image, in every tracked .md file, must resolve to a file in the tree, and its anchor (if any) to a heading in that file — the same way check_runbook_urls already checks a critical alert's runbook_url.

  • Runs over records too. Unlike the claim checks, this scans every tracked Markdown file, changelog.md and the ADRs included, because a dead link is a structural fault, not a claim about now. (The ADR-0095 and exercise-template links that slipped past review this week were both in records/templates.)
  • Repo-relative only. http(s), mailto and tel targets are not reached out to; pure #anchor links are checked against the file's own headings. Links inside fenced code are skipped, the same fence rule markdown_anchors uses.
  • It already caught two real bugs, now fixed in this PR: docs/observability.md linked ../adr/0070-… and ../adr/0088-…, which from docs/ resolve to a repository-root /adr/ that does not exist (the ADRs are under docs/adr/). Both are corrected to adr/….
  • README's assertion count and the script's docstring go from eleven to twelve; check_counts verifies README matches.

Why

check_adr_numbers checks that ADR files are numbered correctly, but nothing checked that a link to a doc lands. Two broken relative links shipped in a week and were caught only by a review bot. This closes that gap in CI.

Blast radius

None at runtime. This is a CI/validation script and three docs; it changes no deployed service.

  • No change to network segmentation or firewall rules
  • No new port published to a VLAN that could not already reach the service
  • No credential added outside secrets/*.sops.yaml

Verification

  • scripts/check_docs.py passes: docs OK — 158 Prometheus + 20 Loki rules, 8 dashboards, 156 panels, 12 assertions
  • ruff check scripts/check_docs.py: All checks passed
  • --self-test still passes (the anchor rules it covers)
  • Unit-checked the new function against a temp tree: it flags a missing file, a missing cross-file anchor and a missing same-file anchor, and passes valid links
  • markdownlint-cli2 reports 0 errors on the changed Markdown
  • Deployed to the lab and confirmed working: not applicable (CI script)

🤖 Generated with Claude Code

https://claude.ai/code/session_01A4769sNhNE79DH3SBN4YXx


Generated by Claude Code

check_adr_numbers checks that ADR files are numbered right; nothing
checked that a link to one lands. Two broken links shipped in a week — a
template naming a mis-typed ADR file, and docs/observability.md using
../adr/ from docs/ (which resolves to a nonexistent repository-root
/adr/). This adds a twelfth assertion: every inline Markdown link in
every tracked file resolves to a file in the tree, and its anchor, if
any, to a heading, the way check_runbook_urls already checks a critical
alert's runbook_url. It runs over records too, since a dead link is a
structural fault rather than a claim about now.

Also fixes the two ../adr/ links in docs/observability.md the new check
caught, and bumps README's assertion count to twelve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4769sNhNE79DH3SBN4YXx
Copilot AI balanced review requested due to automatic review settings October 14, 2026 10:54
CI runs ruff format --check under --require-all; the if condition it
wanted collapsed to one line. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4769sNhNE79DH3SBN4YXx

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The checker misses existing reference and nested links, scans untracked files, and permits targets outside the repository.

6 open findings
What changed in this PR

Adds repository-wide Markdown link validation and fixes two broken ADR links.

Changes:

  • Adds relative file and heading-anchor checks.
  • Updates documentation assertion counts.
  • Corrects observability ADR paths.
File Description
scripts/​check_docs.py Adds link validation.
README.md Updates assertion count.
docs/​observability.md Fixes ADR links.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/check_docs.py Outdated
Comment thread scripts/check_docs.py
Comment thread scripts/check_docs.py Outdated
Comment thread scripts/check_docs.py Outdated
Comment thread README.md
Comment thread scripts/check_docs.py Outdated
…-repo

Copilot's review of #1193:
- Parse reference definitions ([label]: target) and the outer target of a
  linked badge [![alt](img)](target), both of which the repo uses and the
  first cut missed. Footnotes ([^x]:) and label lines followed by unquoted
  prose are not link definitions, per CommonMark.
- Extraction and resolution move into markdown_link_targets and
  relative_link_problems, which --self-test now exercises with fixtures for
  every form and for missing files, missing anchors and out-of-repo targets.
- Discover files with git ls-files, not rglob, so untracked scratch files and
  the .claude/worktrees copies are not scanned (the check_image_pins.py
  convention).
- Reject a target that resolves outside the repository: GitHub cannot render
  a link that leaves the checkout, so an absolute path or an escaping ../ is
  as broken as a missing file.
- README's assertion list and the module docstring now name the relative-link
  check and its records-wide scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4769sNhNE79DH3SBN4YXx
@Gerrrt
Gerrrt merged commit 494aacb into main Oct 14, 2026
8 checks passed
@Gerrrt
Gerrrt deleted the feat/check-docs-link-checker branch October 14, 2026 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants