Files
serverAppConfigs/tests/docs_completeness.bats
T
Frank 2e33ec36c1 docs: three dangling links, and a gate so they cannot accumulate
The vault is the project's source of truth, so a link to a page that does not
exist is a promise the repo cannot keep. Three were dangling:

- Dankclaw/Gitea.md pointed twice at dankclaw/compose.yaml — **which this
  session retired**. The clean cutover obligation from my own change: the page
  now describes the unit that publishes 3000 and the canonical Caddy unit, and
  records why the compose file is gone.
- Dankclaw/Hermes-Dashboard.md linked [[LAN DNS]]; the page is [[LAN-DNS]].
- LLM/frank-medium-radiance-mxfp4.md linked [[Observability on Dankclaw]]; the
  page is [[Observability]].

scripts/check-vault-links.py + a seam keep it that way. The checker models
Obsidian's rules, which took three corrections to get right — and each was a
false positive that would have sent me editing healthy docs:

  1. Wikilinks are case-insensitive. A case-sensitive matcher reported 45
     breaks; 39 were `[[qBittorrent]]` vs qbittorrent.md.
  2. `[[folder/Note]]` resolves by path suffix, not by stem.
  3. `%20` in a markdown link is URL-encoding, not a filename.

Validating the linter against the resolver it models is the prerequisite for
trusting any of its output — the same discipline that caught a bad assertion of
my own earlier tonight.
2026-09-16 07:43:33 -07:00

46 lines
2.0 KiB
Bash

#!/usr/bin/env bats
# Seam: documentation completeness.
# Every service defined in a compose.yaml must have a matching documentation
# page in serverAppConfigs-docs/ and must be reachable from the index. This is the "docs first"
# contract of this repo: no service ships without documentation.
load helpers
@test "every compose service has a docs page" {
while IFS= read -r f; do
while IFS= read -r svc; do
# service name is the docs slug: RomM -> serverAppConfigs-docs/Dankserver/RomM.md
# matched case-insensitively (vault uses Title Case)
local doc
doc=$(find "$REPO_ROOT/serverAppConfigs-docs" -iname "${svc}.md" 2>/dev/null | head -1)
if [ -z "$doc" ]; then
echo "service '$svc' (from $f) has no docs page: expected serverAppConfigs-docs/**/${svc}.md"
return 1
fi
done < <(services_in "$f")
done < <(find "$REPO_ROOT" -name 'compose.yaml' -not -path '*/.git/*' | sort)
}
@test "docs index links every docs page" {
local index="$REPO_ROOT/serverAppConfigs-docs/index.md"
[ -f "$index" ] || { echo "missing $index"; return 1; }
while IFS= read -r page; do
local base
base=$(basename "$page" .md)
grep -qF "[[$base]]" "$index" || {
echo "docs page $base is not linked from $index"
return 1
}
done < <(find "$REPO_ROOT/serverAppConfigs-docs" -name '*.md' -not -name 'index.md' -not -path '*/.obsidian/*' | sort)
}
@test "no unresolved links in the docs vault" {
# A wikilink to a page that does not exist is a promise the repo cannot
# keep. Fixing the three that were dangling (2026-09-16) also meant
# retiring a reference to dankclaw/compose.yaml, which no longer exists.
# The checker models Obsidian's resolution rules; see its docstring for
# the three false-positive classes a naive version produces.
run "$REPO_ROOT/scripts/check-vault-links.py" "$REPO_ROOT/serverAppConfigs-docs"
[ "$status" -eq 0 ] || { echo "$output"; false; }
}