Enforce Documentation Hierarchy
Scope: $ARGUMENTS
Audits documentation against the project's declared hierarchy, then aligns
violations with user approval.
Phase 1: Discover
The authoritative hierarchy lives where the
qte77 doc-structure canon
(SoT) places it: CONTRIBUTING.md's ## Documentation hierarchy statement. The canon's model is
README = the project contract, CONTRIBUTING = conventions + the hierarchy statement,
AGENTS = behavioural rules, CLAUDE = a one-line @AGENTS.md pointer (never a copy).
Read the project's declaration, looking in order:
CONTRIBUTING.md — ## Documentation hierarchy section (canonical location; table or list)
AGENTS.md — "Key references" or "Information sources" section
README.md — "Documentation" / "Refs" section (links to authoritative docs)
Extract:
- Entry points: which docs are human vs agent entry points
- Authority map: which doc owns which content type
- Anti-redundancy rule: stated or implied (default: no duplication across docs)
If no hierarchy is declared, report that as the first finding and stop. Treat the canon model above
as the expected baseline and flag deviations from it.
Phase 2: Audit
Detect violations across the scope. For each finding, record:
| Source File |
Line |
Type |
Description |
| path |
Lnn |
type |
what's wrong |
Violation Types
- broken-link: Reference target does not exist (moved, renamed, deleted, wrong case)
- duplicate: Same content (3+ lines) appears in both an authority doc and a dependent doc
- misplaced: Content is in the wrong doc per the discovered authority map, OR a doc in the hierarchy is not referenced by its parent
- lint-compat: HTML comments before frontmatter
- config-drift: Inline
<!-- markdownlint-disable/enable --> for a rule already disabled in .markdownlint.json. These are dead code that causes false positives when the enable directive re-activates a globally-disabled rule
- unused-link-def:
[key]: url link definition with no corresponding [text][key] or [key] reference in the file
Audit Procedure
Determine scope from $ARGUMENTS:
- File: audit that file's outbound references and content placement
- Directory: audit all
.md files in that directory
full or empty: audit every .md file in the repo
Check links: For each [text](path) and @file reference, verify the
target exists. Check case sensitivity.
Check duplicates: For each authority doc, search dependent docs for
substantial repeated content (3+ lines or identical tables).
Check placement: For each doc, verify its content matches its declared
authority. Flag content that belongs in a different doc per the authority map.
Check chain: Verify each doc in the hierarchy is referenced by at least
one parent doc. Flag orphaned docs.
Run markdownlint (preferred) or manual lint check (fallback):
a) If markdownlint-cli is available (npx markdownlint-cli --version
succeeds): run it with the project config and parse output:
npx markdownlint-cli -c .markdownlint.json <scope> 2>&1
Map results to violation types: MD012 → double blanks (fix during align),
MD053 → unused-link-def, MD022/MD058 → spacing issues from prior
directive removal. Report rule ID + line + message.
b) Fallback (no markdownlint available): read .markdownlint.json
(or .markdownlintrc, .markdownlint.yaml) to get globally-disabled
rules, then manually:
- Flag files with HTML comments before frontmatter on line 1 (
lint-compat)
- Flag inline
<!-- markdownlint-disable/enable MDXXX --> where MDXXX is
already disabled in the config file (config-drift)
- Grep for
[key]: url definitions with no corresponding reference
(unused-link-def)
In both paths: read the lint config to detect config-drift — even
markdownlint doesn't flag dead inline directives for globally-disabled
rules. See frontmatter-convention.md rule for the required config
template.
Output findings table sorted by type, then file.
Phase 3: Align
Resolve findings with user confirmation. Propose each fix and wait for approval.
| Violation |
Fix |
| broken-link |
Update path. If target deleted, remove reference. |
| duplicate |
Keep in authority doc, replace in dependent doc with reference link. |
| misplaced |
Move content to authority doc, replace original with reference link. |
| lint-compat |
Remove HTML comments before frontmatter. |
| config-drift |
Delete the inline directive — the rule is already handled by .markdownlint.json. Do NOT collapse surrounding blank lines (they may be required spacing around headings/tables). |
| unused-link-def |
Add inline reference, or remove the definition if it serves no purpose. |
Rules
- Fix the authority doc first, then fix dependents
- Never duplicate — replace with a reference
- Confirm each fix before applying
- Keep edits minimal
- Read
.markdownlint.json before adding any inline lint directives — if a
rule is globally disabled, inline toggles are dead code that causes false
positives when the enable half re-activates the rule
- When removing inline directives, delete only the directive line — do NOT
collapse surrounding blank lines (use Edit tool, not greedy
sed)
- Commit content changes before repo-wide lint cleanup — never mix content
additions with formatting passes in the same uncommitted state
References
rules/frontmatter-convention.md — required .markdownlint.json config
template and anti-patterns for inline directives
1---2name: enforcing-doc-hierarchy3description: Audit documentation against its declared hierarchy — broken links, duplicates, misplaced content, stale references, single-source-of-truth enforcement. Use for doc health reviews.4---56# Enforce Documentation Hierarchy78**Scope**: $ARGUMENTS910Audits documentation against the project's declared hierarchy, then aligns11violations with user approval.1213## Phase 1: Discover1415The authoritative hierarchy lives where the16[qte77 doc-structure canon](https://github.com/qte77/qte77/blob/main/docs/doc-structure.md)17(SoT) places it: `CONTRIBUTING.md`'s `## Documentation hierarchy` statement. The canon's model is18`README` = the project contract, `CONTRIBUTING` = conventions + the hierarchy statement,19`AGENTS` = behavioural rules, `CLAUDE` = a one-line `@AGENTS.md` pointer (never a copy).2021Read the project's declaration, looking in order:22231. `CONTRIBUTING.md` — `## Documentation hierarchy` section (canonical location; table or list)242. `AGENTS.md` — "Key references" or "Information sources" section253. `README.md` — "Documentation" / "Refs" section (links to authoritative docs)2627Extract:2829- **Entry points**: which docs are human vs agent entry points30- **Authority map**: which doc owns which content type31- **Anti-redundancy rule**: stated or implied (default: no duplication across docs)3233If no hierarchy is declared, report that as the first finding and stop. Treat the canon model above34as the expected baseline and flag deviations from it.3536## Phase 2: Audit3738Detect violations across the scope. For each finding, record:3940| Source File | Line | Type | Description |41|-------------|------|------|-------------|42| path | Lnn | type | what's wrong |4344### Violation Types4546- **broken-link**: Reference target does not exist (moved, renamed, deleted, wrong case)47- **duplicate**: Same content (3+ lines) appears in both an authority doc and a dependent doc48- **misplaced**: Content is in the wrong doc per the discovered authority map, OR a doc in the hierarchy is not referenced by its parent49- **lint-compat**: HTML comments before frontmatter50- **config-drift**: Inline `<!-- markdownlint-disable/enable -->` for a rule already disabled in `.markdownlint.json`. These are dead code that causes false positives when the enable directive re-activates a globally-disabled rule51- **unused-link-def**: `[key]: url` link definition with no corresponding `[text][key]` or `[key]` reference in the file5253### Audit Procedure54551. **Determine scope** from `$ARGUMENTS`:56 - File: audit that file's outbound references and content placement57 - Directory: audit all `.md` files in that directory58 - `full` or empty: audit every `.md` file in the repo59602. **Check links**: For each `[text](path)` and `@file` reference, verify the61 target exists. Check case sensitivity.62633. **Check duplicates**: For each authority doc, search dependent docs for64 substantial repeated content (3+ lines or identical tables).65664. **Check placement**: For each doc, verify its content matches its declared67 authority. Flag content that belongs in a different doc per the authority map.68695. **Check chain**: Verify each doc in the hierarchy is referenced by at least70 one parent doc. Flag orphaned docs.71726. **Run markdownlint** (preferred) or manual lint check (fallback):7374 **a) If `markdownlint-cli` is available** (`npx markdownlint-cli --version`75 succeeds): run it with the project config and parse output:76 ```77 npx markdownlint-cli -c .markdownlint.json <scope> 2>&178 ```79 Map results to violation types: MD012 → double blanks (fix during align),80 MD053 → `unused-link-def`, MD022/MD058 → spacing issues from prior81 directive removal. Report rule ID + line + message.8283 **b) Fallback** (no markdownlint available): read `.markdownlint.json`84 (or `.markdownlintrc`, `.markdownlint.yaml`) to get globally-disabled85 rules, then manually:86 - Flag files with HTML comments before frontmatter on line 1 (`lint-compat`)87 - Flag inline `<!-- markdownlint-disable/enable MDXXX -->` where MDXXX is88 already disabled in the config file (`config-drift`)89 - Grep for `[key]: url` definitions with no corresponding reference90 (`unused-link-def`)9192 **In both paths**: read the lint config to detect `config-drift` — even93 markdownlint doesn't flag dead inline directives for globally-disabled94 rules. See `frontmatter-convention.md` rule for the required config95 template.96977. **Output findings table** sorted by type, then file.9899## Phase 3: Align100101Resolve findings with user confirmation. Propose each fix and wait for approval.102103| Violation | Fix |104|-----------|-----|105| **broken-link** | Update path. If target deleted, remove reference. |106| **duplicate** | Keep in authority doc, replace in dependent doc with reference link. |107| **misplaced** | Move content to authority doc, replace original with reference link. |108| **lint-compat** | Remove HTML comments before frontmatter. |109| **config-drift** | Delete the inline directive — the rule is already handled by `.markdownlint.json`. Do NOT collapse surrounding blank lines (they may be required spacing around headings/tables). |110| **unused-link-def** | Add inline reference, or remove the definition if it serves no purpose. |111112### Rules113114- Fix the **authority doc first**, then fix dependents115- Never duplicate — replace with a reference116- Confirm each fix before applying117- Keep edits minimal118- **Read `.markdownlint.json` before adding any inline lint directives** — if a119 rule is globally disabled, inline toggles are dead code that causes false120 positives when the enable half re-activates the rule121- **When removing inline directives, delete only the directive line** — do NOT122 collapse surrounding blank lines (use Edit tool, not greedy `sed`)123- **Commit content changes before repo-wide lint cleanup** — never mix content124 additions with formatting passes in the same uncommitted state125126### References127128- `rules/frontmatter-convention.md` — required `.markdownlint.json` config129 template and anti-patterns for inline directives