From 734b70a9a67831f2a840e55dba9219cf2e6e52f4 Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:00:14 +0200 Subject: [PATCH 1/6] chore(architecture): refresh cached skills_by_profile enrichment Repopulates the per-module enrichment cache via 'architecture enrich all'. The documentation module gains the skills_by_profile.implementation profile it was missing, which had left documentation-only implementation tasks resolving zero architecture-driven skills; the other four modules pick up the build-server-client skill entry. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EkMQfbbBESLRHEch8ZuUWe --- .../api-sheriff-parent/enriched.json | 12 +++++++++-- .../api-sheriff/enriched.json | 12 +++++++++-- .../benchmarks/enriched.json | 12 +++++++++-- .../documentation/enriched.json | 20 +++++++++++++++++++ .../integration-tests/enriched.json | 12 +++++++++-- 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/.plan/project-architecture/api-sheriff-parent/enriched.json b/.plan/project-architecture/api-sheriff-parent/enriched.json index 4d045c44..10c8a7c5 100644 --- a/.plan/project-architecture/api-sheriff-parent/enriched.json +++ b/.plan/project-architecture/api-sheriff-parent/enriched.json @@ -34,7 +34,11 @@ "description": "Core Java patterns including modern features and performance optimization", "skill": "pm-dev-java:java-core" }, - "pm-dev-java-cui:cui-logging" + "pm-dev-java-cui:cui-logging", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 build-dispatch routes builds through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "module_testing": { @@ -59,7 +63,11 @@ "description": "JUnit 5 testing patterns with AAA structure, coverage analysis, and assertion standards", "skill": "pm-dev-java:junit-core" }, - "pm-dev-java-cui:cui-logging" + "pm-dev-java-cui:cui-logging", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 test-run dispatch routes through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "quality": { diff --git a/.plan/project-architecture/api-sheriff/enriched.json b/.plan/project-architecture/api-sheriff/enriched.json index 3033b675..293f14fa 100644 --- a/.plan/project-architecture/api-sheriff/enriched.json +++ b/.plan/project-architecture/api-sheriff/enriched.json @@ -69,7 +69,11 @@ "skill": "pm-dev-java:java-maintenance" }, "pm-dev-java-cui:cui-logging", - "pm-dev-java-cui:cui-http" + "pm-dev-java-cui:cui-http", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 build-dispatch routes builds through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "module_testing": { @@ -116,7 +120,11 @@ }, "pm-dev-java-cui:cui-logging", "pm-dev-java-cui:cui-testing", - "pm-dev-java-cui:cui-http-testing" + "pm-dev-java-cui:cui-http-testing", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 test-run dispatch routes through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "quality": { diff --git a/.plan/project-architecture/benchmarks/enriched.json b/.plan/project-architecture/benchmarks/enriched.json index c296e449..303dad83 100644 --- a/.plan/project-architecture/benchmarks/enriched.json +++ b/.plan/project-architecture/benchmarks/enriched.json @@ -33,7 +33,11 @@ "description": "Core Java patterns including modern features and performance optimization", "skill": "pm-dev-java:java-core" }, - "pm-dev-java-cui:cui-logging" + "pm-dev-java-cui:cui-logging", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 build-dispatch routes builds through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "module_testing": { @@ -58,7 +62,11 @@ "description": "JUnit 5 testing patterns with AAA structure, coverage analysis, and assertion standards", "skill": "pm-dev-java:junit-core" }, - "pm-dev-java-cui:cui-logging" + "pm-dev-java-cui:cui-logging", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 test-run dispatch routes through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "quality": { diff --git a/.plan/project-architecture/documentation/enriched.json b/.plan/project-architecture/documentation/enriched.json index a9ddb874..88f61c28 100644 --- a/.plan/project-architecture/documentation/enriched.json +++ b/.plan/project-architecture/documentation/enriched.json @@ -37,6 +37,26 @@ "skill": "pm-documents:manage-interface" } ] + }, + "implementation": { + "defaults": [ + { + "description": "AsciiDoc formatting, validation, link verification, and template creation", + "skill": "pm-documents:ref-asciidoc" + }, + { + "description": "Content quality, tone analysis, organization standards, and review orchestration", + "skill": "pm-documents:ref-documentation" + }, + { + "description": "Narrative styles for technical documentation \u2014 tone, voice, and arc for the engineering-narrative style", + "skill": "pm-documents:ref-narrative-styles" + }, + { + "description": "SVG diagram authoring standards \u2014 visual language, theme handling, AsciiDoc embedding, per-diagram-type patterns", + "skill": "pm-documents:ref-svg-diagrams" + } + ] } }, "skills_by_profile_reasoning": "documentation: AsciiDoc design docs under doc/ incl. 4 ADRs (manage-adr optional applies)", diff --git a/.plan/project-architecture/integration-tests/enriched.json b/.plan/project-architecture/integration-tests/enriched.json index ca0df292..206254c0 100644 --- a/.plan/project-architecture/integration-tests/enriched.json +++ b/.plan/project-architecture/integration-tests/enriched.json @@ -56,7 +56,11 @@ "skill": "pm-dev-java:java-maintenance" }, "pm-dev-java-cui:cui-logging", - "pm-dev-java-cui:cui-http" + "pm-dev-java-cui:cui-http", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 build-dispatch routes builds through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "module_testing": { @@ -103,7 +107,11 @@ }, "pm-dev-java-cui:cui-logging", "pm-dev-java-cui:cui-testing", - "pm-dev-java-cui:cui-http-testing" + "pm-dev-java-cui:cui-http-testing", + { + "description": "marshalld build-server consumption client (submit/wait/ping/preflight) \u2014 test-run dispatch routes through the daemon when the project is registered, else falls back in-process", + "skill": "plan-marshall:build-server-client" + } ] }, "quality": { From b2d59a8c263b06437a7c7eb6d81806b385a4217b Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:31:44 +0200 Subject: [PATCH 2/6] docs(diagrams): add deployment diagram type standard, template and IT-topology reference Adds a deployment/topology SVG diagram type to the house documentation, in three artifacts: - doc/development/diagram-type-deployment.md - the type standard, specifying containment nesting (depth limit, insets, per-level corner radius, label bands, minimum legible box size), protocol-and-port edge labels with the crowded-corridor and cross-container rules, the trust-boundary visual (dashed 8 4 at 2.0px plus a crossing glyph, distinguished without hue), first-party vs external components, and mounted material as pills. - doc/resources/templates/deployment-diagram-skeleton.svg - the copy-rename-fill skeleton, carrying a worked placeholder for every affordance. - doc/resources/diagrams/integration-test-topology.svg plus doc/development/integration-test-topology.adoc - the reference implementation and its contributor page, drawn entirely from integration-tests/docker-compose.yml and the mounted gateway.yaml. The standard is authored in Markdown rather than AsciiDoc as a deliberate format outlier, so the intended graduation to pm-documents:ref-svg-diagrams is a file move rather than a rewrite; the rationale and both destination paths are stated in the document itself. Also records the container-run render-verification recipe, including the two corrections established while proving it: the Alpine package is rsvg-convert (librsvg ships only the shared library), and the font packages are mandatory because a fontless container renders every glyph as tofu while still exiting 0. Both SVGs were rasterised against the light and dark GitHub backgrounds and read back before commit. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EkMQfbbBESLRHEch8ZuUWe --- doc/development/README.adoc | 12 + doc/development/diagram-type-deployment.md | 412 ++++++++++++++++++ .../integration-test-topology.adoc | 112 +++++ .../diagrams/integration-test-topology.svg | 192 ++++++++ .../templates/deployment-diagram-skeleton.svg | 129 ++++++ 5 files changed, 857 insertions(+) create mode 100644 doc/development/diagram-type-deployment.md create mode 100644 doc/development/integration-test-topology.adoc create mode 100644 doc/resources/diagrams/integration-test-topology.svg create mode 100644 doc/resources/templates/deployment-diagram-skeleton.svg diff --git a/doc/development/README.adoc b/doc/development/README.adoc index 0a7a8a4e..fdd6236d 100644 --- a/doc/development/README.adoc +++ b/doc/development/README.adoc @@ -66,6 +66,18 @@ This tree is seeded here and grows as contributor-facing material lands. change requires a `FORMAT_VERSION` bump, the derived session identity with its stability-and-uniqueness constraints, the engine-boundary rule, and the unit/integration test entry points. + +| link:integration-test-topology.adoc[Integration-Test Topology] +| The shape of the stack `integration-tests` brings up -- the five gateway instances and their + published ports, the SNI front listener's L4 relay to the passthrough backend, the mounted + certificate and configuration material, and the two trust boundaries -- drawn from + `docker-compose.yml` and the mounted `gateway.yaml`, which stay the authoritative sources. + +| link:diagram-type-deployment.md[Deployment Diagram Type -- Authoring Standard] +| How a deployment/topology SVG is drawn -- containment nesting with its depth limit and insets, + protocol-and-port edge labels, the trust-boundary visual that stays inside the theme-neutral + palette, first-party vs external components, mounted material, and the container-run render + check. Written in Markdown, deliberately, so it can graduate upstream unchanged. |=== == Scope of This Layer diff --git a/doc/development/diagram-type-deployment.md b/doc/development/diagram-type-deployment.md new file mode 100644 index 00000000..3251f3b8 --- /dev/null +++ b/doc/development/diagram-type-deployment.md @@ -0,0 +1,412 @@ +# Diagram Type — Deployment / Topology + +Covers deployment diagrams: what runs where, what it talks to, over which protocol and port, and +where trust changes. The structural unit is the **enclosure** — a box that contains other boxes — +rather than the column, the lifeline or the stage. + +Reference implementation: `doc/resources/diagrams/integration-test-topology.svg`. + +This type layers on top of the shared visual language. Palette, typography, stroke widths, corner +radii, arrow-marker geometry and the 8-pixel grid are defined once in +`pm-documents:ref-svg-diagrams/standards/visual-language.md` and are **not** restated here. Theme +handling follows `pm-documents:ref-svg-diagrams/standards/theme-handling.md`; every diagram of this +type uses **Strategy A (theme-neutral)**, the single `#6e7681` token, because a deployment diagram +carries more strokes per unit area than any other type and the theme-aware variants diverge visibly +under dense nesting. + +## Upstream graduation + +This document is written to move upstream unchanged. Its destination is: + +- `pm-documents:ref-svg-diagrams/standards/diagram-type-deployment.md` +- `pm-documents:ref-svg-diagrams/templates/deployment-diagram-skeleton.svg` + +The graduation is intended to be a **file move, not a rewrite**. Two consequences bind every section +below: + +1. **No project-specific assumption outside the § Reference implementation section.** No service + name, port, path or rule that only makes sense for one gateway appears in the specification + itself. Everything concrete lives in the clearly-named reference-implementation section, which + the move drops or replaces. +2. **This file is Markdown, in a documentation tree that is otherwise AsciiDoc.** That is a + deliberate, accepted format outlier, not an oversight. The upstream `standards/diagram-type-*.md` + documents are Markdown; authoring this one in AsciiDoc would force a format conversion at + graduation time and turn a `git mv` into a rewrite. A reader who encounters a lone `.md` in an + `.adoc` tree should read it as a signal that the file is destined to leave. + +Cross-references to sibling upstream documents are written as skill-notation paths +(`pm-documents:ref-svg-diagrams/standards/visual-language.md`) rather than as relative Markdown +links, because those siblings are not present in this repository. At graduation they become ordinary +relative links (`visual-language.md`) — a mechanical substitution. + +## When to use this type + +Use a deployment diagram when the question the reader is asking is **where does this run, and what +can reach it**. Specifically: + +- **Runtime topology** — hosts, networks, containers, processes and the links between them. +- **Cluster topology** — cluster, namespace, pod, container, and the services fronting them. +- **Trust geometry** — where an untrusted caller becomes a trusted one, and which component performs + the change. +- **Deployment variants** — the same artifact deployed several times with different configuration, + where the differences are what the reader needs. + +Use a different diagram type when: + +- The structure is **flow through stages** → use the flow type (`diagram-type-flow.md`). +- The structure is a **time-ordered exchange** → use the sequence type (`diagram-type-sequence.md`). +- The structure is **producer / store / consumer** or a **side-by-side comparison** → use the block + type (`diagram-type-block.md`). +- The structure is **layers stacked on a substrate** → use the stack type (`diagram-type-stack.md`). +- The structure is **hub-and-spoke relationships without containment** → use the graph type + (`diagram-type-graph.md`). + +The discriminator is containment. If nothing in the picture is *inside* anything else, this is the +wrong type. + +## Layout grid + +### Standard configurations + +| Shape | viewBox | Use | +|-------|---------|-----| +| Single host | `0 0 1000 620` | One host or one network, three to six contained components. The default. | +| Multi-host | `0 0 1200 760` | Two or more peer hosts / networks, or a cluster with two namespaces. | +| Wide strip | `0 0 1200 520` | A single network with many peer containers and shallow nesting. | +| Dense stack | `0 0 1200 900` | One network carrying eight or more components at full nesting depth. The tallest shape; reach for a second diagram before reaching past it. | + +Positions and sizes snap to multiples of 8, per the shared grid rule. + +| Spacing | Value | Notes | +|---------|-------|-------| +| Outer margin (`viewBox` edge → outermost enclosure) | 24 px | Larger than the shared 20 px minimum: the outermost element here is itself a bordered box, so it needs visible air around its stroke. | +| Gutter between sibling enclosures | 32 px | Horizontal or vertical. Cross-container edges run in these gutters. | +| Gutter between sibling leaf boxes inside one enclosure | 24 px | | +| Legend block inset | 24 px from the bottom-left `viewBox` corner | Only when the numbered-leg convention is in use. | + +### Containment nesting + +Enclosures nest. The canonical ladders are: + +```text +host ─▶ network ─▶ container (compose / plain-runtime form) +cluster ─▶ namespace ─▶ pod ─▶ container (orchestrated form) +``` + +| Rule | Value | +|------|-------| +| Maximum nesting depth | **4 boxes**, counting the outermost enclosure as depth 1. | +| Inset — child box edge to parent box inner edge | **16 px** on every side, at every level. | +| Label band — reserved strip at the top of every enclosure | **28 px**. No child box may start above `parent_top + 28`. | +| Minimum legible inner box | **120 × 48 px**. | + +Corner radius decreases with depth, so nested corners read as concentric rather than parallel: + +| Depth | `rx` / `ry` | +|-------|-------------| +| 1 — outermost enclosure (host, cluster) | 8 | +| 2 — network, namespace | 6 | +| 3 — pod, container group | 6 | +| 4 — container, process | 4 | + +Depth 4 is the floor because a box at depth 4 sits 48 px inside its outermost ancestor on each axis +and still has to hold a legible label. **When the topology needs a fifth level, split it into a +second diagram** rather than shrinking the type or the inset — a depth-5 box cannot satisfy the +120 × 48 px minimum inside any of the standard `viewBox` sizes. + +The 120 × 48 px minimum is what a box needs to hold a 13 px label plus one 11 px sub-label with the +standard 12 px left inset and the 28 px label band. **A box that would fall below the minimum is not +drawn smaller — it is collapsed**: replace the group of undersized boxes with one box carrying a +count and an annotation naming what was collapsed (see § Collapsed groups). + +### Enclosure labels + +Enclosure labels sit **top-left inside the box**, never centered. Centering a container label puts +it directly above the first nested child, where the two collide as soon as the child is added. + +```svg + +host +docker bridge network +``` + +| Element | Class | Position | Type | +|---------|-------|----------|------| +| Enclosure label | `encl` | `x = box_left + 12`, baseline `y = box_top + 24` | sans 13 px / 600, upright | +| Enclosure sub-label (optional, one line) | `encl-sub` | same `x`, baseline `y = box_top + 40` | sans 11 px, italic | + +An enclosure carrying a sub-label uses a **44 px** label band instead of 28 px. + +Leaf-component labels — the innermost boxes that represent an actual running thing — follow the same +top-left rule, so a reader's eye tracks a single left edge down the whole nest. + +## Content positioning + +### First-party versus external components + +A deployment diagram almost always mixes components the reader's team owns with components it merely +talks to. The distinction is carried by a **left-edge accent bar**, not by hue: + +```svg + + + + + + +``` + +`.own-bar` is a 3 px stroke in the same token, drawn on the box's left face and inset 8 px from each +corner so it does not fight the corner radius. First-party boxes carry it; external boxes do not. + +The bar is deliberately **not** a border-weight change: a heavier border would collide with the +trust-boundary language below, which owns stroke weight. It is deliberately not a fill: fills are +reserved for activation-style translucency and would darken a nested box past legibility. + +When a diagram is entirely first-party or entirely external, omit the bar everywhere — a distinction +drawn against nothing is noise. State the fact in the `` instead. + +### Mounted material + +Certificates, configuration files and secrets are **material**, not components. They do not run, +they do not receive traffic, and they must not be drawn as another box in the nest — a box implies a +peer, and an arrow into it implies a flow. + +Mounted material is drawn as a **tag pill attached to the consuming box's bottom edge**: + +```svg + + +certificates/ +``` + +| Element | Class | Geometry | +|---------|-------|----------| +| Stem | `mount-stem` | 1.0 px, `stroke-dasharray: 2 2`, 12 px long, vertical, from the box's bottom edge | +| Pill | `stroke` | height 18, `rx="4"`, width = label width + 16 | +| Label | `mount` | mono 10 px, upright, `x = pill_left + 8`, baseline `y = pill_top + 13` | + +The stem is deliberately heavier than the 0.6 px `3 3` divider dash. A 12 px run of 0.6 px dashes +is effectively invisible in the rasterised light-background render — it survives inspection of the +markup and disappears on the PNG, which is exactly the defect class the mandatory read-back exists to +catch. 1.0 px with a `2 2` pattern stays visibly dashed at raster scale while remaining obviously +lighter than the 1.5 px arrow stroke. + +Rules: + +- **No arrow marker on a mount stem.** A mount is inert material present at start-up, not traffic. + The dashed stem with no arrowhead is the whole point of the notation. +- Multiple mounts stack **horizontally** along the bottom edge with 8 px between pills, left-aligned + with the consuming box. +- When the pills exceed the consuming box's width, wrap to a second row 22 px lower. Three rows is + the practical limit; past that, collapse to one pill carrying the mount root. +- A mount pill's label is the **mount path as written in the deployment source**, in monospace, + trailing slash retained for directories. + +### Collapsed groups + +When several components are structurally identical and differ only in configuration, drawing each in +full buries the affordances the diagram exists to show. Collapse them: + +```svg + + +variant instances (4) +same image, overlaid configuration +``` + +- The count goes in the label, in parentheses. A collapsed group without a count is an omission. +- One `encl-sub` line names **what varies**, so the reader knows what the collapse hid. +- The members are enumerated in the `` element, always. The raster hides them; assistive tech + and full-text search must not. +- A collapsed group is a **legibility choice, and it is falsifiable**: if the rendered result shows + the collapse hiding an affordance the diagram is meant to demonstrate, add a second diagram rather + than expanding the group back into the first. + +## Arrows and labels + +### Protocol and port edge labels + +Every edge between components carries what protocol runs over it and, where the port is meaningful, +which port it lands on. + +**Format:** `PROTOCOL :port` — protocol in upper case, single space, colon immediately followed by +the port number with no intervening space. + +```text +HTTPS :8443 HTTP :80 gRPC :9000 TCP :5432 WS :8443 +mTLS :8444 mTLS HTTPS +``` + +- Omit `:port` when the port carries no information for the reader — an in-process call, an L4 relay + leg that inherits its listener's port, or a leg whose port is an implementation detail. +- `mTLS` is written as a protocol in its own right when mutual authentication is the fact the reader + needs; otherwise write `HTTPS` and record the client-certificate requirement in the ``. +- Never abbreviate to a bare port number. `:8443` alone tells the reader nothing about the wire. + +**Type and placement.** Edge labels are **monospace 11 px, upright** (class `edge-lbl`) — they are +identifiers, and the shared typography rule puts identifiers in monospace. This also separates them +at a glance from prose annotations, which stay sans-serif italic. + +| Edge orientation | Label placement | +|------------------|-----------------| +| Horizontal | Centered on the edge, baseline 8 px above the line, `text-anchor: middle` | +| Vertical | 8 px to the right of the line at its midpoint, `text-anchor: start` | +| Orthogonal (multi-segment) | On the **longest** segment of the run, using that segment's rule | + +**Crowded diagrams.** When two edge labels would come within 14 px of each other: + +1. Move the second label to the **opposite side** of its own edge — below a horizontal edge, left of + a vertical one. Never shrink the type; 11 px monospace is the floor at which the rasterised + result stays legible on both backgrounds. +2. If that is still not enough — three or more edges sharing one corridor — switch that corridor to + the **numbered-leg convention**: replace each label with a leg number in a 9 px-radius open circle + at the edge midpoint, monospace 10 px centered, and put the expansion in a legend block at the + bottom-left of the diagram. Number legs in reading order, top-to-bottom then left-to-right. +3. Use the numbered-leg convention for **the whole corridor or none of it**. Mixing inline labels and + leg numbers in one corridor is worse than either. + +**Cross-container edges.** An edge whose endpoints sit in different enclosures: + +- is drawn **orthogonally** — horizontal and vertical segments only, no diagonals; +- **exits its source box through the nearest face**, and enters its target the same way, so the + reader can see which boundary was crossed; +- carries its label in the **widest gutter segment** of its run, never inside an enclosure box where + it would read as belonging to that enclosure's contents; +- switches to the numbered-leg convention when no gutter segment on its run is at least 90 px long. + +### Trust boundaries + +A trust boundary is where an untrusted caller becomes a trusted one. It must be distinguishable from +ordinary containment **at a glance in the rendered raster**, not merely on inspection of the markup. +The palette is a single neutral token and decorative colour is forbidden, so the distinction is +carried by stroke pattern, stroke weight, and a crossing glyph. + +| Element | Class | Style | +|---------|-------|-------| +| Containment border | `stroke` | solid, 1.2 px | +| **Trust boundary** | `trust` | **dashed `8 4`, 2.0 px** | +| Trust-boundary label | `trust-lbl` | sans 11 px / 600, **upright** | +| Trust crossing glyph | `trust-cross` | open circle, `r="3.5"`, 1.2 px, `fill="none"` | + +```svg + +trust boundary — authenticated + +``` + +Rules: + +- The dash pattern `8 4` at 2.0 px is deliberately far from the `3 3` at 0.6 px used for dividers and + mount stems. The two must never be confusable at raster scale. +- The trust-boundary label sits **outside** the boundary, 8 px above its top-left corner, and is + **upright** — every other 11 px annotation in the visual language is italic, so upright weight-600 + reads as a different class of statement. +- **Every arrow that crosses a trust boundary carries a `trust-cross` glyph at the intersection + point.** This is the affordance that makes the boundary readable even when the boundary line itself + is partly occluded by nested content. An arrow crossing two boundaries carries two glyphs. +- A trust boundary may cut **across** containment — it is not required to align with any enclosure + edge, and the interesting cases are exactly the ones where it does not. When a boundary cuts + through the middle of an enclosure, draw it as an open path rather than a closed rect, and label + both sides. +- Name what changes, not the mechanism: `trust boundary — authenticated`, not `trust boundary — JWT`. + +## Naming and file conventions + +| Element | Convention | +|---------|-----------| +| File name | `kebab-case.svg` naming the topology depicted. Suffix `-topology` when the diagram's subject is the topology itself (`integration-test-topology.svg`). | +| Location | `doc/resources/diagrams/`, flat. | +| `` | One sentence naming **which** topology, including its environment. A deployment diagram that does not say which environment it depicts will be mistaken for a production recommendation. | +| `<desc>` | One paragraph. Must enumerate the members of every collapsed group and name every trust boundary. | +| Theme strategy | Strategy A (theme-neutral), recorded in the top-of-file comment. | + +## Render verification + +The shared Step 4 render check (`pm-documents:ref-svg-diagrams/SKILL.md` § Step 4) is mandatory and +blocking for this type as for every other: rasterise against both GitHub backgrounds and **read the +resulting PNGs back**. Deployment diagrams carry the highest stroke density of any type, so the +clipping, collision and legibility items on that checklist fail here more often than anywhere else. + +When no rasteriser is installed on the host, run one in a container. No host package, project +dependency or CI step is required: + +```bash +docker run --rm -v "/abs/path/to/repo:/w" -w /w alpine:3.20 sh -c \ + "apk add --no-cache rsvg-convert font-dejavu font-liberation && \ + rsvg-convert -b '#ffffff' -w 1200 -o .plan/temp/diagram-light.png doc/resources/diagrams/NAME.svg && \ + rsvg-convert -b '#0d1117' -w 1200 -o .plan/temp/diagram-dark.png doc/resources/diagrams/NAME.svg" +``` + +Two details in that invocation are load-bearing and are the two ways this recipe is most often got +wrong: + +1. **The package is `rsvg-convert`, not `librsvg`.** On Alpine the `librsvg` package ships only the + shared library; the CLI lives in its own package. `apk add librsvg` succeeds and the subsequent + `rsvg-convert` call then fails with `sh: rsvg-convert: not found`. +2. **The font packages are not optional.** A bare container has no fonts. `rsvg-convert` still exits + 0 and still writes a structurally valid PNG, but every glyph rasterises as a tofu box — and the + wider tofu glyphs overflow the `viewBox`, which reads as a clipping defect that is not there. + Without fonts the legibility, clipping and font-fallback items on the checklist cannot be + evaluated at all, while the command reports success. + +Verification runs **locally**. A rendered PNG is a verification artifact, not a deliverable: write it +under a scratch directory and do not commit it. + +## Annotated template + +The skeleton template at `doc/resources/templates/deployment-diagram-skeleton.svg` carries the +canonical `<style>` block, the `arrow` marker, the theme-neutral token, and placeholder content laid +out on this type's geometry. Copy it, rename it, and replace the placeholders. + +Every affordance specified above has a worked placeholder in the skeleton, so copy-rename-fill is the +whole workflow — an author does not need to read this document to produce a structurally valid +diagram: + +| Affordance | Placeholder in the skeleton | +|------------|-----------------------------| +| Containment nesting | `host` (depth 1, `rx="8"`, 44 px label band) containing `network` (depth 2, `rx="6"`, 28 px band) containing the components (depth 3, `rx="4"`), at the standard 16 px inset | +| Enclosure labels | Top-left on every box, with one optional `encl-sub` line | +| Trust boundary | Dashed `8 4` at 2.0 px running the full height of the network, with its upright weight-600 label above the top of the line | +| Trust crossings | Two `trust-cross` glyphs, one on each edge that crosses the boundary | +| Protocol + port edge labels | `HTTPS :8443` and `gRPC :9000` on single-segment edges, `TCP :5432` on an orthogonal cross-container edge, labelled on its longest segment | +| First-party vs external | Three boxes carry the `own-bar` left accent; the external component is the same box without it | +| Mounted material | Two pills on dashed stems under the first-party component, `certificates/` and `config/` | +| Collapsed group | `variant instances (N)` with the count in the label and the varying dimension in the sub-label | + +Placeholders the author does not need are **deleted, not left in place**. A skeleton affordance that +survives into a real diagram unedited is worse than an absent one: it reads as a claim about the +topology. + +The skeleton lives under `templates/` rather than `diagrams/` because `asciidoc-embedding.md` +requires `diagrams/` to remain a flat catalogue of actual diagrams, and a skeleton is not a diagram. + +## Reference implementation + +> **This section is project-specific.** Everything above it is the type specification and carries no +> assumption about any particular system. This section names one worked example. + +`doc/resources/diagrams/integration-test-topology.svg` is the canonical deployment diagram in the +API Sheriff documentation. It depicts the **integration-test** topology — the Docker Compose stack +the `integration-tests` module brings up — and demonstrates: + +- Three-level containment nesting: host → compose network → container. +- A real trust-boundary crossing at the SNI front listener, where a matched passthrough connection is + relayed at L4 without ever being terminated while every other connection is handed to the internal + terminating listener. +- Protocol-and-port edge labels traced line-by-line to `integration-tests/docker-compose.yml` and the + mounted `gateway.yaml`. +- Mounted material — the certificate, configuration and asset directories — as pills rather than + boxes. +- First-party gateway containers alongside external components (the identity provider and the test + upstreams). +- A collapsed group: the four variant gateway instances, which are the same image differing only by + overlaid configuration and published ports, rendered as one annotated group so the affordances + above stay legible. + +Its `<title>` and `<desc>` name it as the integration-test topology explicitly, so no reader mistakes +the test upstreams or the fault-injection proxy for a recommended production deployment. + +**Production and Kubernetes topologies are deliberately not drawn here.** They arrive with PLAN-27, +which defines them; drawing them before they are defined would document an intention as though it +were a fact. diff --git a/doc/development/integration-test-topology.adoc b/doc/development/integration-test-topology.adoc new file mode 100644 index 00000000..fc45712c --- /dev/null +++ b/doc/development/integration-test-topology.adoc @@ -0,0 +1,112 @@ += API Sheriff -- Integration-Test Topology +:toc: +:toclevels: 2 +:sectnums: + +The *contributor* view of the stack `integration-tests` brings up: which containers run, which +ports each one publishes, what talks to what, and where trust changes. It exists so a contributor +reading a failing integration test can see the shape of the thing under test without +reverse-engineering `integration-tests/docker-compose.yml`. + +[IMPORTANT] +==== +This is the *integration-test* topology. It is not a production deployment and not a recommended +one. Toxiproxy, go-httpbin, grpc-echo, the passthrough backend and the four variant gateway +instances exist to make specific tests possible. Production and Kubernetes topologies are defined by +PLAN-27 and are deliberately not drawn here or anywhere else yet. +==== + +== The Topology + +image::../resources/diagrams/integration-test-topology.svg[Integration-test topology: the Docker Compose stack used by the integration-tests module, align=center] + +The diagram is drawn to the link:diagram-type-deployment.md[deployment diagram-type standard]. Every +port, protocol and service name in it traces to a line in `integration-tests/docker-compose.yml` or +the mounted `integration-tests/src/main/docker/sheriff-config/gateway.yaml` -- nothing is inferred +and nothing is invented. Those two files are the authoritative sources; when they change, the +diagram is stale and gets redrawn rather than annotated. + +== Reading the Diagram + +=== Trust boundaries + +Two boundaries are drawn, both as heavy dashed lines with a small open circle at each crossing: + +* *Published host ports* -- the only surface reachable from outside the Compose network. Everything + below the line is internal, addressed by Compose service name. +* *Gateway egress* -- every outbound leg the gateway makes. The JWKS fetches are constrained by a + host-exact SSRF allowlist (`allowed_egress_hosts: ["keycloak"]`) and verified against a named TLS + trust profile rather than the JVM default store, because Keycloak serves a self-signed certificate + here. + +=== The primary gateway instance + +The `api-sheriff` container is drawn in full because it is the one instance that exercises the +accept-time SNI split: + +* the *SNI front listener* -- a plain-TCP Vert.x `NetServer` -- owns the public port `8443`; +* a matched passthrough SNI is relayed opaquely at L4 to `passthrough-backend` and is *never + terminated*, which is why `TlsPassthroughIT` observes the backend's own certificate through the + relay; +* every other connection is handed to the internal terminating Quarkus HTTPS listener on `8444`; +* the management interface is HTTPS-only on `9000`, published as `19000`. + +The front-listener architecture itself -- the ClientHello reassembly, the fail-closed selection +rule, and the internal-handoff loopback -- is documented in +link:tls-edge.adoc[TLS Edge -- Front Listener and the Accept-Time SNI Split]. + +=== The collapsed variant group + +Four further gateway instances run the *same* `api-sheriff:distroless` image and differ only by an +overlaid `gateway.yaml` and their published ports. Drawing all five in full would bury the +affordances the diagram exists to show, so they are collapsed into one annotated group that still +names every instance and every port pair: + +[cols="2,1,1,3"] +|=== +| Instance | App port | Mgmt port | Why it is separate + +| `api-sheriff-mtls` +| 10444 +| 19001 +| mTLS is a property of the single terminated listener, so it cannot coexist with the primary + instance's non-client-cert path. + +| `api-sheriff-cookie` +| 10445 +| 19002 +| Session mode is a property of the whole gateway; this instance runs the stateless sealed-cookie + mode. + +| `api-sheriff-cookie-2` +| 10446 +| 19003 +| A peer sharing nothing with `api-sheriff-cookie` but the sealing key, so cookie portability is + proved as a two-instance fact rather than an inference. + +| `api-sheriff-ws-admission` +| 10447 +| 19004 +| The admission budget is process-wide; this instance shrinks the caps to single digits so + permit exhaustion is reachable in a handful of connections. +|=== + +=== Mounted material + +Three read-only volumes are mounted into every gateway instance and are drawn as pills rather than +boxes, because they are inert material rather than components: `certificates/`, `sheriff-config/` +and `assets/`. The variant instances additionally overlay a single file -- +`sheriff-config-<variant>/gateway.yaml` on top of the shared `sheriff-config/` mount -- which is the +whole mechanism by which they differ. + +== Keeping the Diagram Honest + +The diagram is hand-authored SVG, so it does not regenerate itself and CI does not check it. Two +consequences: + +* *When `docker-compose.yml` or the mounted `gateway.yaml` changes, redraw the diagram in the same + change.* A stale topology diagram is worse than none, because it is believed. +* *Re-render and read back before committing.* The verification recipe -- including the two ways it + is commonly got wrong -- is in the + link:diagram-type-deployment.md[deployment diagram-type standard]. Rendered PNGs are verification + artifacts and are never committed. diff --git a/doc/resources/diagrams/integration-test-topology.svg b/doc/resources/diagrams/integration-test-topology.svg new file mode 100644 index 00000000..ea9b73db --- /dev/null +++ b/doc/resources/diagrams/integration-test-topology.svg @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Integration-test topology — the Docker Compose stack the integration-tests module + brings up. NOT a production or recommended deployment. + Every port, protocol and service name traces to integration-tests/docker-compose.yml + or integration-tests/src/main/docker/sheriff-config/gateway.yaml. + theme-handling: A (theme-neutral) + see doc/development/diagram-type-deployment.md +--> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 900" + role="img" aria-labelledby="title desc" + font-family="ui-sans-serif, -apple-system, system-ui, 'Segoe UI', sans-serif"> + <title id="title">API Sheriff integration-test topology, the Docker Compose stack used by the integration-tests module + The integration-test deployment topology, not a production deployment. A host runs a single Docker Compose bridge network named api-sheriff. Crossing from the host into the network are the published ports: the primary api-sheriff gateway on 10443 mapped to container 8443, and Keycloak on 1443 mapped to container 8443. Inside the gateway container a Vert.x SNI front listener owns the public port 8443; a matched passthrough SNI is relayed opaquely at layer 4 to the passthrough-backend and is never terminated, while every other connection is handed to the internal terminating Quarkus HTTPS listener on 8444. The management interface serves HTTPS only, on 9000. Three read-only volumes are mounted: certificates, sheriff-config and assets. The four variant gateway instances are collapsed into one annotated group; their members are api-sheriff-mtls on 10444 and 19001, api-sheriff-cookie on 10445 and 19002, api-sheriff-cookie-2 on 10446 and 19003, and api-sheriff-ws-admission on 10447 and 19004, each the same api-sheriff:distroless image with an overlaid gateway.yaml. Gateway egress crosses a second trust boundary governed by a host-exact SSRF allowlist and a named TLS trust profile, reaching Keycloak on 8443, the go-httpbin echo upstream on 8080, the asset-origin static origin on 80, and the in-repo grpc-echo upstream on 9000. Toxiproxy fronts the passthrough backend on its published admin port 8474 for mid-stream fault injection. Prometheus is published on 9090 and scrapes the management ports; k6 runs only under the benchmark compose profile. The management ports of the primary gateway, 19000, and of each variant instance are published but are not drawn as separate arrows; they are recorded in the boxes. + + + + + + + + + + + host — developer machine or CI runner + only published ports are reachable from outside + + + + + + + + trust boundary — published host ports + + + + network: api-sheriff (bridge) + + + + trust boundary — gateway egress + + + + HTTPS :10443 → :8443 + + + + HTTPS :1443 → :8443 + + + + + + api-sheriff + image api-sheriff:distroless — primary instance + + + + SNI front listener + :8443 public + Vert.x NetServer + + + terminated listener + :8444 internal + Quarkus HTTPS + + + management + :9000 HTTPS → 19000 + + matched passthrough SNI → L4 relay, + every other connection → :8444 + + route anchors: /proxy · /bff · /bff-session · /secure · /assets + /secure-assets · /graphql · /upload · /ws · /grpc + + + + + certificates/ + + + + sheriff-config/ + + + + assets/ + + + + + variant instances (4) + same image, overlaid gateway.yaml, own published ports + api-sheriff-mtls + api-sheriff-cookie + api-sheriff-cookie-2 + api-sheriff-ws-admission + 10444 · 19001 + 10445 · 19002 + 10446 · 19003 + 10447 · 19004 + + + + keycloak + :8443 → 1443 · :9000 → 1090 + + + go-httpbin + :8080 → 18080 + + + asset-origin + :80 · not published + + + + grpc-echo + :9000 · not published + + + + HTTPS :8443 + + + + HTTP :8080 + + + + HTTP :80 + + + + gRPC :9000 + + + + + prometheus + :9090 → 9090 + scrapes the management ports + + + k6 + benchmark + opt-in profile + + + + passthrough-backend + :8443 · self-signed cert + relayed, never terminated + + + toxiproxy + :8474 admin + fault injection + + + + TCP :8443 · L4 relay + + + + + TCP :8443 + + + Integration-test stack only. Toxiproxy, go-httpbin, grpc-echo and the variant instances exist to make tests possible and are not a production pattern. + diff --git a/doc/resources/templates/deployment-diagram-skeleton.svg b/doc/resources/templates/deployment-diagram-skeleton.svg new file mode 100644 index 00000000..99ac5f19 --- /dev/null +++ b/doc/resources/templates/deployment-diagram-skeleton.svg @@ -0,0 +1,129 @@ + + + + Deployment diagram skeleton, placeholder topology + A placeholder deployment topology used as an authoring skeleton. A host contains one network. Left of a trust boundary sits a first-party gateway with mounted certificate and configuration material, and a collapsed group standing in for N identically-shaped instances. Right of the boundary sit a first-party service and an external component. Two edges cross the trust boundary and are marked with crossing glyphs. Replace every placeholder label, and enumerate the members of any collapsed group in this description. + + + + + + + + + + + host + deployment target — replace with the host or cluster name + + + + network + + + + trust boundary — name what changes, not the mechanism + + + + + first-party component + the artifact this documentation owns + listener :8443 + management :9000 + + + + + certificates/ + + + + config/ + + + + + variant instances (N) + same image, varied configuration + :port-a :port-b :port-c + + + + + service + first-party + :8080 + + + + external component + not first-party + :5432 + + + + + + HTTPS :8443 + + + + + gRPC :9000 + + + + + TCP :5432 + + + Skeleton only. Replace every label, delete the affordances the topology does not use, and render on both backgrounds before committing. + From a930835e013a8c11a497c93d21302a573ab016d1 Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Thu, 30 Jul 2026 07:32:14 +0200 Subject: [PATCH 3/6] docs(diagrams): fix three internal inconsistencies in the deployment standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves three CodeRabbit findings on PR #132, all confined to doc/development/diagram-type-deployment.md. 1. Cross-reference convention (dbd3c3): the "Use a different diagram type when" list named five absent upstream siblings as bare filenames while the doc mandates skill-notation paths for absent siblings. All five now use the skill-notation form, so the graduation-time substitution stays the single mechanical rule the doc claims it is. (The reviewer's premise that these were broken links was wrong — they are inline code spans; the real defect was the convention inconsistency.) 2. Label band (357e04): the containment-nesting table stated the reserved band unconditionally as 28 px, contradicting the enclosure-labels section's 44 px band for a sub-labelled enclosure. An author following the table alone placed a child at parent_top + 28 and overlapped the sub-label. The rule is now conditional and the no-child constraint keys to the effective band. The dependent 120 x 48 rationale is aligned. 3. Corner radius (718ee3): the depth table mapped depth 3 to rx 6, but both the skeleton template and the reference implementation use rx="4" on every depth-3 component, so copy-rename-fill produced a diagram the table called nonconforming. The sequence 8, 6, 6, 4 also did not decrease between depth 2 and 3, contradicting the stated rationale. The table is now keyed to enclosure ROLE rather than raw depth, which is what the radius actually tracks and what lets both canonical ladders stay describable: a leaf container takes rx="4" whatever its depth. No committed SVG changes, so no re-rasterisation was required. Co-Authored-By: Claude Opus 5 (1M context) --- doc/development/diagram-type-deployment.md | 52 +++++++++++++--------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/doc/development/diagram-type-deployment.md b/doc/development/diagram-type-deployment.md index 3251f3b8..61e11a44 100644 --- a/doc/development/diagram-type-deployment.md +++ b/doc/development/diagram-type-deployment.md @@ -53,13 +53,16 @@ can reach it**. Specifically: Use a different diagram type when: -- The structure is **flow through stages** → use the flow type (`diagram-type-flow.md`). -- The structure is a **time-ordered exchange** → use the sequence type (`diagram-type-sequence.md`). +- The structure is **flow through stages** → use the flow type + (`pm-documents:ref-svg-diagrams/standards/diagram-type-flow.md`). +- The structure is a **time-ordered exchange** → use the sequence type + (`pm-documents:ref-svg-diagrams/standards/diagram-type-sequence.md`). - The structure is **producer / store / consumer** or a **side-by-side comparison** → use the block - type (`diagram-type-block.md`). -- The structure is **layers stacked on a substrate** → use the stack type (`diagram-type-stack.md`). + type (`pm-documents:ref-svg-diagrams/standards/diagram-type-block.md`). +- The structure is **layers stacked on a substrate** → use the stack type + (`pm-documents:ref-svg-diagrams/standards/diagram-type-stack.md`). - The structure is **hub-and-spoke relationships without containment** → use the graph type - (`diagram-type-graph.md`). + (`pm-documents:ref-svg-diagrams/standards/diagram-type-graph.md`). The discriminator is containment. If nothing in the picture is *inside* anything else, this is the wrong type. @@ -97,27 +100,36 @@ cluster ─▶ namespace ─▶ pod ─▶ container (orchestrated form) |------|-------| | Maximum nesting depth | **4 boxes**, counting the outermost enclosure as depth 1. | | Inset — child box edge to parent box inner edge | **16 px** on every side, at every level. | -| Label band — reserved strip at the top of every enclosure | **28 px**. No child box may start above `parent_top + 28`. | +| Label band — reserved strip at the top of every enclosure | **28 px** for a label-only enclosure, **44 px** when the enclosure carries an `encl-sub` sub-label. No child box may start above `parent_top +` the effective band. | | Minimum legible inner box | **120 × 48 px**. | -Corner radius decreases with depth, so nested corners read as concentric rather than parallel: +Corner radius steps down as the enclosure role gets more concrete, so nested corners read as +concentric rather than parallel. The radius is keyed to the **role**, not to the raw depth number, +because the two canonical ladders put different roles at the same depth — the compose ladder has a +container at depth 3 where the orchestrated ladder has a pod: -| Depth | `rx` / `ry` | -|-------|-------------| -| 1 — outermost enclosure (host, cluster) | 8 | -| 2 — network, namespace | 6 | -| 3 — pod, container group | 6 | -| 4 — container, process | 4 | +| Enclosure role | `rx` / `ry` | +|----------------|-------------| +| Outermost enclosure — host, cluster | 8 | +| Network, namespace | 6 | +| Everything the network contains — pod, container group, container, process | 4 | -Depth 4 is the floor because a box at depth 4 sits 48 px inside its outermost ancestor on each axis -and still has to hold a legible label. **When the topology needs a fifth level, split it into a -second diagram** rather than shrinking the type or the inset — a depth-5 box cannot satisfy the -120 × 48 px minimum inside any of the standard `viewBox` sizes. +A leaf container therefore takes `rx="4"` whatever its depth: depth 3 in the compose ladder, depth 4 +in the orchestrated one. The innermost tier is flat at 4 rather than continuing to decrease because +4 px is the smallest radius that still reads as a rounded corner at raster scale — a pod at 4 and its +contained container at 4 are the one place two nested corners share a radius, and the 16 px inset +keeps them from reading as parallel. + +Depth 4 is the nesting floor because a box at depth 4 sits 48 px inside its outermost ancestor on +each axis and still has to hold a legible label. **When the topology needs a fifth level, split it +into a second diagram** rather than shrinking the type or the inset — a depth-5 box cannot satisfy +the 120 × 48 px minimum inside any of the standard `viewBox` sizes. The 120 × 48 px minimum is what a box needs to hold a 13 px label plus one 11 px sub-label with the -standard 12 px left inset and the 28 px label band. **A box that would fall below the minimum is not -drawn smaller — it is collapsed**: replace the group of undersized boxes with one box carrying a -count and an annotation naming what was collapsed (see § Collapsed groups). +standard 12 px left inset and the 44 px label band that a sub-label requires. **A box that would +fall below the minimum is not drawn smaller — it is collapsed**: replace the group of undersized +boxes with one box carrying a count and an annotation naming what was collapsed (see § Collapsed +groups). ### Enclosure labels From 15571128a65690014876f7857a17b4cc938fec40 Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:18:23 +0200 Subject: [PATCH 4/6] docs(diagrams): use ladder-neutral wording for the innermost radius row The row label read 'Everything the network contains', which is compose-ladder framing and contradicts the ladder-neutral rationale two lines above: in the orchestrated ladder (cluster to namespace to pod to container) a pod is contained by the namespace, not the network. 'Nested workload roles' reads correctly against both canonical ladders. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EkMQfbbBESLRHEch8ZuUWe --- doc/development/diagram-type-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/diagram-type-deployment.md b/doc/development/diagram-type-deployment.md index 61e11a44..2705ec32 100644 --- a/doc/development/diagram-type-deployment.md +++ b/doc/development/diagram-type-deployment.md @@ -112,7 +112,7 @@ container at depth 3 where the orchestrated ladder has a pod: |----------------|-------------| | Outermost enclosure — host, cluster | 8 | | Network, namespace | 6 | -| Everything the network contains — pod, container group, container, process | 4 | +| Nested workload roles — pod, container group, container, process | 4 | A leaf container therefore takes `rx="4"` whatever its depth: depth 3 in the compose ladder, depth 4 in the orchestrated one. The innermost tier is flat at 4 rather than continuing to decrease because From 06979aec2f09f5890e159d8f4e1f2299aeb025b4 Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Thu, 30 Jul 2026 10:59:38 +0200 Subject: [PATCH 5/6] chore(finalize): apply finalize-step-preference-emitter changes Promotes one recurring disposition pattern from this plan to a durable architecture hint on the documentation module: review suggestions that contradict an already-audited decision are declined with a citation rather than reversing the audit. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EkMQfbbBESLRHEch8ZuUWe --- .plan/project-architecture/documentation/enriched.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.plan/project-architecture/documentation/enriched.json b/.plan/project-architecture/documentation/enriched.json index 88f61c28..5bbbfcca 100644 --- a/.plan/project-architecture/documentation/enriched.json +++ b/.plan/project-architecture/documentation/enriched.json @@ -1,5 +1,7 @@ { - "best_practices": [], + "best_practices": [ + "When an automated review suggestion contradicts a decision this plan already audited and recorded, decline the suggestion and cite the audit rather than reversing it. Documentation standards in this repo carry deliberate, recorded choices (format outliers kept for upstream-graft portability, verification obligations deliberately left local rather than CI-gated); a reviewer seeing only the file will read those as defects. Treat re-litigation of a settled decision as a standing consideration, not a finding." + ], "insights": [], "internal_dependencies": [], "key_dependencies": [], From 98657947d31df9c5a7d0292eac4f8aa1d1a898d3 Mon Sep 17 00:00:00 2001 From: cuioss oliver <23139298+cuioss@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:18:18 +0200 Subject: [PATCH 6/6] docs(diagrams): justify the depth-4 nesting floor by legibility, not geometry The standard claimed a depth-5 box cannot satisfy the 120x48 px minimum inside any standard viewBox. That is false: in the smallest viewBox (1000x620) a depth-5 box still has roughly 820x330 px after the 24 px outer margin, four 16 px insets and four worst-case 44 px label bands. Depth 4 remains the limit, now with the accurate rationale: the corner-radius ladder has only three steps (8/6/4), so depth 4 already shares depth 3's leaf radius and a fifth level would carry no distinguishing radius at all. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EkMQfbbBESLRHEch8ZuUWe --- doc/development/diagram-type-deployment.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/development/diagram-type-deployment.md b/doc/development/diagram-type-deployment.md index 2705ec32..d268db83 100644 --- a/doc/development/diagram-type-deployment.md +++ b/doc/development/diagram-type-deployment.md @@ -120,10 +120,15 @@ in the orchestrated one. The innermost tier is flat at 4 rather than continuing contained container at 4 are the one place two nested corners share a radius, and the 16 px inset keeps them from reading as parallel. -Depth 4 is the nesting floor because a box at depth 4 sits 48 px inside its outermost ancestor on -each axis and still has to hold a legible label. **When the topology needs a fifth level, split it -into a second diagram** rather than shrinking the type or the inset — a depth-5 box cannot satisfy -the 120 × 48 px minimum inside any of the standard `viewBox` sizes. +Depth 4 is the nesting floor, and the reason is legibility rather than geometry. Space is not the +constraint: in the smallest `viewBox` (`0 0 1000 620`) a depth-5 box still has roughly 820 × 330 px +after the 24 px outer margin, four 16 px insets and four 44 px label bands, so it clears the +120 × 48 px minimum with room to spare. What runs out is the reader's ability to track containment +and the radius ladder that signals it: the ladder has only three steps (8 / 6 / 4), so depth 4 +already shares its radius with depth 3's leaves, and a fifth level would carry no distinguishing +radius at all — five nested strokes in one neutral colour read as a pattern rather than a hierarchy. +**When the topology needs a fifth level, split it into a second diagram** rather than shrinking the +type or the inset. The 120 × 48 px minimum is what a box needs to hold a 13 px label plus one 11 px sub-label with the standard 12 px left inset and the 44 px label band that a sub-label requires. **A box that would