aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-22 14:54:44 -0500
committerCraig Jennings <c@cjennings.net>2026-05-22 14:54:44 -0500
commit94f26e020193a61c703f30742afd23fc01a85178 (patch)
tree0f9ce0683f822510fb0ecb7c9bbba78c33724368
parent7c66d682c628f7e74198ef423288a77b042f3e04 (diff)
downloadrulesets-94f26e020193a61c703f30742afd23fc01a85178.tar.gz
rulesets-94f26e020193a61c703f30742afd23fc01a85178.zip
docs(commands): make c4 skills notation-independent and level-consistent
Two audit fixes to c4-analyze and c4-diagram. Both treated draw.io as the only output. They now offer draw.io, Structurizr DSL, Mermaid (native C4 types), and PlantUML, with a headless fallback that emits a text notation instead of failing when drawio or a GUI is absent. Both also gained an abstraction-boundary section (a Container is a deployable unit, not a Docker container, and a Component isn't separately deployable) and a check that every element and relationship stays at the diagram's single C4 level.
-rw-r--r--.claude/commands/c4-analyze.md41
-rw-r--r--.claude/commands/c4-diagram.md39
2 files changed, 70 insertions, 10 deletions
diff --git a/.claude/commands/c4-analyze.md b/.claude/commands/c4-analyze.md
index d0e5f69..f9565b4 100644
--- a/.claude/commands/c4-analyze.md
+++ b/.claude/commands/c4-analyze.md
@@ -73,9 +73,31 @@ For each element, capture:
- **Description**: One sentence about its responsibility
- **Relationships**: What it talks to, how (protocol), and why (purpose)
+### Abstraction boundaries (get these right before drawing)
+
+The two interior C4 levels are easy to confuse. Pin them down:
+
+- A **Container** is a separately deployable or runnable unit — a server-side web app, a single-page application, a desktop or mobile app, a serverless function, a database, a message broker, a file store. The test is "does it run as its own process / deployable artifact?" A Container is **not** the same thing as a Docker container; a Docker image often holds one C4 Container, but the C4 abstraction is about the deployable unit, not the packaging technology. The `Dockerfile` / k8s / serverless signals in Step 1 are useful evidence, but a SPA bundle or a managed database is a Container too even though no `Dockerfile` names it.
+- A **Component** lives *inside* a single Container — a grouping of code (a controller, a service, a repository, a module) behind a clear interface. A Component is **not** separately deployable; it ships and runs as part of its Container. If something can be deployed or started on its own, it is a Container, not a Component.
+
## Step 3: Generate Diagrams
-Generate diagrams as **draw.io XML** (`.drawio` files). Always generate Level 1 and Level 2.
+The C4 model is notation-independent — it defines the *abstractions* (people, systems, containers, components) and the levels, not a file format. Pick the output notation that fits the environment. Always generate Level 1 and Level 2.
+
+### Choosing a notation
+
+Ask the user, or infer from context:
+
+- **draw.io XML** (`.drawio`) — editable diagrams, GUI refinement, PNG export. The default when `drawio` and a desktop session are available. Detailed format below.
+- **Structurizr DSL** (`.dsl`) — define the model once, render every level as a view. The notation closest to the C4 model itself; ideal when the diagrams are checked into a repo as text.
+- **Mermaid** (`.mmd` / fenced ```` ```mermaid ````) — native `C4Context`, `C4Container`, `C4Component`, and `C4Dynamic` diagram types. Renders in GitHub, GitLab, and most Markdown viewers without a separate tool. The best choice for diagrams that live in a README or PR.
+- **PlantUML with C4-PlantUML** (`.puml`) — `!include` the C4-PlantUML macros (`C4_Context.puml`, `C4_Container.puml`, `C4_Component.puml`) and use `Person()`, `System()`, `Container()`, `Component()`, `Rel()`. Renders via a PlantUML server or CLI.
+
+### Fallback when no GUI or `drawio` is available
+
+If `drawio` is not on `PATH`, or the session has no display (headless / SSH / CI), do **not** fail. Emit a text-based notation the user can render elsewhere — Mermaid or Structurizr DSL by default, since both render without a desktop app. Write the source file, tell the user which notation it is and how to render it (paste into a Mermaid viewer, push to GitHub, run through the Structurizr CLI), and skip the PNG-export and desktop-open steps. The model and the levels are the deliverable; the renderer is interchangeable.
+
+The remaining format and layout detail below is specific to draw.io XML. The model built in Step 2, the abstraction-boundary rules, and the don't-mix-levels consistency check apply to every notation.
### draw.io XML format
@@ -186,14 +208,23 @@ Labels on parallel edges will also overlap if the edges are close. Since labels
Check that no edge or edge label passes through or overlaps the boundary's swimlane header (the `startSize` area, typically 30px tall at the top of the boundary). If edges enter the boundary from above, they should connect at the boundary top edge, not route through the title.
+### 4e. Verify single abstraction level (notation-independent)
+
+Every element and every relationship in one diagram must sit at the same C4 level. A Container diagram contains containers (plus the people and external systems around the boundary for context); it must not slip a Component-level box inside the boundary. A Component diagram contains components within one container; it must not promote a sibling container to a peer inside the same boundary.
+
+Walk every element and confirm its level matches the diagram's level. Walk every relationship and confirm both endpoints are elements that belong on this diagram (a level-matched element, or an in-context person / external system / neighboring container). If anything is off-level — a class shown where a component belongs, a component shown where a container belongs — either remove it or move it to the correct diagram. This check applies to every notation, not just draw.io.
+
## Step 5: Output
-1. **Save the draw.io XML** to the project root:
- - Filenames: `system-context.drawio`, `containers.drawio`, `components-[container-name].drawio`
+1. **Save the diagram source** to the project root, with an extension matching the chosen notation:
+ - draw.io: `system-context.drawio`, `containers.drawio`, `components-[container-name].drawio`
+ - Structurizr DSL: `workspace.dsl` (one file defines the model and every view)
+ - Mermaid: `system-context.mmd`, `containers.mmd`, `components-[container-name].mmd`
+ - PlantUML: `system-context.puml`, `containers.puml`, `components-[container-name].puml`
-2. **Export to PNG** by running `drawio --export --format png --output <name>.png <name>.drawio` via Bash.
+2. **(draw.io only) Export to PNG** by running `drawio --export --format png --output <name>.png <name>.drawio` via Bash. Skip if `drawio` is unavailable or the session is headless — the text source is already the deliverable.
-3. **Open in draw.io desktop** by running `drawio <filepath>` via Bash.
+3. **(draw.io only) Open in draw.io desktop** by running `drawio <filepath>` via Bash. Skip when there is no GUI. For text notations, instead tell the user how to render the source (Mermaid viewer / GitHub, Structurizr CLI, PlantUML server).
4. **Print a summary** of what was discovered:
- Number of containers, components, external systems, and user types identified
diff --git a/.claude/commands/c4-diagram.md b/.claude/commands/c4-diagram.md
index 07519ed..47716d9 100644
--- a/.claude/commands/c4-diagram.md
+++ b/.claude/commands/c4-diagram.md
@@ -52,13 +52,33 @@ For each element, define:
- **Description**: One sentence about its responsibility
- **Relationships**: What connects to what, the purpose, and the mechanism
+The two interior C4 levels are easy to confuse. Pin them down:
+
+- A **Container** is a separately deployable or runnable unit — a server-side web app, a single-page application, a desktop or mobile app, a serverless function, a database, a message broker, a file store. The test is "does it run as its own process / deployable artifact?" A Container is **not** the same thing as a Docker container; a Docker image often holds one C4 Container, but the C4 abstraction is about the deployable unit, not the packaging technology.
+- A **Component** lives *inside* a single Container — a grouping of code (a controller, a service, a repository, a module) behind a clear interface. A Component is **not** separately deployable; it ships and runs as part of its Container. If something can be deployed or started on its own, it is a Container, not a Component.
+
### Phase 3: Generate Diagrams
-Generate diagrams as **draw.io XML** (`.drawio` files).
+The C4 model is notation-independent — it defines the *abstractions* (people, systems, containers, components) and the levels, not a file format. Pick the output notation that fits the environment.
Default behavior: generate Level 1 (System Context) and Level 2 (Container).
Generate Level 3 (Component) only when the user asks to drill into a specific container.
+#### Choosing a notation
+
+Ask the user, or infer from context:
+
+- **draw.io XML** (`.drawio`) — editable diagrams, GUI refinement, PNG export. The default when `drawio` and a desktop session are available. Detailed format below.
+- **Structurizr DSL** (`.dsl`) — define the model once, render every level as a view. The notation closest to the C4 model itself; ideal when the diagrams are checked into a repo as text.
+- **Mermaid** (`.mmd` / fenced ```` ```mermaid ````) — native `C4Context`, `C4Container`, `C4Component`, and `C4Dynamic` diagram types. Renders in GitHub, GitLab, and most Markdown viewers without a separate tool. The best choice for diagrams that live in a README or PR.
+- **PlantUML with C4-PlantUML** (`.puml`) — `!include` the C4-PlantUML macros (`C4_Context.puml`, `C4_Container.puml`, `C4_Component.puml`) and use `Person()`, `System()`, `Container()`, `Component()`, `Rel()`. Renders via a PlantUML server or CLI.
+
+#### Fallback when no GUI or `drawio` is available
+
+If `drawio` is not on `PATH`, or the session has no display (headless / SSH / CI), do **not** fail. Emit a text-based notation the user can render elsewhere — Mermaid or Structurizr DSL by default, since both render without a desktop app. Write the source file, tell the user which notation it is and how to render it (paste into a Mermaid viewer, push to GitHub, run through the Structurizr CLI), and skip the PNG-export and desktop-open steps. The model and the levels are the deliverable; the renderer is interchangeable.
+
+The remaining format and layout detail below is specific to draw.io XML. The model built in Phase 2, the abstraction-boundary rules, and the don't-mix-levels consistency check apply to every notation.
+
#### draw.io XML format
Use the `<mxfile><diagram><mxGraphModel>` structure with `<mxCell>` elements.
@@ -172,14 +192,23 @@ Labels on parallel edges will also overlap if the edges are close. Since labels
Check that no edge or edge label passes through or overlaps the boundary's swimlane header (the `startSize` area, typically 30px tall at the top of the boundary). If edges enter the boundary from above, they should connect at the boundary top edge, not route through the title.
+#### 4e. Verify single abstraction level (notation-independent)
+
+Every element and every relationship in one diagram must sit at the same C4 level. A Container diagram contains containers (plus the people and external systems around the boundary for context); it must not slip a Component-level box inside the boundary. A Component diagram contains components within one container; it must not promote a sibling container to a peer inside the same boundary.
+
+Walk every element and confirm its level matches the diagram's level. Walk every relationship and confirm both endpoints are elements that belong on this diagram (a level-matched element, or an in-context person / external system / neighboring container). If anything is off-level — a class shown where a component belongs, a component shown where a container belongs — either remove it or move it to the correct diagram. This check applies to every notation, not just draw.io.
+
### Phase 5: Output
-1. **Save the draw.io XML** to the project root (or current directory):
- - Filenames: `system-context.drawio`, `containers.drawio`, `components-[name].drawio`
+1. **Save the diagram source** to the project root (or current directory), with an extension matching the chosen notation:
+ - draw.io: `system-context.drawio`, `containers.drawio`, `components-[name].drawio`
+ - Structurizr DSL: `workspace.dsl` (one file defines the model and every view)
+ - Mermaid: `system-context.mmd`, `containers.mmd`, `components-[name].mmd`
+ - PlantUML: `system-context.puml`, `containers.puml`, `components-[name].puml`
-2. **Export to PNG** by running `drawio --export --format png --output <name>.png <name>.drawio` via Bash.
+2. **(draw.io only) Export to PNG** by running `drawio --export --format png --output <name>.png <name>.drawio` via Bash. Skip if `drawio` is unavailable or the session is headless — the text source is already the deliverable.
-3. **Open in draw.io desktop** by running `drawio <filepath>` via Bash.
+3. **(draw.io only) Open in draw.io desktop** by running `drawio <filepath>` via Bash. Skip when there is no GUI. For text notations, instead tell the user how to render the source (Mermaid viewer / GitHub, Structurizr CLI, PlantUML server).
4. **Offer next steps:**
- "Want me to zoom into any container for a component diagram?"