tidaldb/scripts/check-docs.sh
jx12n ad4134e280 chore: doc consolidation, seven-dimension review fixes, and commit hooks
- Eliminate the tidal/ self-contained doc mirror; docs now have two canonical
  homes (root *.md and docs/), with planning/specs/research/reviews moved up
- Remove stale .agents/skills and .ai mirrors; canonicalize skills under .claude/
- Add pre-commit hook + scripts/check-docs.sh doc-guard + scripts/install-hooks.sh
- Implement M0-M10 seven-dimension review findings across engine, net, server,
  and tidalctl (durability, replication, query, WAL, storage, CLI hardening)
2026-06-08 22:46:28 -06:00

100 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Documentation consolidation guard.
#
# tidalDB documentation has exactly TWO canonical homes: the repository-root *.md
# files and docs/. This script prevents the doc set from drifting back into the
# per-crate mirror that once sprawled to 240+ duplicated files (tidal/docs/,
# tidal/ai-lookup/, tidal/site/, .ai/, .agents/skills/).
#
# Hard failures (exit 1): a mirror tree reappears; CLAUDE.md's Repository
# Structure stops listing a real workspace crate; a core-doc relative link breaks.
# Warnings (exit 0): planning-archive link breakage and ROADMAP phase-dir gaps.
#
# Run standalone (`scripts/check-docs.sh`) or via the pre-commit hook.
set -uo pipefail
cd "$(git rev-parse --show-toplevel)" || exit 2
fail=0
err() { printf ' \033[31m✗\033[0m %s\n' "$*" >&2; fail=1; }
ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; }
warn() { printf ' \033[33m!\033[0m %s\n' "$*"; }
echo "doc-guard: checking documentation consolidation invariants"
# ---------------------------------------------------------------------------
# 1. No doc-mirror tree may reappear.
# ---------------------------------------------------------------------------
mirror_hit=0
for p in tidal/docs tidal/ai-lookup tidal/site .ai .agents/skills; do
if [ -e "$p" ] || git ls-files --error-unmatch "$p" >/dev/null 2>&1; then
err "doc mirror reappeared: '$p' — docs live only at repo root + docs/ (see CLAUDE.md § Critical Rules)"
mirror_hit=1
fi
done
[ "$mirror_hit" -eq 0 ] && ok "no doc-mirror trees present"
# ---------------------------------------------------------------------------
# 2. CLAUDE.md Repository Structure must list every sibling workspace crate.
# ---------------------------------------------------------------------------
struct_hit=0
for crate in tidal-net tidal-server tidalctl; do
if grep -q "^\s*\"$crate\"" Cargo.toml; then
grep -q "$crate" CLAUDE.md || { err "CLAUDE.md does not mention workspace crate '$crate' (Cargo.toml/CLAUDE.md drift)"; struct_hit=1; }
fi
done
[ "$struct_hit" -eq 0 ] && ok "CLAUDE.md structure lists all sibling crates"
# ---------------------------------------------------------------------------
# 3. Core-doc relative links resolve (hard). Planning archive is warn-only.
# ---------------------------------------------------------------------------
check_links() { # $1 = file, $2 = "hard"|"warn"
local f="$1" mode="$2" dir broken=0
dir=$(dirname "$f")
# extract ](target) link targets
while IFS= read -r target; do
[ -z "$target" ] && continue
case "$target" in
http://*|https://*|mailto:*|\#*) continue ;; # external / anchors
*\{*\}*) continue ;; # {template} placeholders
esac
target=${target%%#*} # strip #anchor
[ -z "$target" ] && continue
if [ ! -e "$dir/$target" ]; then
if [ "$mode" = hard ]; then err "broken link in $f -> $target"; else warn "broken link in $f -> $target"; fi
broken=1
fi
done < <(grep -oE '\]\([^)]+\)' "$f" 2>/dev/null | sed -E 's/^\]\(//; s/\)$//')
return $broken
}
core_docs=(README.md CLAUDE.md AGENTS.md CONTRIBUTING.md CHANGELOG.md \
VISION.md USE_CASES.md SEQUENCE.md ARCHITECTURE.md API.md \
QUICKSTART.md CODING_GUIDELINES.md thoughts.md docs/README.md)
core_ok=1
for f in "${core_docs[@]}"; do [ -f "$f" ] && { check_links "$f" hard || core_ok=0; }; done
[ "$core_ok" -eq 1 ] && [ "$fail" -eq 0 ] && ok "core-doc links resolve"
# specs cross-links (hard)
for f in docs/specs/*.md; do [ -f "$f" ] && check_links "$f" hard >/dev/null || true; done
# planning archive (warn-only — historical docs)
for f in $(git ls-files 'docs/planning/*.md' 2>/dev/null); do check_links "$f" warn >/dev/null || true; done
# ---------------------------------------------------------------------------
# 4. ROADMAP COMPLETE milestones should have an on-disk planning dir (warn).
# ---------------------------------------------------------------------------
if [ -f docs/planning/ROADMAP.md ]; then
for n in $(grep -oE 'M[0-9]+' docs/planning/ROADMAP.md | sort -u | sed 's/M//'); do
if grep -qiE "\| *\*?\*?M$n\b.*(COMPLETE|✅)" docs/planning/ROADMAP.md 2>/dev/null; then
[ -d "docs/planning/milestone-$n" ] || warn "ROADMAP marks M$n complete but docs/planning/milestone-$n/ is missing (backlog: backfill phase/task docs)"
fi
done
fi
if [ "$fail" -ne 0 ]; then
echo "doc-guard: FAILED — fix the ✗ items above." >&2
exit 1
fi
echo "doc-guard: OK"