diff --git a/docs/dream-cycle/LEDGER.md b/docs/dream-cycle/LEDGER.md
index ea369a4..28704f3 100644
--- a/docs/dream-cycle/LEDGER.md
+++ b/docs/dream-cycle/LEDGER.md
@@ -32,3 +32,5 @@ Schema note (2026-09-14, unmerged): FR6.6 of PRD-augmentation-conditions adds `R
| 2026-09-30 | seo-and-meta | Given the shipped page at `d4e4429` declares an Organization but never declares | NONE | https://github.com/DreamLab-AI/VisionFlow/pull/4 | yes | ACCEPT | | 65a9d02cf480 | | | |
| 2026-10-01 | build-pipeline | Given `website/static/robots.txt` reaches `dist/` only through `build.sh`'s whol | NONE | https://github.com/DreamLab-AI/VisionFlow/pull/11 | yes | ACCEPT | | 7f88b6695afe | | | |
| 2026-10-02 | seo-and-meta | Given the shipped page at `2480a8f` declares its brand only in `
`/nav cop | NONE | https://github.com/DreamLab-AI/VisionFlow/pull/12 | yes | ACCEPT | | 369fbc125f52 | | | |
+| 2026-10-03 | estate-health | Given canon declares seven sidestr crates on crates.io while `scripts/estate-hea | NONE | https://github.com/DreamLab-AI/VisionFlow/pull/13 | yes | ACCEPT | | 9c56f9ff2d42 | | | |
+| 2026-10-04 | content-integrity | Given the shipped page's `href="#…"` anchors all resolve to `id="…"` targets in | NONE | https://github.com/DreamLab-AI/VisionFlow/pull/14 | yes | ACCEPT | | 617ce2f95547 | | | |
diff --git a/scripts/dream-link-check.sh b/scripts/dream-link-check.sh
index 9139cdc..8240414 100755
--- a/scripts/dream-link-check.sh
+++ b/scripts/dream-link-check.sh
@@ -3,7 +3,7 @@
# the built static site. Emits a surface-dependent count; non-zero missing
# refs prints LINK-INTEGRITY-FAIL. Used as a dream-cycle evaluator entrypoint.
#
-# Two ref families are resolved:
+# Three ref families are resolved:
# 1. href/src attributes — ordinary markup links and asset references.
# 2. meta[content] media URLs (og:image, twitter:image, …). These were a
# shared blind spot: dream-meta-tags-scan.sh COUNTS the tags but never
@@ -11,6 +11,12 @@
# og:image pointing at a file absent from dist/ passed both gates. The
# social card is a required asset in website/assets.manifest.json, and
# this makes that requirement observable from the link surface too.
+# 3. in-page hash anchors. href="#id" must resolve to an id="id" target in
+# the same document. Family 1's pattern excludes every ref containing
+# '#', so pure-hash anchors (the entire nav) were invisible to this gate
+# (2026-09-29 ledger). The 2026-10-04 content-integrity night verified a
+# 0-dangling baseline, and the operator approved (2026-10-02) promoting
+# dangling > 0 to a failing condition.
#
# Social URLs are absolute on the canonical domain (they must be, for the
# crawlers), so "site-relative" here includes same-origin absolute URLs: the
@@ -38,7 +44,7 @@ resolve_ref() {
esac
}
-miss=0; checked=0
+miss=0; checked=0; anchors=0; dangling=0
while IFS= read -r f; do
d=$(dirname "$f")
@@ -58,6 +64,17 @@ while IFS= read -r f; do
checked=$((checked+1))
[ -e "$d/$t" ] || [ -e "./$t" ] || { echo "MISSING: $f -> $ref"; miss=$((miss+1)); }
done
+
+ # 3. in-page hash anchors — must resolve to an id= target in the same
+ # document. Family 1 skips refs containing '#', so a nav or card link
+ # to a missing section id passed this gate unnoticed.
+ for ref in $(grep -oE '[[:space:]]href="#[^"]+"' "$f" 2>/dev/null | sed -E 's/.*href="#([^"]*)"/\1/'); do
+ [ -n "$ref" ] || continue
+ anchors=$((anchors+1))
+ grep -qE "[[:space:]]id=\"$ref\"" "$f" 2>/dev/null \
+ || { echo "DANGLING-ANCHOR: $f -> #$ref"; dangling=$((dangling+1)); }
+ done
done < <(find . -name '*.html')
echo "internal-refs-checked: $checked missing: $miss"
-[ "$miss" -eq 0 ] && echo LINK-INTEGRITY-OK || echo LINK-INTEGRITY-FAIL
+echo "anchors-checked: $anchors dangling: $dangling"
+[ "$miss" -eq 0 ] && [ "$dangling" -eq 0 ] && echo LINK-INTEGRITY-OK || echo LINK-INTEGRITY-FAIL