Skip to content

docs: convert 21 dead BACKLOG anchors, repair 23 source-link targets, file #1098-#1100 (BACKLOG #1095) - #283

Merged
wshallwshall merged 3 commits into
mainfrom
claude/citation-link-rot-4808fe
Aug 7, 2026
Merged

docs: convert 21 dead BACKLOG anchors, repair 23 source-link targets, file #1098-#1100 (BACKLOG #1095)#283
wshallwshall merged 3 commits into
mainfrom
claude/citation-link-rot-4808fe

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Closes out the remaining docs half of BACKLOG #1095. Independent of #281 (the link-resolution gate): the two branches trial-merge clean and merge order does not matter.

What this changes

  • 21 dead BACKLOG line anchors converted to item references. A docs/BACKLOG.md:1234 anchor points at a line number that moves the moment anything above it is edited, so these were already wrong and would keep going wrong. Item references survive edits.
  • 23 source-link targets rewritten across the master test plan to move the line number out of the href. The repo's file_path:line_number convention appears inside link targets, where it cannot resolve as a path under any prefix.
  • BACKLOG #1095's own counts corrected. The item asserted figures that its own remediation superseded.
  • Files #1098, #1099, #1100:
    • 1098 - the coordination hook prints a session UUID where a commit SHA is expected
    • 1099 - #1094 describes an archival pass that generates anchors, but no archival tooling exists
    • 1100 - the master test plan asserts document contradictions that were resolved before it was written

Verification, and why it is stated rather than assumed

This is a docs-only PR, which is this repo's blind mode, not its cheap one: the doc-drift guards live in pytest gated on code == 'true', and .md is in the noncode allowlist, so they skip pre-merge and fire on the push to main afterwards. Run locally instead, using the exact command from the ungated doc-guard step in .github/workflows/ci.yml:

pytest -q -rs tests/test_asvs_file_surface_doc_drift.py tests/test_cloud_phi_hipaa_doc_drift.py \
  tests/test_crit2_inline_doc_drift.py tests/test_doc_ref_handle.py tests/test_docs_db_grants.py \
  tests/test_docs_runbooks.py tests/test_docs_security_pathways.py tests/test_security_doc_drift.py \
  tests/test_security_doc_rate_limits.py tests/test_threat_model_doc_drift.py \
  tests/test_backlog_status_check.py tests/test_sds_rule_ids_are_stable.py

Result: 175 passed, 89 skipped. All 89 skips are tests/test_threat_model_doc_drift.py, which asserts against docs/security/THREAT-MODEL.md - vault-only and absent from this tree by design (the ADR 0156 class that ci.yml already documents). No skip here is masking a failure in the guards that can see this tree.

Branch is current with main at time of push (git merge origin/main reported already up to date), so the diff above is the whole change.

Provenance

Authored by the session in worktree sweet-dhawan-57cbc8 (claude/citation-link-rot-4808fe). Pushed and opened by the coordinator session under an explicit owner grant covering external repo actions; the commits are unmodified from that session's tips, anchored by tag backup/1095-docs-ee9f10b4.

A `BACKLOG.md:<line>` anchor cannot survive an actively edited ledger. The file
moved 6,318 -> 6,616 lines during this work alone, and of 43 anchors repo-wide,
every one examined had drifted onto unrelated text. Five that were past EOF a day
earlier are now IN range and land on plausible-looking wrong items, which is worse
than dangling. Item numbers survive both editing and archival, so that is what
these become.

Root cause, and it is not gradual drift: commit 4ea1501 (the master test plan) is
NOT a descendant of 03f1fbd (the 185-item archival). They were parallel branches.
The plan's anchors were authored against an 8,742-line ledger and landed beside a
commit that cut it to 3,858, so they were stale on arrival, not over time.

Recovery was per-site reading with the citing prose as the primary evidence -- it
usually names the item inline -- backed by resolving each anchor against the
pre-archival ledger at 03f1fbd^. Every mapping was then put to an adversarial
reviewer instructed to refute by default. 32 of 43 survived; the 11 refusals were
not noise and are deliberately NOT converted here:

