Configuration¶
pyproject.toml¶
[tool.menard]
require_links = ["src/**/*.py"] # Code files that must have doc links
doc_paths = ["docs/**/*.md"] # Where to look for documentation
exclude_docs = ["**/adr/**"] # Docs to exclude from bootstrap suggestions
transitive_depth = 1 # Import chain depth for staleness detection
brevity_exclude = ["*#License"] # Sections to exclude from duplicate detection
The require_links glob determines which code files need documentation. Files matching this pattern without a link in links.toml will show up in coverage reports.
The doc_paths patterns specify where menard looks for documentation files.
The exclude_docs patterns prevent menard bootstrap from suggesting links to certain docs. This is useful for excluding static/historical documents like ADRs (Architectural Decision Records) or plans that record past decisions rather than living documentation that should track code changes.
Note: exclude_docs only affects bootstrap suggestions. You can still manually link to excluded docs in links.toml, and staleness checks will run normally. The distinction is intentional: ADRs might be legitimately linked to architecture code, but shouldn't be auto-suggested since they're historical records.
Set transitive_depth to control how deep menard follows imports when detecting staleness. If src/auth.py imports src/crypto.py, and crypto changes, should auth docs be marked stale? With depth 1, yes. With depth 0, no.
The brevity_exclude patterns filter results from menard brevity (duplicate detection). Use this for intentional duplicates like License sections mirrored across files, or entire files like CLAUDE.md that you don't want flagged. Patterns can match files (CLAUDE.md), sections (*#License), or both (README.md#Quick Start).
links.toml¶
Define code→doc relationships in .menard/links.toml:
# Single file → single doc
[[link]]
code = "src/auth.py"
docs = ["docs/api.md"]
# Section-level precision (recommended)
[[link]]
code = "src/auth.py"
docs = ["docs/api.md#Authentication"]
# One code file → multiple doc sections
[[link]]
code = "src/models/user.py"
docs = [
"docs/models.md#User Model",
"docs/api.md#User Endpoints",
]
# Glob patterns
[[link]]
code = "src/models/*.py"
docs = ["docs/models.md"]
# Auto-generated docs (skip staleness checks)
[[link]]
code = "src/cli.py"
docs = ["docs/reference/cli.md"]
auto_generated = true
# Permanently ignore staleness (e.g., intentionally outdated)
[[link]]
code = "src/legacy.py"
docs = ["docs/legacy.md"]
ignore = true
The ignore flag permanently skips staleness checks for a link. Unlike auto_generated, this is for cases where you've deliberately decided not to keep docs in sync. Use menard fix-ignore to add this flag interactively.
Section-level links are more precise. When you change src/auth.py, only the Authentication section needs updating, not the entire 500-line doc file.
menard parses doc files to find section headings, extracts line ranges, then uses git diff to check if those specific lines changed. If the section's lines didn't change since the code changed, it's stale.
Auto-generated Documentation¶
Use auto_generated = true to skip staleness checks for documentation that's automatically regenerated by tools like mkdocs-click, sphinx-autodoc, or rustdoc. These docs stay synchronized with code by definition, so staleness checks are unnecessary.
[[link]]
code = "src/cli.py"
docs = ["docs/reference/cli.md"]
auto_generated = true # Skip staleness checks
Important: Validation still runs for auto-generated links. If docs/reference/cli.md doesn't exist, menard will report an error. Only staleness checks are skipped.
When auto-generated docs are skipped, menard reports the count:
JSON output includes skipped_auto_generated count for programmatic access.
Staleness Detection¶
menard uses git diff analysis to detect stale docs, combined with AST parsing to identify specific symbol changes (functions, classes added/removed). It checks in two places:
# 1. Check staged content first (same commit workflow)
git diff --cached -- docs/api.md
# 2. Fall back to git history
git log -1 --format=%H -- src/auth.py
git diff <commit> HEAD -- docs/api.md
When you stage both code and docs together, menard detects the staged changes and passes. If docs aren't staged, it compares git history: if the diff shows changes overlapping with the section's line range, the doc was updated. If no overlap, it's stale.
This works transitively too. If src/auth.py imports src/crypto.py, and crypto changes, auth docs can be marked stale (controlled by transitive_depth).
Enriched Output¶
When docs are stale, menard shows detailed information to help you understand what changed:
- Dates: When code and docs were last updated (
code_last_modified,doc_last_modified) - Commits: Recent commits that modified the code since docs were updated
- Symbol changes: Functions and classes added or removed (via AST analysis)
- Code diff: The actual git diff (with
--show-diffflag) - Line range: Exact lines to edit in the doc file
- Suggested action:
updateorcreate
$ menard list-stale
docs/api.md#Authentication
Code: src/auth.py
Last code change: 2026-03-17 (abc1234)
Last doc update: 2026-03-10
Commits since doc updated:
abc1234 (2026-03-17) feat: add MFA support
Changed: +2 symbols, -1 symbol
Added: mfa_verify, mfa_setup
Removed: legacy_auth
The JSON output includes all this metadata in a structured format for machine consumption:
{
"stale": [{
"code_file": "src/auth.py",
"doc_target": {
"file": "docs/api.md",
"section": "Authentication",
"line_range": [45, 89]
},
"code_last_modified": "2026-03-17",
"doc_last_modified": "2026-03-10",
"commits_since": [{
"sha": "abc1234",
"date": "2026-03-17",
"message": "feat: add MFA support"
}],
"symbols_added": ["mfa_verify", "mfa_setup"],
"symbols_removed": ["legacy_auth"],
"severity": null,
"auto_generated": false,
"suggested_action": "update",
"reason": "Section unchanged since src/auth.py changed"
}]
}
The structured doc_target with line_range enables AI agents to make precise, scoped edits.
Symbol extraction results are cached in .menard/symbols_cache.json for performance. The cache uses content-based hashing, so identical file contents always return cached results. Old entries are automatically evicted to keep the cache under 500 entries.
donttouch¶
Protect critical content from staleness checks in .menard/donttouch:
# Section protection - never mark these as stale
README.md#License
docs/contributing.md#Code of Conduct
CLAUDE.md#Terminology (ENFORCED)
# Literal protection - warn if these strings change
"Python 3.10+"
"Apache-2.0"
"#7730E1"
License text, brand colors, version requirements, and policies shouldn't be flagged just because related code changed. Protected sections are skipped during staleness checks. Protected literals trigger warnings if modified.
Shows all protected sections and literals.
Bootstrap¶
Auto-generate link suggestions:
This uses filename matching (src/auth.py → docs/auth.md), content analysis (grep docs for code file references), and import graphs to propose links. Review suggestions carefully—bootstrap can't reliably infer links for human-facing docs like tutorials.
Use exclude_docs in your config to prevent bootstrap from suggesting links to static documents:
[tool.menard]
doc_paths = ["docs/**/*.md"]
exclude_docs = ["**/adr/**", "**/plans/**", "**/decisions/**"]
Validation and Coverage¶
menard validate-links # Check all link targets exist
menard coverage # Show documentation coverage percentage
Validation catches typos and missing sections. Coverage shows what percentage of your require_links files have documentation.
Commands¶
menard check # Pre-commit: validate links + check staged files for stale docs
menard check --all # Manual audit: check ALL tracked files (not just staged)
menard list-stale # Audit: list ALL stale docs across entire repo
menard fix # Interactive mode: review/update/mark/ignore stale docs
menard fix-mark-reviewed --code FILE --doc TARGET # Mark as reviewed (ephemeral)
menard fix-ignore --code FILE --doc TARGET # Permanently ignore link
menard clean-reviewed # Remove orphaned review records
The check command runs during pre-commit and performs two validations:
1. Link validation - ensures all links in links.toml point to existing files/sections
2. Staleness detection - checks if staged code changes require doc updates
Both check and list-stale support --format json for machine consumption. The JSON output includes file paths, section names, line ranges, and git diffs—everything an AI agent needs to make scoped updates.