tools: generate the artifact page from canon markdown

The page and the ADR had already diverged once. They are now one source: the
markdown is authoritative and the page is generated, never hand-edited.

The renderer recognises conventions the document already uses rather than
requiring extra markup, so the source stays a readable document. A table whose
first column is **X0**/**X1** becomes a level ladder; a table headed Threat
becomes the threat matrix; a table with a Kind column gets mechanical and
adversarial chips; a table headed "E \ P" becomes the two-axis grid; ## N.
headings build the section rail.

Stdlib only, per the structure-not-tooling stance. A publishing step that needs
its own toolchain is a publishing step that stops being run - the same
reasoning tenant-engine used for its pin check.

One real consequence beyond deduplication: the E x P matrix had existed only on
the page, so the canonical document did not contain its own central diagram.
It is now a table in the markdown, which is where it should always have been.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
codex 2026-08-17 15:10:22 +02:00
parent c4f22a5e55
commit 1c65257352
4 changed files with 587 additions and 4 deletions

View file

@ -227,10 +227,24 @@ breach SHOULD target E3 or above.
| **P3** | Dedicated cluster per tenant. | Business apps per `business-app-service-contract` §1.2 |
| **P4** | P3 plus separate region or jurisdiction. | None |
**P0 → P1 → P2 does not raise the E level.** Those steps buy consumer
isolation, capacity predictability, independent retention and a smaller
operational blast radius. Only P3 makes E4 reachable. This is the most
misusable fact in the framework and §11 governs how it may be described.
Enforcement and placement are independent axes. Plotted together, with where
each service actually sits — parenthesised entries are targets or defaults
rather than current positions, and `—` marks a cell the coupling in §3.2 makes
unreachable:
| E \ P | P0 | P1 | P2 | P3 | P4 |
|---|---|---|---|---|---|
| **E4** | — | — | — | (business app) | |
| **E3** | | (target) | | | |
| **E2** | | tenant-engine<br>audit-core | | | |
| **E1** | (absorbed repo) | | | | |
| **E0** | | | | | |
**P0 → P1 → P2 is movement along the horizontal axis only.** Those steps buy
consumer isolation, capacity predictability, independent retention and a
smaller operational blast radius. They do not raise the tenant boundary by one
step. Only P3 makes E4 reachable. This is the most misusable fact in the
framework and §11 governs how it may be described.
**Decision 4.4.1:** P1 is the default for platform services; P3 for
client-facing business apps, as already ratified. A service unsure which it is