feat(ci): cross-check the documents against the configs - #155
Merged
Conversation
Documentation was the last defect class here with no check. Every other one was answered by moving truth somewhere CI can verify it — provisioned dashboards, digest pinning, promtool unit tests after a rule sat unfireable for months, a real Loki boot for the LogQL rules. Prose was still verified by someone noticing. scripts/check_docs.py makes six assertions, following the pattern scripts/snmp-targets.sh --check already established for the SNMP inventory: 1. Counted claims rules, dashboards and panels quoted in prose 2. SNMP targets snmp.yaml <-> docs/network.md 3. Host and stack table docs/architecture.md <-> docs/network.md, stacks/ 4. Ports table docs/architecture.md <-> compose.yaml 5. Compute table docs/hardware.md <-> docs/network.md 6. Image versions prose <-> compose.yaml On first run it found seven live disagreements, all in stacks/observability/README.md: the stale "34 alert rules" of #72, and all six stale image versions of #73, whose title names the cause — "CI does not check Markdown". Both are fixed here. Scope is a fixed list of present-tense documents. roadmap.md and adr/ record what was true when the work landed — roadmap.md still says "(34 rules)" in a Done entry and that is correct as written. Failing on those would need an ignore list, and this repository already learned where that leads with the .gitleaksignore deleted in the history purge. Two definitions are pinned in the checker rather than left implicit: a panel count includes rows (84 with, 73 without), and profile-gated services are excluded from the ports check, so `renderer` behind the capture profile does not make a correct document fail. Each assertion was mutation-tested: a deliberate error in each of the six inputs produces a non-zero exit and a message naming the file. Also wires scripts/snmp-targets.sh --check into CI. It ran in `make validate` and nowhere else — the only check with that asymmetry, so the SNMP inventory had never been verified on a pull request. That is #68, and adding a documentation check to one side only would have repeated it. The two script lists are now identical. Refs #154, #72, #73, #68 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1QX12EJLwMfRAWsbpfRJz
This was referenced Aug 26, 2026
6 tasks
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.
Closes #154. Fixes #73, and closes the mechanism half of #72 and #68.
What changed
scripts/check_docs.pyasserts the prose still agrees with the configs it describes, and runs in bothmake validateand CI. Nothing about the running lab changes — this is a build-time check plus the seven stale strings it found.Six assertions, following the pattern
scripts/snmp-targets.sh --checkalready established for the SNMP inventory:snmp.yaml↔docs/network.mddocs/architecture.md↔docs/network.md, and everystacks/*/is nameddocs/architecture.md↔compose.yamldocs/hardware.md↔docs/network.mdcompose.yamlIt found seven live disagreements on the first run, all in
stacks/observability/README.md— the stale34 alert rulesfrom #72, and all six stale image versions from #73, whose title already named the cause: "and CI does not check Markdown". Fixed in this PR, which is why it is green.It also wires
scripts/snmp-targets.sh --checkinto CI. Comparing the scripts each side invokes, that was the only checkmake validatehad and CI did not — so the SNMP inventory had never been verified on a pull request. That is #68 in the flesh, and adding a documentation check to one side only would have repeated it. The two lists are now identical:Why
Documentation drift is this repository's most frequently-recurring defect and the only class that had no check.
oraclewas recorded as an i5-1235U with 32 GB when it is a dual-core A6-9200 with 4 GB — ADR-0008 notes that wrong entry was load-bearing in planning.shivawas described as the hypervisor for several revisions.roadmap.mdsays of itself that it "has already been wrong about the switch answering SNMP and about the history purge". #104 records a rule count that disagrees with reality.Every other defect class here was answered by moving truth somewhere CI can check it. This does that for
docs/.Two decisions worth reviewing
Scope is a fixed file list, not an ignore list.
docs/roadmap.mdanddocs/adr/are excluded because they record what was true when the work landed —roadmap.mdstill says "(34 rules)" in a Done entry and that is correct as written. Failing on those would need a suppression mechanism, and this repository already learned where that leads with the.gitleaksignoredeleted in the history purge: "an acknowledgement, not a fix… a CI job that is permanently red for a known reason gets ignored."Two definitions are pinned in the checker rather than left implicit. A panel count includes rows (84 with, 73 without), matching the number already in the documents. And profile-gated services are excluded from the ports check, so
rendererbehind thecaptureprofile does not make a correct document fail — the running stack is six services and the docs are right to say so.Known consequence
Assertion 6 means a Dependabot image bump will now fail CI until
stacks/observability/README.mdis updated in the same PR. That is the intended reading of #73, but the alternative is worth naming:ci.ymlalready says "Image versions are NOT duplicated here… hardcoded copies went stale silently", and the stricter fix would be to drop version numbers from that table entirely and letcompose.yamlbe the only place they appear. Happy to switch if you prefer that reading.Blast radius
secrets/*.sops.yaml— the checker reads tracked files only and never needs a decryption keyBuild-time only. Nothing is deployed, no container is added, no service changes.
Verification
Each assertion was mutation-tested, because a check that passes and cannot fail is what #63 was. A deliberate error in each of the six inputs, one at a time:
48→49 alert rulesinREADME.mdREADME.md:48 claims 49 alert rules; the repository has 35 or 48mjolniraddress changed innetwork.mdsnmp.yaml polls mjolnir at 10.0.99.10 on VLAN 99, and docs/network.md does not list that host at that addressoracleaddress changed inarchitecture.mddocs/architecture.md places oracle at 10.0.99.31 on VLAN 99; docs/network.md does not list it there3100→3200in the ports tabledocs/architecture.md says loki publishes 3200; compose.yaml publishes 3100oracleOS changed inhardware.mddocs/hardware.md says oracle runs 'Ubuntu Server 22.04.1'; docs/network.md says ubuntu 24.04.3grafana/lokiversion reverted in the stack READMEstacks/observability/README.md:13 says grafana/loki:3.3.2; compose.yaml pins 3.7.6All six exit non-zero and name the file and line. Clean tree passes:
make validatepasses — not run in full, and saying so rather than implying otherwise. No docker daemon, promtool, amtool, alloy or gitleaks available here, so the compose, Prometheus, Alertmanager, Alloy and secret-scan sections would have skipped, and this repository is right that skipped checks prove nothing. What did run:check_docs.py,check_dashboards.py,check_compose_health.py,snmp-targets.sh --check,markdownlint-cli2,yamllint --strict,bash -n scripts/validate.sh— all clean. CI is the authority on the rest.stacks/observability/README.mdcorrected, andREADME.mdgains a Highlights bullet for the new check🤖 Generated with Claude Code
https://claude.ai/code/session_01W1QX12EJLwMfRAWsbpfRJz
Generated by Claude Code