ADR 0005: Auto-Generated Documentation
July 12, 2026 · View on GitHub
Status: Accepted
Authors: Provenant team
Supersedes: None
Current contract owner:
../DOCUMENTATION_INDEX.md,../HOW_TO_ADD_A_PARSER.md, and thextaskgenerator own the live documentation workflow. This ADR records the decision to keep a generated support matrix instead of maintaining that coverage manually.
Context
With 40+ package ecosystems and 136+ file formats to document, maintaining accurate parser documentation is a significant challenge:
- Scale Problem: Manual documentation for dozens of parsers is error-prone
- Staleness Risk: Documentation easily gets out of sync with code
- Consistency: Different contributors document parsers differently
- Discovery: Users need to find which formats are supported
- Maintenance Burden: Every parser addition requires manual doc updates
The Python ScanCode Toolkit solves this using a custom script (regen_package_docs.py) that extracts metadata from parser classes and generates reStructuredText documentation.
Question: How do we keep documentation accurate, comprehensive, and up-to-date as we add 40+ parsers?
Decision
Use a hybrid documentation approach: Auto-generate format tables from code metadata + manual architecture/guide documentation + Rust doc comments for API reference.
Documentation Strategy
┌─────────────────────────────────────────────────────────┐
│ Documentation Sources │
└─────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌────────────┐
│ Parser │ │ Doc Comments │ │ Manual │
│ Metadata │ │ (/// //!) │ │ Markdown │
│ (code) │ │ │ │ Files │
└──────┬──────┘ └──────┬───────┘ └──────┬─────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌────────────┐
│ Pre-commit │ │ cargo doc │ │ GitHub │
│ Hook │ │ (docs.rs) │ │ README │
│ (generate) │ │ │ │ /docs/ │
└──────┬──────┘ └──────────────┘ └────────────┘
│
▼
┌──────────────────┐
│ SUPPORTED_ │
│ FORMATS.md │
│ (auto-generated) │
└──────────────────┘
Three Documentation Layers
1. Auto-Generated Format Table
Source: Registered parser metadata (descriptions, path patterns, package types, languages, docs URLs)
Output: docs/SUPPORTED_FORMATS.md
Generation: The xtask generator renders the Markdown table, and CI/pre-commit hooks verify that the checked-in file stays in sync
Example:
| Format | Package Type | File Patterns | Status |
|---|---|---|---|
| package.json | npm | **/package.json | ✅ Complete |
| Cargo.toml | cargo | **/Cargo.toml | ✅ Complete |
| pom.xml | maven | **/pom.xml | ✅ Complete |
Implementation (conceptual): parser metadata is declared close to the parser registration surface, and the generator renders SUPPORTED_FORMATS.md from that registered metadata rather than from hand-maintained Markdown.
Benefits:
- Always accurate (generated from actual code)
- Comprehensive (covers all parsers automatically)
- Consistent format (same template for all)
- No manual maintenance (automated in the Lefthook pre-commit hook)
2. Rust Doc Comments (API Reference)
Source: /// and //! comments in source code
Output: docs.rs API documentation (generated by cargo doc)
Target Audience: Rust developers using Provenant as a library
Example shape: Rust doc comments should explain supported inputs, important behavior, and one small doctest-sized usage example when the public API benefits from it.
Benefits:
- Standard Rust documentation approach
- Integrated with IDE tooltips (rust-analyzer)
- Automatically published to docs.rs
- Supports doc tests (examples are tested)
- Searchable and linkable
3. Manual Markdown Documentation
Source: Manually maintained files in docs/ directory
Output: GitHub repository documentation
Target Audience: End users, contributors, architects
Structure:
docs/
├── ARCHITECTURE.md # System design
├── HOW_TO_ADD_A_PARSER.md # How to add parsers
├── TESTING_STRATEGY.md # Testing philosophy and guidelines
├── adr/ # Architectural decisions
│ ├── 0001-trait-based-parsers.md
│ └── ...
└── improvements/ # Beyond-parity features
├── alpine-parser.md
└── ...
Benefits:
- Explains "why" not just "what"
- Provides context for design decisions
- Documents trade-offs and alternatives
- Guides contributors on conventions
Generation Workflow
Pre-Commit Hook
Lefthook owns the pre-commit behavior. When parser metadata changes, the hook runs the supported-formats generator and stages the regenerated file automatically.
Documentation Generator (Conceptual)
The generator iterates the registered parser metadata, renders a normalized Markdown table, and writes the result to docs/SUPPORTED_FORMATS.md.
Consequences
Benefits
-
Documentation Drift Is Harder To Miss
- Generated from registered metadata instead of hand-maintained tables
- Pre-commit hooks and CI enforce updates
- Parser metadata changes are easy to catch in review
-
Consistency
- Same format for all parsers
- Template-driven generation
- Uniform presentation
-
Discoverability
- Single source of truth (SUPPORTED_FORMATS.md)
- Easy to search and filter
- Clear status indicators
-
Low Maintenance
- No manual doc updates for format table
- Contributors only write code
- Automation handles the rest
-
Multi-Audience
- End users: README + SUPPORTED_FORMATS.md
- API users: docs.rs documentation
- Contributors: Architecture docs + ADRs
Trade-offs
-
Build Complexity
- Requires documentation generation tooling
- Pre-commit tooling adds setup dependency
- Acceptable: One-time setup, huge long-term benefit
-
Partial Automation
- Can't auto-generate architecture decisions or guides
- Still need manual ADRs and improvement docs
- Acceptable: Hybrid approach balances automation and flexibility
-
Rust Doc Comments Required
- Contributors must write
///comments - More verbose than Python docstrings
- Acceptable: Standard Rust practice, kept as a contributor convention
- Contributors must write
Alternatives Considered
1. Fully Manual Documentation
Approach: write and maintain all format coverage documentation by hand.
Rejected because:
- High maintenance burden (40+ parsers)
- Prone to staleness (docs drift from code)
- Inconsistent formatting (different contributors)
- Easy to forget updating when adding parsers
2. Python-Style Registration System
Approach: use runtime metadata registration similar to Python class attributes.
Rejected because:
- Not idiomatic Rust (prefer compile-time)
- Requires runtime introspection
- More complex than trait-based approach
- Doesn't leverage Rust's type system
3. Macro-Based Registration
Approach: use proc macros to register parsers automatically.
Partial acceptance: This could work, but adds complexity.
Why not primary approach:
- Proc macros add compile-time overhead
- Harder to debug
- Less explicit than trait methods
- Can achieve same result with simpler build script
4. No Auto-Generation (Rust Doc Only)
Approach: Rely solely on cargo doc output, no separate SUPPORTED_FORMATS.md.
Rejected because:
- Not user-friendly (API docs are developer-focused)
- Requires users to browse docs.rs
- No single-page format reference
- Harder to search and filter
Python Reference Comparison
Python Approach:
- Auto-generation: Manual script execution
- Format: reStructuredText
- Metadata source: Python class attributes
Our Rust Approach:
- Auto-generation: Pre-commit hook
- Format: Markdown
- Metadata source: Registered parser metadata consumed by the
xtaskgenerator - Additional: cargo doc for API reference
Related ADRs
- ADR 0001: Trait-Based Parser Architecture - Trait system enables metadata extraction
- ADR 0003: Golden Test Strategy - Testing validates documentation accuracy
References
- Rust documentation best practices: Rust API Guidelines