# doc-forge — Architecture & Design Specification **doc-forge** is a renderer-agnostic Python documentation compiler. It converts Python source code and docstrings into a structured, semantic documentation model and then emits multiple downstream representations, including: * Human-facing documentation sites (MkDocs, Sphinx) * Machine-facing documentation bundles (MCP JSON) * Live documentation APIs (MCP servers) This document is the **authoritative design and codebase specification** for the library. It is written to be both **LLM-friendly** and **developer-facing**, and should be treated as the canonical reference for implementation decisions. --- ## 1. Design Goals 1. **Single Source of Truth** Python source code and docstrings are the only authoritative input. 2. **Renderer Agnosticism** MkDocs, Sphinx, MCP, or future renderers must not influence the core model. 3. **Deterministic Output** Given the same codebase, outputs must be reproducible. 4. **AI-Native Documentation** Documentation must be structured, queryable, and machine-consumable. 5. **Library-First, CLI-Second** All functionality must be accessible as a Python API. The CLI is a thin wrapper. --- ## 2. Core Mental Model ### Fundamental Abstraction > **The atomic unit of documentation is a Python import path** Examples: * `mail_intake` * `mail_intake.config` * `mail_intake.adapters.base` Files, Markdown, HTML, and JSON are *representations*, not documentation units. --- ## 3. High-Level Architecture ``` Python Source Code ↓ Introspection Layer (Griffe) ↓ Documentation Model (doc-forge core) ↓ Renderer / Exporter Layer ├── MkDocs ├── Sphinx ├── MCP (static JSON) └── MCP Server (live) ``` Only the **Documentation Model** is shared across all outputs. --- ## 4. Package Layout (Proposed) ``` docforge/ ├── __init__.py ├── model/ │ ├── project.py │ ├── module.py │ ├── object.py │ └── nav.py ├── loader/ │ └── griffe_loader.py ├── renderers/ │ ├── base.py │ ├── mkdocs.py │ └── sphinx.py ├── exporters/ │ └── mcp.py ├── server/ │ └── mcp_server.py ├── cli/ │ └── main.py └── utils/ ``` --- ## 5. Documentation Model (Core) The documentation model is renderer-neutral and must not contain any MkDocs-, Sphinx-, or MCP-specific logic. ### 5.1 Project ```python class Project: name: str version: str | None modules: dict[str, Module] nav: Navigation ``` --- ### 5.2 Module ```python class Module: path: str # import path docstring: str | None members: dict[str, DocObject] ``` --- ### 5.3 DocObject Represents classes, functions, variables, etc. ```python class DocObject: name: str kind: str # class, function, attribute, module path: str signature: str | None docstring: str | None members: dict[str, DocObject] ``` Private members (`_name`) are excluded by default. --- ### 5.4 Navigation ```python class Navigation: entries: list[NavEntry] class NavEntry: title: str module: str ``` Navigation is derived, not authored. --- ## 6. Introspection Layer ### 6.1 Griffe Loader Griffe is the **only supported introspection backend**. Responsibilities: * Load modules by import path * Resolve docstrings, signatures, and members * Tolerate alias resolution failures Output: fully populated `Project` and `Module` objects. --- ## 7. Renderer Interface Renderers consume the documentation model and emit renderer-specific source trees. ```python class DocRenderer(Protocol): name: str def generate_sources(self, project: Project, out_dir: Path) -> None: """Generate renderer-specific source files.""" def build(self, config: RendererConfig) -> None: """Build final artifacts (HTML, site, etc.).""" def serve(self, config: RendererConfig) -> None: """Serve documentation locally (optional).""" ``` --- ## 8. MkDocs Renderer ### Source Generation * Emits `.md` files * One file per module * Uses `mkdocstrings` directives exclusively ```md # Config ::: mail_intake.config ``` ### Build * Uses `mkdocs.commands.build` ### Serve * Uses `mkdocs.commands.serve` MkDocs-specific configuration lives outside the core model. --- ## 9. Sphinx Renderer ### Source Generation * Emits `.rst` files * Uses `autodoc` directives ```rst mail_intake.config ================== .. automodule:: mail_intake.config :members: :undoc-members: ``` ### Build * Uses `sphinx.application.Sphinx` directly ### Serve * Optional (static build is sufficient) --- ## 10. MCP Exporter (Static) The MCP exporter bypasses renderers entirely. ### Output Structure ``` mcp/ ├── index.json ├── nav.json └── modules/ └── package.module.json ``` ### Design Principles * Alias-safe * Deterministic * Fully self-contained * No Markdown, HTML, or templates --- ## 11. MCP Server (Live) The MCP server exposes documentation as queryable resources. ### Resources * `docs://index` * `docs://nav` * `docs://module/{module}` ### Characteristics * Read-only * Stateless * Backed by MCP JSON bundle --- ## 12. CLI Design The CLI is a thin orchestration layer. ```bash doc-forge generate --renderer mkdocs doc-forge generate --renderer sphinx doc-forge build --renderer mkdocs doc-forge serve --renderer mkdocs doc-forge export mcp ``` Renderer choice never affects the core model. --- ## 13. Explicit Non-Goals * Markdown authoring * Theme design * Runtime code execution * Code formatting or linting --- ## 14. Invariants (Must Never Break) 1. Import paths are canonical identifiers 2. Core model contains no renderer logic 3. MCP does not depend on MkDocs or Sphinx 4. Renderers do not introspect Python directly 5. All outputs trace back to the same model --- ## 15. One-Line Definition > **doc-forge is a documentation compiler that turns Python code into structured knowledge and emits it through multiple human and machine interfaces.** --- *End of specification.*