feat(gate): resolve every cited CLAUDE.md section number, and list the four missing subpackages - #356
Merged
Merged
Conversation
The repository-layout tree listed 10 subpackages while messagefoundry/ has 14. security/, support/, verify/ and tray/ were absent, so CLAUDE.md contained ZERO occurrences of "tray" despite ADR 0113 and a 17-module package shipping at HEAD. Cause: the tray, verify and support packages landed in the private lineage between 2026-07-13 and 07-25, and that content never reached the repo of record at publication -- five commits dropped by omission, absent from HEAD. The tree has been stale since. Descriptions taken from each package's own __init__ docstring. The tray line records what it wraps, verified rather than assumed: tray/ imports only messagefoundry.service, messagefoundry.service_status and its own submodules -- no api/, no engine internals -- so section 4's dependency rule needs no carve-out for it. Insertion-only, no section renumbered. That is deliberate: 281 tracked files cite this file by section number, nothing validates a section number (link_check.py:21 validates the path and skips the fragment), and the same rot has already landed once -- test_sds_rule_ids_are_stable.py:7-11 records four security citations that still resolve to the wrong section today. Verified: tests/test_link_resolution.py and tests/test_sds_rule_ids_are_stable.py, 34 passed. The second was confirmed to read this worktree's file by injecting a bogus SDS-9.9 citation and watching it go red, then removing it.
…nested anchor escaping the SDS gate
CLAUDE.md's numbered sections are a de facto API and NOTHING validated a
section number. Measured: 281 tracked files name the file, 646 citations name
one of its sections, and tests/test_dependency_boundaries.py cites section 4
in its own docstring, so this reaches code and not only prose.
Every existing gate stays green through a renumber. link_check.py:21 says so
in its own header -- it resolves the PATH and skips the #fragment -- and the
two doc guards that already read CLAUDE.md check other things: one checks
SDS-N.N identifiers, one checks link paths. Path resolves, identifiers
resolve, only the meaning moves. The checkable half stays green.
The rot is not hypothetical. The sibling guard
tests/test_sds_rule_ids_are_stable.py:5-11 records it landing on the Secure
Development Standards -- a new section 5 pushed 5-9 to 6-10, and citations
still resolve to the wrong section today. The instance was fixed and the
class left open. This closes it for the anchor.
MEASURED BLAST RADIUS: renumbering section 11 alone breaks 61 citations.
scripts/docs/claude_section_check.py, stdlib only, beside its siblings:
- parses the anchor's own `## N.` headings as the source of truth
- scans tracked .md/.py/.ps1/.yml via git ls-files
- requires the anchor's NAME on the same line within a bounded window, so a
section number belonging to another document is never attributed to it --
false positives are how a gate gets disabled
- ALWAYS prints what it scanned; a finding count alone cannot distinguish a
clean scan from one that examined nothing
- REFUSES to run when zero headings parse or the anchor is missing, rather
than reporting every citation broken or the corpus clean
ADDED TO DOC_GUARDS in ci.yml. Without that it would be born unable to fire:
a markdown-only PR is classified non-code and runs only that list, so a check
landed outside it goes green by absence on exactly the PR shape it exists to
catch. That enumeration hole has already been fixed twice for other paths
(BACKLOG #327, #1200) and left open both times; ci.yml:1050 documents its own
recurrence.
Also widens _citation_sources() in the SDS guard to include nested CLAUDE.md
files. Root is a literal path and the globs are rooted at docs/,
messagefoundry/ and scripts/, so a subpackage anchor -- which CLAUDE.md
section 3 invites -- would take its SDS citations out of coverage silently.
Resolved through git ls-files, not rglob, because this repository nests
worktrees under .claude/worktrees/ and an rglob would scan sibling checkouts.
There are no nested anchors today; this is the prerequisite, not a response.
Verified. Both guards were made to FAIL ON PURPOSE and the injected defect
confirmed to land:
- renumber section 11 to 13 -> rc=1, 61 citations; revert -> rc=0
- nested CLAUDE.md citing SDS-9.9 -> SDS guard red; remove -> green
- 8 self-bite tests, including both refuse-to-run guards and a
false-positive case (another document's section 9 is not claimed)
- ruff check, ruff format, mypy strict clean
- full DOC_GUARDS set, 15 modules: 219 passed, 89 skipped
wshallwshall
enabled auto-merge (squash)
August 13, 2026 00:21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #355, which carried the same commits on a branch whose name referenced an unrelated research topic. Identical content -- the trees match at
aa4933a395a8. Nothing about this change derives from that research; only the branch name did, and it is gone.What it closes
CLAUDE.md's numbered sections are a de facto API -- 653 citations across 260 tracked files -- and nothing validated a section number.link_check.pyresolves the PATH and skips the fragment; the doc guards that read CLAUDE.md check SDS identifiers and link paths. A renumber landed entirely green.5 files, +366/-2.
CLAUDE.mdis insertion-only, no section renumbered. Its 4 added lines listsecurity/,support/,verify/andtray/-- subpackages that exist in the tree and were missing from the section 3 listing.ci.ymladds the new module to the existingDOC_GUARDSlist, besidetest_link_resolution.pyandtest_dast_claims.py.Verified here, not accepted on report
Reproduced against a tree including
main's newest CLAUDE.md change:The guard goes red on the defect, names the citers, and leaves no residue -- it is not green because it checks nothing.
A caution about that measurement, recorded because it nearly fooled me: my first attempt piped the guard through
tailand read$?, capturing tail's exit code and reportingrc=0on a run that had plainly failed. Re-measured with no pipe. That is SDS-3.8, and this PR's own subject -- a check that silently does not check -- is the same family one level up.Why it belongs in the repo
It is the same category as the doc guards already here, and it is a contributor-facing control rather than a runtime one: it protects the accuracy of the file every session loads as context. The 15th module on a list whose 14th caught a broken link on a PR earlier today, while a guard that was not on that list reached
main-- same blind mode, two outcomes, decided purely by list membership.Provenance
Authored by another session and routed here for push and PR. Verified before pushing: commits present,
5 files +366/-2exactly as stated, stillmerge-tree rc=0against currentmain, and a leak scan of all 366 added lines clean with a negative control.Unblocks a third session: the ASVS tracking lane is recording its section-citation exposure as closed pending merge of
e7e825edrather than closed -- correctly, since a control on an unpushed branch is not a shipped control.