Skip to content

ADR-0077 — MkDocs + Structurizr is the single source of truth for project documentation

Status: active Date: 2026-04-28 Introduced by: Issue #747 ; operator scale-up directive Supersedes: complements ADR-66 (UI Stack) by elevating MkDocs + Structurizr to SSoT status


Context

The project produces knowledge artefacts at multiple surfaces : ADRs, design docs, runbooks, architecture diagrams, committee dossiers, MLOps readiness templates, blueprints, README files, code comments, claude memory. These surfaces serve different audiences (operators, developers, AI assistants, future-self) but historically there has been no rule about which surface is authoritative when they disagree.

Observed failure modes :

  • An architecture decision documented in a chat / claude memory but never landed in documentation/architecture/ → next operator has no way to discover it
  • A Need designed in a documentation/design/CVN-N<nnn>-*.md doc but the doc is never published to docs.cvntrade.eu (mkdocs nav not updated)
  • A Structurizr workspace.dsl change that adds a container but the corresponding mermaid diagram in the design doc is not updated → the two diverge
  • A new feature shipped without an architecture entry, requiring archaeology to understand later
  • ADR-58 invariants (FTF guardrails) discoverable only by reading code comments

ADR-69 + ADR-76 codify SSoT for work tracking (OpenProject). This ADR codifies SSoT for documentation : if it's not in the docs site, it doesn't exist as project knowledge.

The mkdocs site at docs.cvntrade.eu (per ADR-66 stack choice) and the Structurizr workspace at documentation/architecture/workspace.dsl are the two artefacts that, together, form the canonical project knowledge base.

Decision

MkDocs (documentation/ rendered at docs.cvntrade.eu) + Structurizr (documentation/architecture/workspace.dsl) are the single source of truth for project documentation. Any project knowledge — Need, Epic, Design, Architecture decision, Operations procedure, MLOps template, runbook — MUST exist in documentation/ (and therefore on docs.cvntrade.eu) before the corresponding work is considered shipped.

Concrete rules :

  1. Need lifecycle : definition → committee plan_review → log in documentation/needs/CVN-N<nnn>-<slug>.md (post-approval) → linked from OP Need wp
  2. Design : every Need has a design doc at documentation/design/CVN-N<nnn>-<slug>.md (template documentation/templates/TEMPLATE_design.md) — committee-reviewed before implementation per ADR-68
  3. Architecture : load-bearing changes extend documentation/architecture/workspace.dsl (Structurizr) AND have a corresponding documentation/architecture/<topic>.md page with a mermaid diagram (the mkdocs page is the readable extract ; the DSL is the canonical model)
  4. Operations : new operator procedures land in documentation/OPERATIONS.md (or a new section) — runbook discoverable from Grafana / OP wp comment
  5. ADRs : documentation/adr/<NNNN>-<kebab>.md (ADR template) + entry in documentation/adr/index.md ; new ADRs from ADR-56 onwards in English (ADR-57)
  6. MLOps readiness : per-Story file documentation/stories/<cvn_id>/mlops_readiness.md (template documentation/templates/TEMPLATE_mlops_readiness.md) per ADR-70
  7. Discrepancy resolution : when a knowledge surface (chat, claude memory, code comment) disagrees with the docs site, mkdocs site wins. Reconcile other surfaces to the docs site, never the inverse.
  8. Strict-mode build : mkdocs build --strict MUST pass on every PR (already enforced in CI per .github/workflows/docs-site.yml). A broken anchor / missing doc IS a CI failure that blocks merge.
  9. Structurizr lockstep : when an ADR introduces a new container or relationship that is load-bearing for an active invariant (i.e., other ADRs / docs reference it as if it existed), workspace.dsl is updated in the same PR that ratifies the ADR — no exception, no "follow-up PR" loophole. Temporary waiver path : if the design genuinely needs further validation before the DSL change is meaningful (e.g., the container's responsibilities are still being negotiated), the operator MAY merge the ADR with status proposed (per ADR-76 status-honesty pattern) ; the DSL update lands in the same PR that promotes the ADR to active. This forbids the "active ADR + missing DSL" failure mode while preserving the ability to ship in-progress design work. The DSL is render-validated locally with structurizr-cli or the VS Code extension before commit.

Invariants

  • I1 — Docs site is authoritative : every project knowledge artefact (Need, Design, ADR, Operations procedure, MLOps readiness, runbook) is what the docs site at docs.cvntrade.eu says it is. Other knowledge surfaces (chat, claude memory, code comment, README outside documentation/) mirror the docs site, never the inverse.
  • I2 — Strict-mode CI is non-negotiable : mkdocs build --strict is a hard gate on every PR. Pre-existing broken refs are technical debt to be fixed (cf. PR #728 — first cleanup of the strict-mode failure backlog), not a license to introduce more.
  • I3 — Structurizr coverage : every architectural container / relationship that load-bears for an ADR exists in workspace.dsl. A mermaid-only diagram is acceptable for ad-hoc internal explanations, but the DSL model is the canonical view referenced from ADRs.
  • I4 — Migration on touch : pre-ADR-77 docs that don't follow the conventions are migrated when the work resumes on them — not retroactively in one batch. The CVN-N009 Epic (docs site health, #725) tracks the cleanup backlog.
  • I5 — Need → Epic → Story chain documented : every Story has a discoverable path to its Epic and its Need via the docs site (cross-reference links + the OP wp tree). The cvn_id naming convention (per ADR-76 I2) makes this navigable.
  • I6 — No knowledge dark matter (scoped to implemented decisions) : if a finalized, implemented or about-to-be-implemented decision exists somewhere (chat, code comment, claude memory) but not in the docs site, the operator (or AI assistant) MUST land it in the docs site before considering the work done. "I'll document it later" is a violation. Out of scope : ephemeral ideation-phase discussions, abandoned alternatives, brainstorm threads — these are explicitly NOT subject to I6 (they don't represent project decisions, only exploration). The boundary : a decision is "finalized" once it has either (a) a committee plan_review verdict, (b) an issue + branch, (c) a PR, or (d) a code change. Pre-(a) ideation stays in chat / claude memory without obligation. (Per committee b7a2b7d4 reco 2 — clarifies the rule's scope to avoid creating doc-write friction during early exploration.)

Alternatives rejected

  • Wiki / Confluence / Notion : another vendor, no version control, not co-located with the code, hard to migrate. Rejected — mkdocs in-repo is co-located and version-controlled by definition.
  • README files scattered across the repo : no central nav, no cross-doc anchors enforced, no rendered site. Rejected — README files are acceptable as code-adjacent micro-docs but the canonical knowledge MUST be in documentation/.
  • Code comments + auto-generated API docs only : works for API surfaces, fails for architecture / process / decisions / runbooks. Rejected — code-only documentation is a known anti-pattern at scale.
  • OpenProject as docs SSoT (collapse with ADR-76) : OP is good at work tracking but bad at long-form knowledge (no per-page anchors, no rendered diagrams, no cross-link validation, no version control). Rejected — keep ADR-76 (OP for work) and ADR-77 (mkdocs/Structurizr for docs) complementary.
  • Mermaid diagrams in markdown sufficient (skip Structurizr) : mermaid handles per-doc diagrams well but doesn't model the cross-doc system architecture coherently. Structurizr's DSL is the canonical model that mermaid extracts can deviate from. Rejected — keep both, with Structurizr as the canonical model when load-bearing for an ADR.

Consequences

  • Positive : single answer to "where is the knowledge ?" — docs.cvntrade.eu. Discoverability is uniform (search + nav). Diagrams stay coherent because the DSL is the source.
  • Positive : strict-mode CI catches broken refs at PR time, preventing the slow rot we observed pre-PR #728.
  • Positive : synergistic with ADR-66 (UI Stack chose mkdocs + Structurizr), ADR-68 (committee), ADR-69 + ADR-76 (OP). The 5 ADRs together form a coherent process + knowledge spine.
  • Positive : the migrate-on-touch policy (I4) avoids a busywork rewrite of all pre-existing docs ; debt is paid down as work resumes on each surface.
  • Negative : ~5-15 minutes overhead per substantive PR to land the doc update + mermaid / DSL diagram. Acceptable given the discoverability gain.
  • Negative : Structurizr DSL learning curve for new contributors. Mitigated by the existing documentation/architecture/workspace.dsl as a worked example + the per-ADR extension pattern (cf. CVN-N010 design §4.5).
  • Neutral : claude memory continues to exist, but it is a cache (per ADR-76 §Alternatives), not a truth source. Memories that contradict the docs site are stale and should be deleted on discovery.

Rollback

This ADR is process. Rollback = remove this file + revert CLAUDE.md / OPERATIONS.md edits introduced by issue #747. The docs site at docs.cvntrade.eu and the Structurizr workspace stay (descriptive artefacts regardless of policy).

If the SSoT discipline proves systematically wasteful (e.g., > 30 % of operator time spent on docs updates with no measurable improvement in discoverability), revisit the migration-on-touch policy or the Structurizr lockstep requirement before retiring the ADR.

References

  • ADR-26 — Grafana as single entry point (sister SSoT for observability surfaces)
  • ADR-57 — ADRs in English (process discipline, applies to all docs from ADR-56)
  • ADR-66 — UI Stack (chose mkdocs + Structurizr — this ADR elevates them to SSoT)
  • ADR-68 — Expert Committee = default review channel (committee dossiers live in documentation/reviews/)
  • ADR-69 — OpenProject orchestrator (Story dossiers in documentation/stories/)
  • ADR-70 — MLOps readiness template (template + filled per-Story files in documentation/)
  • ADR-76 — OpenProject SSoT for project memory (sister ADR, same scale-up directive — work tracking vs knowledge base)
  • documentation/architecture/workspace.dsl — Structurizr canonical model
  • mkdocs.yml — docs site nav config
  • .github/workflows/docs-site.yml — strict-mode CI gate (I2 enforcement)
  • Issues : #593 (OP + docs Phase 1), #725 (CVN-N009 docs site health Epic — backlog cleanup), #747 (this ADR + ADR-76)
  • Committee sessions : b7a2b7d4 (this ADR's plan_review, PASSED OK avg 8.4 strong, 0 blocker, 0 dissent, 8 forward-looking recos : reco 2 I6 scope clarified ✅ in this revision (ideation vs implementation boundary explicit) ; recos 1/3/4/5/6/7/8 captured as backlog : Structurizr training (1), CVN-N009 backlog reviews (3), doc-adherence metrics (4), PR-template semantic checklist (5), pilot adoption (6), recovery path doc in OPERATIONS.md (7), docsync CLI abstraction (8))