Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ set-allowlist repository posture="selected":
actions-policy-test:
@bash scripts/tests/actions-policy-486-test.sh

# Internal-pointer integrity: every asciidoc link:/image: and markdown
# relative link in the repo must resolve. The September reorg moved files
# without updating relative links (first measured census 2026-09-19: 194
# live-canon findings, incl. inside RSR-SPEC-v2.adoc itself); this makes
# that class a standing check. Usage: just link-rot-guard [ROOT]
link-rot-guard root="." :
@bash scripts/link-rot-guard.sh "{{root}}"

# Wave-0 anti-false-green regression: proves each fixed validator CAN fail
false-green-test:
@bash scripts/tests/wave0-false-green-test.sh
Expand Down
71 changes: 71 additions & 0 deletions scripts/link-rot-guard.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: MPL-2.0
#
# link-rot-guard.sh — internal-pointer integrity check for the canon and
# the spine (and any repo).
#
# Rationale: the estate has repeatedly shipped docs that point at paths a
# reorg moved (ADR-0003 and archetypes/README.adoc cite
# rhodium-standard-repositories/spec/SCAFFOLD-LIFECYCLE.adoc; the spec now
# lives at 0-canon/rsr/). The deed-pointer sweep (standards f5ba975) proved
# the pattern works when done by hand; this makes it a standing check.
#
# Checks real link syntax only (not code-quoted paths — too noisy):
# 1. AsciiDoc: link:target[label] and image:target[alt]
# 2. Markdown: [text](target)
# Relative targets only; http(s)/mailto/absolute/fragment-stripped.
#
# Usage: link-rot-guard.sh [REPO_ROOT]

set -uo pipefail
ROOT="${1:-.}"
FAIL=0

# Archive exclusion: rhodium-standard-repositories/ is the LAST COPY of a
# dead upstream (8 of 10 satellites have no external home; standards-map
# note: "ARCHIVE, do not delete"). Its internal links are historical and
# deliberately unmaintained; the guard polices live content.
EXCLUDES=()
case "$(basename "$(cd "$ROOT" && pwd)")" in

Check failure on line 29 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUp&open=AaC7KW-mrFF_D3zH-cUp&pullRequest=862
standards) EXCLUDES+=("-path" "$ROOT/rhodium-standard-repositories" -prune -o) ;;
esac

check_target() {
local src="$1" target="$2"
target="${target%%#*}"
target="${target%%\?*}"
[ -z "$target" ] && return 0

Check failure on line 37 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUq&open=AaC7KW-mrFF_D3zH-cUq&pullRequest=862
case "$target" in

Check failure on line 38 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUr&open=AaC7KW-mrFF_D3zH-cUr&pullRequest=862
http*|https*|mailto:*|/*) return 0 ;;
esac
local dir
dir="$(dirname "$src")"
if [ -e "$dir/$target" ] || [ -e "$ROOT/$target" ]; then

Check failure on line 43 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUs&open=AaC7KW-mrFF_D3zH-cUs&pullRequest=862

Check failure on line 43 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUt&open=AaC7KW-mrFF_D3zH-cUt&pullRequest=862
return 0
fi
echo "BROKEN $src -> $target"
FAIL=1
return 0
}

# 1. AsciiDoc links and local images
while IFS= read -r -d '' f; do
while IFS= read -r target; do
check_target "$f" "$target"
done < <(grep -oE '(link|image):[^][]+\[' "$f" 2>/dev/null | sed -E 's/^(link|image)://; s/\[$//')
done < <(find "$ROOT" "${EXCLUDES[@]:-}" -path '*/.git' -prune -o -type f -name '*.adoc' -print0)

# 2. Markdown relative links
while IFS= read -r -d '' f; do
while IFS= read -r target; do
check_target "$f" "$target"
done < <(grep -oE '\]\([^)]+\)' "$f" 2>/dev/null | sed -E 's/^\]\(//; s/\)$//' | grep -vE '^(http|mailto|#|/)')
done < <(find "$ROOT" "${EXCLUDES[@]:-}" -path '*/.git' -prune -o -type f -name '*.md' -print0)

echo "----"
if [ "$FAIL" -ne 0 ]; then

Check failure on line 66 in scripts/link-rot-guard.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaC7KW-mrFF_D3zH-cUu&open=AaC7KW-mrFF_D3zH-cUu&pullRequest=862
echo "link rot: findings above (exit 1)"
exit 1
else
echo "all internal links resolve"
fi
Loading