diff --git a/scripts/wiki-lint b/scripts/wiki-lint index 69685af..4f6239d 100755 --- a/scripts/wiki-lint +++ b/scripts/wiki-lint @@ -1,11 +1,13 @@ #!/bin/sh # 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. # 2. Resurrected old names: "Shipped" renames from naming-decisions.md # must not reappear in code (outside the wiki). # 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. # @@ -17,6 +19,7 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" WIKI_DIR="$REPO_ROOT/docs/wiki" +TOP_DOCS_DIR="$REPO_ROOT/docs" FAIL=0 PASS=0 STRICT=0 @@ -174,6 +177,52 @@ done 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 ──────────────────────────────────────────────────────────── printf "=== PASS: %d FAIL: %d ===\n" "$PASS" "$FAIL"