diff --git a/scripts/check-doc-refs.sh b/scripts/check-doc-refs.sh
index 24894438..d6ae0a8f 100755
--- a/scripts/check-doc-refs.sh
+++ b/scripts/check-doc-refs.sh
@@ -9,12 +9,18 @@
# Emits a CI error annotation per hit and exits 1 if any pointer is dead, 0
# otherwise. Runs as its own job; seconds, pure git+grep.
#
-# Two arms, because a pointer is written two ways:
+# Three arms, because a pointer is written three ways:
# root — a repo-root-relative path in prose or a comment, `docs/
/.md`
# rel — a markdown link resolved against the linking file, [x](..//.md)
+# path — any repo-relative source path, `/src/.rs`, `nix/.nix`
# (spelled with placeholders on purpose: a literal example path here would be
# a dead pointer this script then reports against itself)
#
+# The third arm carries no skip-list, and could only be built after the prose
+# it would have flagged was fixed: every false positive was a sentence naming
+# where something *used to* live. Tolerating those needs a past-tense
+# heuristic — the permanent carve-out that teaches readers to ignore a gate.
+#
# Scope is every tracked file, deliberately unrestricted by type: prose
# describes a path in words, and a stale path in a config file (an ignore
# rule, a build input) is a live defect rather than a stale comment.
@@ -98,10 +104,45 @@ done < "$tmp/rel"
# something that resolves before a clean run means anything: if a rename, a
# pattern edit or a tree move silences the search, that is a failure of this
# script, not a property of the tree.
+# Third arm: repo-relative paths to any tracked file, not just `docs/**.md`.
+# A comment naming a moved `.rs` or `.nix` file misleads exactly as much as a
+# dead doc link, and nothing was checking it.
+#
+# Both ends are anchored, and each anchor is there because its absence
+# invented a path: unanchored, a `.js` extension matched the prefix of a
+# `.json` one, and a bare `/` matched mid-path inside `nix//…`,
+# conjuring a top-level twin of a real nested file. (Placeholders, per the
+# header — a literal here would be a dead path this arm reports against
+# itself.) `grep -o` has no lookaround, so the boundary char is stripped below.
+prefix_pattern='(nix|scripts|docs|frontend|branding|hive-[a-z0-9-]+/src|hivectl/src|swarmctl/src|swarm-controller/src)'
+ext_pattern='(nix|sh|rs|md|ts|tsx|js|css|toml)'
+path_pattern="(^|[^A-Za-z0-9._/-])${prefix_pattern}/[A-Za-z0-9._/-]+\\.${ext_pattern}($|[^A-Za-z0-9])"
+
+git ls-files -z | xargs -0 -r grep -nEo "$path_pattern" /dev/null 2>/dev/null \
+ > "$tmp/paths0" || true
+sed -E 's/^([^:]*:[0-9]+:)[^A-Za-z0-9]?/\1/; s/[^A-Za-z0-9]$//' "$tmp/paths0" \
+ > "$tmp/paths" || true
+
+while IFS=: read -r file lineno ref; do
+ [ -n "${ref:-}" ] || continue
+ exempt "$file" "$lineno" && continue
+ if [ -f "$ref" ]; then
+ alive=$((alive + 1))
+ else
+ dead=$((dead + 1))
+ printf '::error file=%s,line=%s::repo path does not resolve: %s\n' \
+ "$file" "$lineno" "$ref"
+ fi
+done < "$tmp/paths"
+
if [ "$(wc -l < "$tmp/root")" -eq 0 ]; then
echo 'check-doc-refs: root-pointer search matched nothing — pattern or tree changed' >&2
exit 1
fi
+if [ "$(wc -l < "$tmp/paths")" -eq 0 ]; then
+ echo 'check-doc-refs: repo-path search matched nothing — pattern or tree changed' >&2
+ exit 1
+fi
if [ "$(wc -l < "$tmp/rel")" -eq 0 ]; then
echo 'check-doc-refs: relative-link search matched nothing — pattern or tree changed' >&2
exit 1