# CI documentation rules Automated checks for markdown in this repository. Enforced on every **pull request** and on **push to `main`** (including docs-only pushes that skip the simulator gate). Implementation: [`bin/check_docs.py`](../../bin/check_docs.py) reads [`bin/docs_rules.yaml`](../../bin/docs_rules.yaml). Workflow: [`.github/workflows/release.yaml`](../../.github/workflows/release.yaml) job **`Verify documentation`**. Tracked from [issue #1](https://git.produktor.io/eSlider/swarm-house/issues/1) and the [2026-07-10 audit](2026-07-10-audit.md). ## Gates | Rule | Severity | What it checks | | --- | --- | --- | | Relative links | error | Every `[text](path)` and `[text](path#anchor)` to a local `.md` file resolves to an existing file | | Fragment anchors | error | `#anchor` fragments match a `##` or `###` heading in the target file (GitHub-style slug) | | Banned targets | error | Known-bad paths (e.g. deleted `03-storage-design.md`) are not linked | | Glossary fragments | error | All `00-glossary.md#…` links use anchors that exist in the glossary | | Section vs term links | warning | Links to glossary *section* headers (`#swarm--robotics`, …) are listed; prefer `###` term anchors where they exist | | Bare cross-refs | error | Prose cross-references to numbered docs without a proper markdown link | | ADR immutability | error | No modifications to existing `docs/adr/ADR-*.md` (compare `origin/main`); add new ADR to supersede | Warnings are printed in the job log but do not fail CI. Errors exit non-zero and block merge. ## PR workflow ```mermaid flowchart LR PR[Pull request] --> DV[docs-verify] PR --> V[verify simulator] V --> S[Trivy scan] DV --> OK{All required green?} V --> OK S --> OK OK -->|yes| Merge[Mergeable] ``` | Event | `docs-verify` | `verify` + `scan` | | --- | --- | --- | | PR → `main` | always | always | | Push → `main` (code paths) | always | runs | | Push → `main` (docs/README/AGENTS only) | always | skipped (`paths-ignore`) | Docs-only changes therefore still get link and glossary validation on `main`; they do not trigger a semantic release. ## Local run ```bash python bin/check_docs.py python bin/check_adrs.py --base origin/main python bin/check_docs.py --warnings-as-errors # treat section-link warnings as failures ``` ## Changing rules 1. Edit `bin/docs_rules.yaml` (paths, banned targets, preferred anchors). 2. Adjust `bin/check_docs.py` only when adding a new rule *type*. 3. Add a row to the table above and mention it in the audit/improvement record if the change is significant.