- Eight sites where the citing CLAIM is dead, not just the pointer. The alerting
  chapter still asserts #139 and PHI.md "currently contradict each other" when
  #139 was corrected 2026-08-01 and the code fixed by #323 on 2026-08-02; the
  ranked-backlog row for #338 is a pre-shipping snapshot. Repointing those would
  relaunder a false present-tense claim as a fresh, durable-looking reference.
  They need a content fix, which is a different change.
- Six anchors that cannot be attributed to any item, including one pointing at an
  un-numbered narrative bullet. Left uniform rather than half-repaired: uniform
  staleness is at least detectable, and a confident wrong pointer is not.
- Three that are not citations at all -- two inside a fenced transcript in #1083
  reproducing scanner output, and #347's own Source paragraph narrating the
  falsification test that made the status checker fail on purpose.

One mapping was recovered after the review: 16-security-phi:891's "no dormancy
contingency" quotes #89's title ("hl7apy security hardening -- dormant-upstream
contingency") almost verbatim. One proposal was refuted only on fragment
uniqueness, not on the item; applying line-scoped rather than globally makes it
safe, which is how all of these were applied.
The repo's file_path:line_number citation convention had leaked inside the href:
[`pipeline/alerts.py:27`](messagefoundry/pipeline/alerts.py:27). No prefix makes
that resolve. Owner's ruling was to move the line out of the target and keep it in
the link text, where it already sits -- no second convention, and nothing lost.

These carry a second defect that hid behind the first. The targets were also
root-relative, the class #280 repaired across 333 hrefs in this same subtree; they
were skipped there because link_check.py deliberately skips ":<line>" targets, so
each defect concealed the other. Both are fixed here: the line comes out and the
../../../ prefix goes in, matching the form #280 established in these files.

All 23 targets were confirmed to exist before rewriting. Repo-wide there are now
zero file:line hrefs, so --include-line-cites reports nothing new -- the checker's
skip is now a safety net rather than a suppression.
#1095 recorded four measured counts and three of them were wrong -- in the way the
item itself is about. Its href figure of 13 was low by two orders of magnitude
against at least 629 repaired sites. Its two anchor counts moved between filing and
repair, and not because anything improved: this file grew 6,318 -> 6,616 lines, so
five anchors that were safely past EOF came back INTO range and now land on
plausible-looking wrong items. A dangling pointer degrades into a confident one as
the file grows, which is the sharpest argument for the item-number convention.

Root cause of the anchor class recorded, because it is not gradual drift: commit
4ea1501 (the master test plan) is not a descendant of 03f1fbd (the 185-item
archival). Parallel branches, so anchors written against an 8,742-line ledger
landed beside a commit that cut it to 3,858 -- stale on arrival. It also explains
the anchor citing line 8429, absurd against 6,616 and ordinary against 8,742.

The markdown-only scope note is discharged rather than left open. The predicted
sweep of harness/, ide/src/, messagefoundry/, scripts/, tests/, packaging/ and
.github/workflows/ ran over 1,219 files: 68 nonexistent paths, none of them a
rotted citation -- test fixtures, withheld directories, and past-tense historical
comments. The prediction was wrong and the reason is kept: citations in code are
written about the past, prose citations are written as pointers.

Three items filed, numbers allocated with alloc.ps1:

#1098 -- the coordination hook prints a session UUID in the column a commit SHA
occupies. Small, but it is an instrument answering a different question from the
one its header asks, with nothing in the output saying so.

#1099 -- #1094 says "the archival pass generates the anchor". There is no archival
tooling at all; the move is manual. The sentence sits in the archive as settled
record and points maintenance at a generator that was never built.

#1100 -- the nine sites where the citing CLAIM is dead, not just the pointer,
split out of #1095 rather than repointed. The alerting chapter still sets exit
criteria requiring that #139 and PHI.md be made to agree when #139 was corrected
2026-08-01 and the code fixed by #323 on 2026-08-02. Converting those pointers
would attach a durable reference to a false claim, which is worse than leaving the
stale anchor visible. Found by the adversarial pass: every one was proposed as a
clean repoint by a first reader and refuted by a second who checked the claim
against the code rather than against the anchor.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 7, 2026 23:10
@wshallwshall
wshallwshall merged commit 131acae into main Aug 7, 2026
33 checks passed
@wshallwshall
wshallwshall deleted the claude/citation-link-rot-4808fe branch August 7, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant