feat(wiki-lint): check #4 — top-level docs dangling links
PR #224 fixed two stale references to removed docs by hand. The root cause: wiki-lint only validated docs/wiki/, never the top-level docs/*.md — so a doc could link to a removed sibling forever with nothing to catch it. Add check #4: scan docs/*.md for two doc-reference patterns and verify they resolve (relative to docs/ or repo root): a) markdown links [label](local.md) — the exact #224 bug class b) backtick SHOUTING-CASE .md refs (e.g. `FOO-BAR.md`) Scoped to doc-to-doc references deliberately. External URLs, anchors, and cross-repo paths are skipped, and bare lowercase source filenames (env.sh, build.sh — often runtime/contextual) are out of scope, so the check has zero false positives on current main (171 pass) and fail-closes under --strict (which CI already runs). Calibrated by injecting fake removed-doc links: both the markdown link and the backtick doc-name ref are detected and exit non-zero. (Sam & Claude)
This commit is contained in:
parent
9552b79d0c
commit
8ffbf09f12
1 changed files with 50 additions and 1 deletions
|
|
@ -1,11 +1,13 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# wiki-lint — validate the docs/wiki/ knowledge base against the codebase.
|
# wiki-lint — validate the docs/wiki/ knowledge base against the codebase.
|
||||||
#
|
#
|
||||||
# Three deterministic checks (no LLM, CI-friendly):
|
# Four deterministic checks (no LLM, CI-friendly):
|
||||||
# 1. Dangling references: every path/line cited in wiki pages must exist.
|
# 1. Dangling references: every path/line cited in wiki pages must exist.
|
||||||
# 2. Resurrected old names: "Shipped" renames from naming-decisions.md
|
# 2. Resurrected old names: "Shipped" renames from naming-decisions.md
|
||||||
# must not reappear in code (outside the wiki).
|
# must not reappear in code (outside the wiki).
|
||||||
# 3. Orphan pages: every docs/wiki/*.md must be linked from index.md.
|
# 3. Orphan pages: every docs/wiki/*.md must be linked from index.md.
|
||||||
|
# 4. Top-level docs dangling links: markdown links + doc-name refs in
|
||||||
|
# docs/*.md must resolve (catches links to removed docs, e.g. PR #224).
|
||||||
#
|
#
|
||||||
# Output: PASS count or FAIL report. Non-zero exit on failure in --strict.
|
# Output: PASS count or FAIL report. Non-zero exit on failure in --strict.
|
||||||
#
|
#
|
||||||
|
|
@ -17,6 +19,7 @@ set -eu
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
WIKI_DIR="$REPO_ROOT/docs/wiki"
|
WIKI_DIR="$REPO_ROOT/docs/wiki"
|
||||||
|
TOP_DOCS_DIR="$REPO_ROOT/docs"
|
||||||
FAIL=0
|
FAIL=0
|
||||||
PASS=0
|
PASS=0
|
||||||
STRICT=0
|
STRICT=0
|
||||||
|
|
@ -174,6 +177,52 @@ done
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# ── 4. top-level docs dangling links ──────────────────────────────────
|
||||||
|
#
|
||||||
|
# Catches links to removed docs (the PR #224 bug class). Two patterns:
|
||||||
|
# a) markdown links [label](local.md) — resolve relative to docs/ or root
|
||||||
|
# b) backtick SHOUTING-CASE .md refs — doc-name references (e.g. FOO-BAR.md)
|
||||||
|
# External URLs, anchors, and cross-repo paths are skipped. Bare lowercase
|
||||||
|
# source filenames (env.sh, build.sh) are intentionally out of scope — those
|
||||||
|
# are often runtime/contextual, not committed doc references.
|
||||||
|
|
||||||
|
echo "=== 4. top-level docs dangling links ==="
|
||||||
|
|
||||||
|
for doc_file in "$TOP_DOCS_DIR"/*.md; do
|
||||||
|
base="$(basename "$doc_file")"
|
||||||
|
# index.md (README) and the wiki-split plan doc are meta; still check them.
|
||||||
|
|
||||||
|
# a) markdown links to local .md files
|
||||||
|
_tmp_links=$(mktemp)
|
||||||
|
grep -oE '\]\([^)]+\.md\)' "$doc_file" \
|
||||||
|
| sed 's/^\](//; s/)$//' > "$_tmp_links"
|
||||||
|
while IFS= read -r link; do
|
||||||
|
case "$link" in
|
||||||
|
http*|\#|mailto:*) continue ;;
|
||||||
|
esac
|
||||||
|
if [ -e "${TOP_DOCS_DIR}/$link" ] || [ -e "$REPO_ROOT/$link" ]; then
|
||||||
|
pass
|
||||||
|
else
|
||||||
|
fail "docs/$base → markdown link '$link' (target not found)"
|
||||||
|
fi
|
||||||
|
done < "$_tmp_links"
|
||||||
|
rm -f "$_tmp_links"
|
||||||
|
|
||||||
|
# b) backtick SHOUTING-CASE doc-name references (e.g. `COLIBRI-SKILLS.md`)
|
||||||
|
_tmp_docrefs=$(mktemp)
|
||||||
|
grep -o '`[A-Z][A-Z0-9_-]*\.md`' "$doc_file" | tr -d '`' > "$_tmp_docrefs"
|
||||||
|
while IFS= read -r docref; do
|
||||||
|
if [ -e "${TOP_DOCS_DIR}/$docref" ] || [ -e "$REPO_ROOT/$docref" ]; then
|
||||||
|
pass
|
||||||
|
else
|
||||||
|
fail "docs/$base → doc reference '$docref' (not found)"
|
||||||
|
fi
|
||||||
|
done < "$_tmp_docrefs"
|
||||||
|
rm -f "$_tmp_docrefs"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
# ── report ────────────────────────────────────────────────────────────
|
# ── report ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
printf "=== PASS: %d FAIL: %d ===\n" "$PASS" "$FAIL"
|
printf "=== PASS: %d FAIL: %d ===\n" "$PASS" "$FAIL"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue