Wiki · Architecture & Decisions
ADR-0004 — Architecture is model-as-code; hand-authored SVG is not an architecture format
How to read this page
Three ways to read this page. Precise is the document itself, exactly as it is written in the repository. Plain and Clear were written for this website to help you meet that document — they are about it. They are not it, and they are not evidence.
This is the design record of the part of the estate whose job is to stop the rest of it claiming more than it has measured. It runs to architecture notes, numbered decision records, phase plans, and the results written after each phase ran.
It is for anyone curious how a decision was actually reached — what was considered, what was refused, and what the refusal cost. The decision records are the most durable part: each names one choice, the alternatives, and the consequence accepted in exchange. The failure-modes page is worth the detour, because it states each refusal as something you could go and test rather than as a promise.
Read the architecture page first. It opens by declaring itself part design and part built, which sets the tone for the whole section. Then the first decision record, then whichever phase interests you — read as a pair, the plan and the results page written afterwards, including the phases where a premise turned out to be wrong and the receipt says so.
What it is not: a description of running software, and not a complete record. Three documents from this area were withheld from publication, because they describe private infrastructure or the operator declined them; they are listed as withheld rather than quietly dropped, so you can see that they exist.
Your browser cannot switch reading levels, so the document itself is shown.
Precise — the source document
This is the document. Rendered from the repository at the commit above, with nothing rewritten for the web. A gate re-renders it on every deploy and fails the build if a single byte differs.
- Status: Accepted (supersedes the SVG diagrams committed in
0587c3b) - Date: 2026-07-25
- Deciders: Michael, Veritas, Custos
Context
The first attempt at documenting this architecture committed two hand-authored SVG files with absolute x/y coordinates on every element — 508 lines of positioned rectangles and text.
The operator's objection, verbatim: "how the fuck are we going to collaborate over SVG files… now it is just a picture."
The objection is correct and the failure is concrete:
- Not editable. Moving one box requires recomputing the coordinates of every neighbour and every connecting path by hand.
- Not diffable. A layout change and a semantic change look identical in
git diff. A reviewer cannot see that a relationship was added. - Not readable by the author. Coordinates do not carry meaning; the file cannot be reasoned about after it is written, only re-rendered.
- No single source of truth. Two SVGs sharing the same subject drift independently, with nothing to detect it — the same disease ADR-0002 exists to prevent.
- Not a model. There are no typed elements or relationships, so nothing can be validated, queried, or projected into a second view.
Decision
Architecture is maintained as text that models, not text that draws:
workspace.dsl— Structurizr DSL, the model of record. One C4 model with typed people, containers, relationships and deployment nodes; multiple views derived from it. Industry-standard C4, tool-supported, reviewable line by line.views.md— Mermaid, the zero-tooling projection. Renders natively in GitHub, GitLab and most markdown viewers with no build step. Text in, picture out.decisions/ADR-*.md— the reasoning. MADR format. Every consequential choice gets its context, decision, consequences, alternatives and falsifier.ARCHITECTURE.md— the prose that the model cannot carry: contracts, invariants, failure modes, acceptance criteria.
The hand-authored SVGs are deleted, not left beside the model. Two sources of truth is the problem being fixed.
Consequences
Positive. Every element and relationship is a reviewable line. A pull request shows "added relationship Control Plane → Approval queue", not a coordinate delta. One model yields several views without redrawing. Both a human and an agent can read the model back and reason over it. Layout is the renderer's job, which is what renderers are for.
Negative. Structurizr rendering needs tooling (structurizr-cli, Java, or the free web renderer) that is not currently installed — recorded as an open item. Mitigated by Mermaid, which needs nothing. Mermaid's C4 support is less complete than Structurizr's, so the two views may not be pixel-identical; the DSL is authoritative where they differ.
Neutral. Contributors need to know C4's four levels and Mermaid basics. Both are widely documented and standard practice.
Alternatives considered
Keep the SVGs, add a model alongside. Rejected: two sources of truth that drift, with no mechanism to detect it.
PlantUML. Viable and text-based, but needs a render step everywhere and has no first-class deployment or C4 element typing without an extension library.
Mermaid alone, no DSL. Simpler, and it was tempting. Rejected because Mermaid views are independent drawings — the same relationship must be repeated in each diagram, so views drift from one another. The DSL holds one model that the views project.
Diagrams-as-code in a general language (Python diagrams, D2). Rejected: adds a runtime dependency to read the architecture, and D2 is not C4-native.
Falsifier
If a relationship exists in a rendered view but not in workspace.dsl, or a view is edited without the model, this decision has been violated. If anyone hand-positions a diagram element again, it has been violated.
Addendum — toolchain installed 2026-07-25, and one honest limitation
The tooling gap this ADR recorded as open is now closed. Installed user-local; nothing system-wide was changed:
| tool | version | why |
|---|---|---|
| Temurin JDK 17 | 17.0.19 | structurizr-cli ships class file 61.0; the system JDK 11 (55.0) cannot load it |
| structurizr-cli | 2025.11.09 | validates the DSL and exports every view from the one model |
| PlantUML | 1.2026.6 | renders the exported C4-PlantUML to SVG and PNG |
| graphviz | 14.1.0 | already present; PlantUML's layout engine |
Reproduce with render.sh: validate, export to C4-PlantUML and Mermaid, render to SVG and PNG.
Two real errors were caught by validate that a hand-drawn diagram could never have surfaced:
deployment takes environment then key, and the key rejected a description used in its place;
and element styles require one property per line. This is the argument for the format, made by the format.
Limitation — element styles do not survive the PlantUML export. The Built / NotBuilt /
Store styling in workspace.dsl renders in the Structurizr renderer (web or Lite), but
C4-PlantUML applies its own theme, so every container in generated/*.png appears in the same
blue. The built-versus-not-built distinction is therefore carried in the element descriptions
and in ARCHITECTURE.md, never by colour alone. Do not read the rendered PNGs as a statement
about what is built. This is recorded rather than worked around, because a reader who infers
"all blue means all built" would be badly misled.
sha256 fc6e389f9b54e422 — of the original file, so what was ingested stays checkable.
Plain — written for this website, not the source document
How the architecture itself gets written down is settled here, in a dated record. It replaces two hand-drawn diagram files that had been committed earlier.
The problem with those files was concrete. Every shape carried its own position, so moving one box meant recomputing its neighbours by hand. A layout change and a change of meaning looked identical in a diff, so a reviewer could not see that a relationship had been added. Coordinates carry no meaning, so the file cannot be reasoned about after it is written, only re-rendered. And two drawings of the same subject could drift apart with nothing to detect it.
The decision is to keep the architecture as text that models rather than text that draws. That means one typed model of record, a lighter text-to-picture projection that needs no tooling, one reasoning record per consequential choice, and prose for what a model cannot carry. The drawings were deleted rather than kept alongside, because two sources of truth was the problem being fixed.
An addendum records that the missing tooling was later installed, and one honest limitation you should not skip.
Plain · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is fc6e389f9b54e422
Clear — written for this website, not the source document
A dated decision record about the format of the architecture documentation, superseding an earlier commit of hand-authored drawings.
The context is that the first attempt at documenting this architecture committed two drawing files with absolute coordinates on every element — hundreds of lines of positioned rectangles and text. The operator objected, bluntly, that collaborating over such files is not possible and that the result is just a picture. The record accepts the objection and lists concrete failures. The files are not editable, because moving one box means recomputing every neighbour and every connecting path by hand. They are not diffable, because a layout change and a change of meaning look the same. They are not readable by their own author, because coordinates carry no meaning. They are not a single source of truth, because two drawings of one subject drift independently with nothing to detect it. And they are not a model at all, because there are no typed elements or relationships, so nothing can be validated, queried, or projected into a second view.
The decision is that architecture is maintained as text that models rather than text that draws, in four layers. A typed model of record holds people, containers, relationships and deployment nodes, and several views are derived from it. A lighter markup projection renders natively in common viewers with no build step. Individual decision records carry the reasoning: context, decision, consequences, alternatives, and the finding that would show the choice wrong. Prose carries what the model cannot: contracts, failure modes, acceptance criteria, and the rules that must not break. The hand-authored drawings were deleted rather than left beside the model, because two sources of truth is the disease being treated.
The consequences are given in both directions. In favour: every element and relationship becomes a reviewable line, so a change reads as a relationship added rather than as a coordinate delta; one model yields several views without redrawing; and layout goes back to being the renderer's job. Against: at the time of writing, the tooling for the model of record was not installed, which the record files as an open item. The lighter projection is also less complete, so the two views may not match exactly — where they differ, the model is authoritative.
Several alternatives were considered and each rejected with a reason, including keeping the drawings alongside a model, and using the lighter markup on its own. The second was tempting, but was rejected because independent drawings repeat the same relationship in each diagram and so drift from one another.
An addendum records that the tooling gap was later closed, installed for one user rather than system-wide, and notes that validation immediately caught two real errors that a hand-drawn diagram could never have surfaced. It closes with a limitation that a reader should not skip: the styling that marks which containers are built and which are not does not survive the export, so every container in the generated pictures comes out the same colour. That distinction is therefore carried in the descriptions and in the prose, never by colour alone, and the record says plainly not to read the rendered pictures as a statement about what is built.
Clear · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is fc6e389f9b54e422