From ad7d109a5f30bc9fbb6e19cd424794d93815078b Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola Date: Sat, 25 Apr 2026 12:03:51 +0200 Subject: [PATCH 01/11] feat(plantuml): scaffold plugin with inherited authoring/convert skills --- .claude-plugin/marketplace.json | 8 + plantuml/.claude-plugin/plugin.json | 11 + plantuml/README.md | 35 ++++ plantuml/skills/plantuml-authoring/SKILL.md | 149 +++++++++++++ .../plantuml-authoring/diagrams/INDEX.md | 59 ++++++ .../plantuml-authoring/diagrams/activity.md | 63 ++++++ .../plantuml-authoring/diagrams/archimate.md | 59 ++++++ .../skills/plantuml-authoring/diagrams/c4.md | 70 +++++++ .../plantuml-authoring/diagrams/class.md | 79 +++++++ .../diagrams/communication.md | 99 +++++++++ .../plantuml-authoring/diagrams/component.md | 64 ++++++ .../diagrams/composite-structure.md | 90 ++++++++ .../plantuml-authoring/diagrams/deployment.md | 66 ++++++ .../skills/plantuml-authoring/diagrams/er.md | 73 +++++++ .../plantuml-authoring/diagrams/gantt.md | 66 ++++++ .../diagrams/interaction-overview.md | 111 ++++++++++ .../plantuml-authoring/diagrams/json-yaml.md | 100 +++++++++ .../plantuml-authoring/diagrams/mindmap.md | 84 ++++++++ .../diagrams/nwdiag-family.md | 55 +++++ .../plantuml-authoring/diagrams/object.md | 70 +++++++ .../plantuml-authoring/diagrams/package.md | 72 +++++++ .../plantuml-authoring/diagrams/profile.md | 75 +++++++ .../plantuml-authoring/diagrams/salt.md | 69 +++++++ .../plantuml-authoring/diagrams/sequence.md | 80 +++++++ .../plantuml-authoring/diagrams/state.md | 86 ++++++++ .../plantuml-authoring/diagrams/timing.md | 62 ++++++ .../plantuml-authoring/diagrams/usecase.md | 66 ++++++ .../skills/plantuml-authoring/diagrams/wbs.md | 57 +++++ .../skills/plantuml-authoring/principles.md | 184 +++++++++++++++++ .../plantuml-authoring/project-config.md | 195 ++++++++++++++++++ .../plantuml-authoring/render-profiles.md | 103 +++++++++ .../scripts/aggregate-reviews.sh | 94 +++++++++ .../scripts/generate-variants.sh | 47 +++++ .../scripts/render-variants.sh | 45 ++++ .../scripts/run-test-suite.sh | 27 +++ .../scripts/seed-variants.sh | 56 +++++ .../plantuml-authoring/templates/_base.puml | 16 ++ .../plantuml-authoring/templates/_brand.puml | 8 + .../plantuml-authoring/templates/_fonts.puml | 4 + .../plantuml-authoring/templates/_layout.puml | 13 ++ .../templates/_targets/docx.puml | 5 + .../templates/_targets/pdf.puml | 4 + .../templates/_targets/pptx.puml | 5 + .../templates/_targets/web.puml | 4 + .../plantuml-authoring/templates/_theme.puml | 172 +++++++++++++++ .../test-variants/activity/.plantuml | 1 + .../test-variants/activity/detailed.puml | 46 +++++ .../test-variants/activity/minimal.puml | 12 ++ .../test-variants/activity/standard.puml | 22 ++ .../test-variants/archimate/.plantuml | 1 + .../test-variants/archimate/detailed.puml | 56 +++++ .../test-variants/archimate/minimal.puml | 11 + .../test-variants/archimate/standard.puml | 18 ++ .../test-variants/c4/.plantuml | 1 + .../test-variants/c4/detailed.puml | 49 +++++ .../test-variants/c4/minimal.puml | 13 ++ .../test-variants/c4/standard.puml | 22 ++ .../test-variants/class/.plantuml | 1 + .../test-variants/class/detailed.puml | 70 +++++++ .../test-variants/class/minimal.puml | 10 + .../test-variants/class/standard.puml | 27 +++ .../test-variants/communication/.plantuml | 1 + .../test-variants/communication/detailed.puml | 28 +++ .../test-variants/communication/minimal.puml | 15 ++ .../test-variants/communication/standard.puml | 24 +++ .../test-variants/component/.plantuml | 1 + .../test-variants/component/detailed.puml | 51 +++++ .../test-variants/component/minimal.puml | 10 + .../test-variants/component/standard.puml | 19 ++ .../composite-structure/.plantuml | 1 + .../composite-structure/detailed.puml | 31 +++ .../composite-structure/minimal.puml | 16 ++ .../composite-structure/standard.puml | 23 +++ .../test-variants/deployment/.plantuml | 1 + .../test-variants/deployment/detailed.puml | 55 +++++ .../test-variants/deployment/minimal.puml | 12 ++ .../test-variants/deployment/standard.puml | 21 ++ .../test-variants/er/.plantuml | 1 + .../test-variants/er/detailed.puml | 87 ++++++++ .../test-variants/er/minimal.puml | 15 ++ .../test-variants/er/standard.puml | 30 +++ .../test-variants/gantt/.plantuml | 1 + .../test-variants/gantt/detailed.puml | 39 ++++ .../test-variants/gantt/minimal.puml | 11 + .../test-variants/gantt/standard.puml | 23 +++ .../interaction-overview/.plantuml | 1 + .../interaction-overview/detailed.puml | 35 ++++ .../interaction-overview/minimal.puml | 13 ++ .../interaction-overview/standard.puml | 22 ++ .../test-variants/json-yaml/.plantuml | 1 + .../test-variants/json-yaml/detailed.puml | 66 ++++++ .../test-variants/json-yaml/minimal.puml | 10 + .../test-variants/json-yaml/standard.puml | 18 ++ .../test-variants/mindmap/.plantuml | 1 + .../test-variants/mindmap/detailed.puml | 50 +++++ .../test-variants/mindmap/minimal.puml | 10 + .../test-variants/mindmap/standard.puml | 22 ++ .../test-variants/nwdiag-family/.plantuml | 1 + .../test-variants/nwdiag-family/detailed.puml | 31 +++ .../test-variants/nwdiag-family/minimal.puml | 9 + .../test-variants/nwdiag-family/standard.puml | 18 ++ .../test-variants/object/.plantuml | 1 + .../test-variants/object/detailed.puml | 65 ++++++ .../test-variants/object/minimal.puml | 10 + .../test-variants/object/standard.puml | 28 +++ .../test-variants/package/.plantuml | 1 + .../test-variants/package/detailed.puml | 60 ++++++ .../test-variants/package/minimal.puml | 12 ++ .../test-variants/package/standard.puml | 29 +++ .../test-variants/profile/.plantuml | 1 + .../test-variants/profile/detailed.puml | 31 +++ .../test-variants/profile/minimal.puml | 12 ++ .../test-variants/profile/standard.puml | 20 ++ .../test-variants/salt/.plantuml | 1 + .../test-variants/salt/detailed.puml | 41 ++++ .../test-variants/salt/minimal.puml | 13 ++ .../test-variants/salt/standard.puml | 15 ++ .../test-variants/sequence/.plantuml | 1 + .../test-variants/sequence/detailed.puml | 73 +++++++ .../test-variants/sequence/minimal.puml | 14 ++ .../test-variants/sequence/standard.puml | 31 +++ .../test-variants/state/.plantuml | 1 + .../test-variants/state/detailed.puml | 54 +++++ .../test-variants/state/minimal.puml | 10 + .../test-variants/state/standard.puml | 15 ++ .../test-variants/timing/.plantuml | 1 + .../test-variants/timing/detailed.puml | 39 ++++ .../test-variants/timing/minimal.puml | 14 ++ .../test-variants/timing/standard.puml | 24 +++ .../test-variants/usecase/.plantuml | 1 + .../test-variants/usecase/detailed.puml | 59 ++++++ .../test-variants/usecase/minimal.puml | 16 ++ .../test-variants/usecase/standard.puml | 25 +++ .../test-variants/wbs/.plantuml | 1 + .../test-variants/wbs/detailed.puml | 39 ++++ .../test-variants/wbs/minimal.puml | 10 + .../test-variants/wbs/standard.puml | 21 ++ plantuml/skills/plantuml-convert/SKILL.md | 104 ++++++++++ 138 files changed, 5037 insertions(+) create mode 100644 plantuml/.claude-plugin/plugin.json create mode 100644 plantuml/README.md create mode 100644 plantuml/skills/plantuml-authoring/SKILL.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/INDEX.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/activity.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/archimate.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/c4.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/class.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/communication.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/component.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/composite-structure.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/deployment.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/er.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/gantt.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/interaction-overview.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/json-yaml.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/mindmap.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/nwdiag-family.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/object.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/package.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/profile.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/salt.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/sequence.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/state.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/timing.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/usecase.md create mode 100644 plantuml/skills/plantuml-authoring/diagrams/wbs.md create mode 100644 plantuml/skills/plantuml-authoring/principles.md create mode 100644 plantuml/skills/plantuml-authoring/project-config.md create mode 100644 plantuml/skills/plantuml-authoring/render-profiles.md create mode 100755 plantuml/skills/plantuml-authoring/scripts/aggregate-reviews.sh create mode 100755 plantuml/skills/plantuml-authoring/scripts/generate-variants.sh create mode 100755 plantuml/skills/plantuml-authoring/scripts/render-variants.sh create mode 100755 plantuml/skills/plantuml-authoring/scripts/run-test-suite.sh create mode 100755 plantuml/skills/plantuml-authoring/scripts/seed-variants.sh create mode 100644 plantuml/skills/plantuml-authoring/templates/_base.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_brand.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_fonts.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_layout.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_targets/docx.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_targets/pdf.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_targets/pptx.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_targets/web.puml create mode 100644 plantuml/skills/plantuml-authoring/templates/_theme.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/activity/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/activity/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/activity/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/activity/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/archimate/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/archimate/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/archimate/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/archimate/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/c4/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/c4/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/c4/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/c4/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/class/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/class/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/class/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/class/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/communication/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/communication/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/communication/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/communication/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/component/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/component/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/component/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/component/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/composite-structure/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/composite-structure/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/composite-structure/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/composite-structure/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/deployment/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/deployment/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/deployment/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/deployment/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/er/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/er/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/er/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/er/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/gantt/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/gantt/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/gantt/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/gantt/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/interaction-overview/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/interaction-overview/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/interaction-overview/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/interaction-overview/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/json-yaml/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/json-yaml/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/json-yaml/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/json-yaml/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/mindmap/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/mindmap/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/mindmap/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/mindmap/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/object/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/object/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/object/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/object/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/package/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/package/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/package/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/package/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/profile/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/profile/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/profile/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/profile/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/salt/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/salt/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/salt/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/salt/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/sequence/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/sequence/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/sequence/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/sequence/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/state/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/state/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/state/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/state/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/timing/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/timing/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/timing/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/timing/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/usecase/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/usecase/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/usecase/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/usecase/standard.puml create mode 120000 plantuml/skills/plantuml-authoring/test-variants/wbs/.plantuml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/wbs/detailed.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/wbs/minimal.puml create mode 100644 plantuml/skills/plantuml-authoring/test-variants/wbs/standard.puml create mode 100644 plantuml/skills/plantuml-convert/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 7062ba9..ee5f161 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -34,6 +34,14 @@ "category": "engineering", "tags": ["kaizen", "improvement", "optimization", "automation", "autoresearch", "kpi", "refactoring"] }, + { + "name": "plantuml", + "source": "./plantuml", + "description": "Authoring, rendering, and maintenance of PlantUML diagrams — policy-driven projects with multi-target rendering, lint, validate, review, advisor, and migrate", + "version": "1.0.0", + "category": "documentation", + "tags": ["plantuml", "diagrams", "uml", "c4", "documentation", "rendering", "lint"] + }, { "name": "project-management", "source": "./project-management", diff --git a/plantuml/.claude-plugin/plugin.json b/plantuml/.claude-plugin/plugin.json new file mode 100644 index 0000000..267afa7 --- /dev/null +++ b/plantuml/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "plantuml", + "version": "1.0.0", + "description": "Authoring, rendering, and maintenance of PlantUML diagrams — policy-driven projects with multi-target rendering, lint, validate, review, advisor, and migrate", + "author": { + "name": "MrBogomips", + "url": "https://github.com/MrBogomips" + }, + "license": "MIT", + "keywords": ["plantuml", "diagrams", "uml", "c4", "documentation", "rendering", "lint"] +} diff --git a/plantuml/README.md b/plantuml/README.md new file mode 100644 index 0000000..6e29d99 --- /dev/null +++ b/plantuml/README.md @@ -0,0 +1,35 @@ +# plantuml + +Authoring, rendering, and maintenance of PlantUML diagrams in Claude Code projects. Policy-driven theming, multi-target output (web/docx/pdf/pptx), and a maintenance layer for lint, validate, review, advisor, and migration. + +## Requirements + +- `plantuml` CLI — `brew install plantuml` (macOS) or `apt install plantuml` (Debian/Ubuntu) +- `java` — pulled transitively by the plantuml package +- `jq` — required by the marketplace structural tests + +## Install + +Add the marketplace and install the plugin via Claude Code's plugin manager. + +## Quickstart + +1. Run `/plantuml-init` in your project root to create a `## PlantUML Policy` in `CLAUDE.md` and materialize `.plantuml/`. +2. Author `.puml` files — the `plantuml-authoring` skill activates on intent. +3. Maintain: `plantuml-lint`, `plantuml-validate`, `plantuml-review`, `plantuml-advisor`, `plantuml-migrate`. + +## Components + +- **Skills:** `plantuml-authoring`, `plantuml-convert`, `plantuml-bootstrap`, `plantuml-lint`, `plantuml-validate`, `plantuml-review`, `plantuml-advisor`, `plantuml-migrate` +- **Agents:** `puml-linter`, `puml-renderer`, `puml-migrator`, `puml-visual-checker` +- **Commands:** `/plantuml-init` + +See each skill's `SKILL.md` for usage details. + +## Verifying install + +Run `bash tests/ci/run-structural-tests.sh` from the marketplace root. All suites should pass. + +## Dev + +The plugin is the source of truth for all PlantUML evolution. Edits go directly under `plantuml/`. diff --git a/plantuml/skills/plantuml-authoring/SKILL.md b/plantuml/skills/plantuml-authoring/SKILL.md new file mode 100644 index 0000000..feb6f2f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/SKILL.md @@ -0,0 +1,149 @@ +--- +name: plantuml-authoring +description: Author PlantUML diagrams (UML, C4, ER, ArchiMate, MindMap, WBS, + Gantt, Salt, JSON/YAML, nwdiag family). Use when creating, restructuring, or + choosing a type for a .puml/.plantuml/.iuml file, when setting up PlantUML + project configuration, or when adapting diagrams to a render target + (web/docx/pdf/pptx). For rendering to image, use plantuml-convert. +allowed-tools: Read, Write, Edit, Glob, Grep, Bash +--- + +# PlantUML Authoring + +Progressive disclosure router. Do NOT read all sub-files by default. +Decide which sub-files to load based on the routing rules below. + +## Routing + +1. **Project has no `.plantuml/` AND no "PlantUML Policy" section in + CLAUDE.md?** + → Read `project-config.md` and run the bootstrap dialog. + +2. **Need to choose a diagram type or generate one?** + → Read `principles.md` (once, ~200 lines; applies to every type). + → If type is already picked: read only `diagrams/.md`. + → If not: read `diagrams/INDEX.md` first to pick, then the type file. + +3. **Rendering requested (any target)?** + → Read `render-profiles.md`. + → Compose the `plantuml-convert` invocation with `PLANTUML_TARGET` env var. + +4. **Refactoring or reviewing an existing diagram?** + → Do NOT re-run bootstrap. Respect existing `.plantuml/`. + → Read `principles.md` + `diagrams/.md`. + +## Universal workflow (apply in order) + +1. **Audience & purpose.** Ask: who reads this, what decision/understanding + does it support? If unclear, ask the user — do not guess. +2. **Pick type.** Consult `diagrams/INDEX.md` decision table. +3. **Pick detail level preset.** The `diagrams/.md` file lists its + own `minimal`/`standard`/`detailed` presets. Default to the project's + `Default detail level` from CLAUDE.md Policy, else `standard`. +4. **Emit `.puml`.** Start with: + ``` + @startuml + !$target = %getenv("PLANTUML_TARGET") + !include .plantuml/_base.puml + !include .plantuml/_targets/$target.puml + ``` + If the project is unconfigured and the user chose "one-shot", inline + defaults instead of `!include`, and add: + `' TODO: run /plantuml-init to share styling across diagrams`. +5. **Validate.** Run `plantuml -checkonly <file>`; must exit 0. +6. **Render (if requested).** Invoke `plantuml-convert` with appropriate + target profile (see `render-profiles.md`). + +## Inherited invariants + +The rules file (`~/.claude/rules/documentation/plantuml.md`) is auto-loaded +when editing `*.puml` and lists 5 universal invariants. They are NOT +repeated here; treat them as always applied. + +## Do NOT + +- Do NOT modify `plantuml-convert`; only invoke it. +- Do NOT write `skinparam` blocks inline in a diagram when the project has + `.plantuml/_theme.puml` — put them in the theme file. +- Do NOT commit rendered PNG/SVG to version control unless the project + explicitly requires it (images are build artifacts of `.puml` sources). +- Do NOT translate skill files; they stay English. Per-project label + localization is controlled by the `Label language` Policy key. + +## Test harness — reviewer dispatch + +The test harness (`scripts/run-test-suite.sh`) automates steps 1–2 +(generate + render) and step 4 (aggregate). Step 3 (adversarial +reviewers) is an **agent** responsibility: this skill does not +spawn subagents from a shell script. + +### When to run the suite +- After any material change to principles.md, render-profiles.md, + or a `diagrams/<type>.md` file. +- Before considering this skill "done" in an implementation cycle. + +### Dispatch protocol + +1. Run the orchestrator: + `bash scripts/run-test-suite.sh` + This prints the run directory (e.g. `~/temp/plantuml-tests/2026- + 04-24-run-01`). +2. For each type directory under `<run>/`, spawn one subagent via + the `Agent` tool (general-purpose, with `Read` on .puml / .svg / + .png). **Use the multimodal capability to view PNG.** +3. Prompt (English, adversarial; see full text below). +4. Subagent writes `review.md` into each `<run>/<type>/<variant>/` + directory. +5. After all 23 subagents complete, run + `scripts/aggregate-reviews.sh <run>` — it emits `_report.md` and + exits non-zero if any variant FAILs. + +### Subagent prompt template + +```text +You are an adversarial reviewer for PlantUML diagrams. Your job is +to find EVERY flaw. Do not be diplomatic, do not praise. If a +diagram is fine, say so in one sentence and move on. Most of your +output should be criticism. + +Inspect 3 variants of a <TYPE> diagram: minimal / standard / detailed. +For each variant, read the source .puml, the .png (vision), and +optionally the .svg (XML text if overflow suspected): + - <RUN>/<TYPE>/minimal/<file>.puml / .png / .svg + - <RUN>/<TYPE>/standard/<file>.puml / .png / .svg + - <RUN>/<TYPE>/detailed/<file>.puml / .png / .svg + +Evaluate against axes (severity BLOCKER / HIGH / MEDIUM / LOW): + 1. Readability (overflow, overlap, font size, contrast) + 2. Semantic clarity (is the ONE message identifiable in 5 s?) + 3. Detail-level coherence (does the gradient feel authentic?) + 4. Design-principle adherence (principles.md) + 5. Target appropriateness (DOCX & web) + 6. Source quality (include pattern, no hardcoded colors, title + matches filename) + +For each variant produce <RUN>/<TYPE>/<VARIANT>/review.md with: + - `Verdict: PASS | PASS-WITH-WARNINGS | FAIL` (on a line starting + with `Verdict:`) + - Issues table: `| severity | axis | description | fix |` + - One-line summary + +End your combined output (just log, not file) with a cross-variant +comparison: is the gradient authentic, or are variants +interchangeable? + +Do not modify any file. Reviews only. +``` + +### Iteration loop + +If `_report.md` has FAILs: + 1. Read `_report.md` top 10 systemic issues. + 2. Fix the corresponding principles/diagram file(s). + 3. Re-seed the FAILed variants with improved content. + 4. Re-run `run-test-suite.sh`. + 5. Dispatch reviewers again on the regenerated variants only. + 6. Re-aggregate. + +Stop when `_report.md` shows 0 FAILs and any remaining warnings are +acknowledged. diff --git a/plantuml/skills/plantuml-authoring/diagrams/INDEX.md b/plantuml/skills/plantuml-authoring/diagrams/INDEX.md new file mode 100644 index 0000000..dc705f0 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/INDEX.md @@ -0,0 +1,59 @@ +# Diagram Type Decision Table + +Use this when you know *what* you want to show but not *which diagram +type*. Load the specific `diagrams/<type>.md` only after selecting. + +## "I want to show…" + +| You want to show… | Use | +|-----------------------------------------------------|--------------------------| +| Static class structure, inheritance, associations | class | +| Runtime objects & their links | object | +| Package dependencies, namespacing | package | +| Software components and their interfaces | component | +| Internal structure of a complex component | composite-structure | +| Physical/cloud deployment of components | deployment | +| UML meta-model extensions, custom stereotypes | profile | +| Temporal flow between participants (messages) | sequence | +| Alternative view of sequence focusing on structure | communication | +| Control flow / business process | activity | +| State transitions of a single entity | state | +| User goals and actor interactions | usecase | +| Overview of multiple interactions | interaction-overview | +| Time-based behavior (signals, clocks, lifecycles) | timing | +| System context / landscape (C4 L1) | c4 (Context) | +| Containers inside a system (C4 L2) | c4 (Container) | +| Components inside a container (C4 L3) | c4 (Component) | +| Code-level structure (C4 L4) | use `class` instead | +| Data model, entities, relationships | er | +| Enterprise architecture, business capability map | archimate | +| Hierarchical concept map, brainstorm | mindmap | +| Work Breakdown Structure (project decomposition) | wbs | +| Project timeline, milestones | gantt | +| Low-fidelity UI wireframe | salt | +| Data structure (API response, config) | json-yaml | +| Network topology (rack, L2/L3, packet flow) | nwdiag-family | + +## Tiebreakers + +- **class vs er**: use `class` if methods matter; `er` if the story is + "entities + cardinalities + attributes only". +- **sequence vs communication**: default to `sequence` (temporal + flow is easier to read). Use `communication` only if the structural + grouping matters more than the ordering. +- **activity vs state**: `activity` models a process *across* actors + or phases; `state` models the lifecycle *of one thing*. +- **component vs c4-component**: `component` is a generic UML type; + `c4 (Component)` is opinionated (specific shapes, relationship + notation). Use C4 if the rest of the documentation is C4. +- **deployment vs c4-container**: `deployment` is node-centric (where + things physically run); `c4 (Container)` is logical unit-centric. + +## Anti-patterns + +- Do NOT use a diagram type just because it's the project default if + the content doesn't fit. Split the request instead. +- Do NOT combine types in one file (e.g., class + deployment). One + file = one type. +- Do NOT pick `timing` unless the diagram is genuinely about time + axes; the notation is unfamiliar and costly to learn for readers. diff --git a/plantuml/skills/plantuml-authoring/diagrams/activity.md b/plantuml/skills/plantuml-authoring/diagrams/activity.md new file mode 100644 index 0000000..456e0f6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/activity.md @@ -0,0 +1,63 @@ +# Activity Diagrams + +## Purpose + +Model a process as a sequence of activities with decisions, parallel +paths, and swimlanes. Think flowchart with UML semantics. + +## Choose this when / avoid when + +- ✅ Business processes or workflows +- ✅ Decision-heavy flows (many `if/else`) +- ✅ Cross-role processes (swimlanes show ownership) +- ❌ Temporal message exchange between systems → use `sequence` +- ❌ Single-object lifecycle → use `state` +- ❌ Class structure → use `class` + +## Detail-level presets + +- `minimal`: linear flow, no swimlanes, ≤ 10 steps. +- `standard`: swimlanes for each role/system, decisions, merge nodes. +- `detailed`: parallel fork/join, exceptions, notes on SLAs or + business rules. + +## Layout tips + +- PlantUML's `beta` activity syntax (`:Step;`) is current practice; + avoid the legacy `(*)` syntax. +- Direction: TB by default; LR for long horizontal processes. +- Use `partition` for swimlanes. +- Keep decision branches to 2–3; more than that becomes a table. + +## Snippet + +```plantuml +@startuml Activity_Return_Process +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +start +:Receive return request; +partition "Warehouse" { + :Inspect item; + if (Item sellable?) then (yes) + :Restock; + else (no) + :Scrap; + endif +} +partition "Finance" { + :Issue refund; +} +:Notify customer; +stop +@enduml +``` + +## Common pitfalls + +- Mixing beta and legacy syntax. Fix: use beta (`:…;`, `start/stop`, + `if/then/else/endif`, `fork/end fork`). +- Missing `stop` or `end`. Fix: every path must terminate. +- Too many swimlanes (≥ 5). Fix: collapse or split. diff --git a/plantuml/skills/plantuml-authoring/diagrams/archimate.md b/plantuml/skills/plantuml-authoring/diagrams/archimate.md new file mode 100644 index 0000000..f56e45d --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/archimate.md @@ -0,0 +1,59 @@ +# ArchiMate Diagrams + +## Purpose + +Model enterprise architecture across Business, Application, and +Technology layers per the ArchiMate 3 specification. PlantUML +provides `<archimate/Archimate>` stdlib macros. + +## Choose this when / avoid when + +- ✅ Enterprise architecture docs +- ✅ Capability-to-technology mappings +- ❌ Software architecture of a single system → use `c4` +- ❌ If the audience is dev-focused: ArchiMate notation is heavy + +## Detail-level presets + +- `minimal`: one layer, 5–7 elements. +- `standard`: two layers (Business + Application) with realizations + and serves relationships. +- `detailed`: all three layers, with motivation elements (goals, + drivers, stakeholders). + +## Layout tips + +- Use the stdlib element macros: `Business_Actor`, `Business_Process`, + `Application_Component`, `Technology_Node`, etc. +- Layer elements vertically (Business top, Technology bottom) — this + is the ArchiMate convention. +- Color-coding is built in; don't override. + +## Snippet + +```plantuml +@startuml Archimate_OrderManagement +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <archimate/Archimate> + +Business_Actor(customer, "Customer") +Business_Process(place_order, "Place Order") +Application_Service(order_svc, "Order Service") +Application_Component(order_app, "Order App") +Technology_Node(k8s, "Kubernetes Cluster") + +Rel_Triggering(customer, place_order) +Rel_Serving(order_svc, place_order) +Rel_Realization(order_app, order_svc) +Rel_Assignment(k8s, order_app) +@enduml +``` + +## Common pitfalls + +- Using ArchiMate where a simpler diagram works. Fix: only use if + enterprise stakeholders specifically expect ArchiMate. +- Mixing layer conventions. Fix: stick to Business above, Technology + below. diff --git a/plantuml/skills/plantuml-authoring/diagrams/c4.md b/plantuml/skills/plantuml-authoring/diagrams/c4.md new file mode 100644 index 0000000..47e8d30 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/c4.md @@ -0,0 +1,70 @@ +# C4 Diagrams (Context / Container / Component / Dynamic) + +## Purpose + +Document software architecture at four levels of zoom, per Simon +Brown's C4 model. PlantUML provides a stdlib via +`!include <C4/C4_Context>`, `<C4/C4_Container>`, `<C4/C4_Component>`, +`<C4/C4_Dynamic>`. + +## Choose this when / avoid when + +- ✅ Modern software-architecture documentation (default choice for + PRDs, ADRs, onboarding docs) +- ✅ Multi-audience docs (exec → context, devs → container/component) +- ❌ Fine-grained code structure → use `class` +- ❌ Physical topology → use `deployment` +- ❌ If the team does not use C4 anywhere else: use plain UML to + avoid novelty cost + +## Detail-level presets (maps to C4 levels) + +- `minimal` = **Context (L1)**: the system + external users/systems. +- `standard` = **Container (L2)**: containers inside the system + key + external dependencies. +- `detailed` = **Component (L3)** or **Dynamic**: components inside a + container, or a runtime sequence through containers. + +## Layout tips + +- Use C4-PlantUML stdlib macros (`Person`, `System`, `Container`, + `Component`, `Rel`, `Boundary`) rather than raw PlantUML shapes. +- Colors are baked into the stdlib; override only with `AddElementTag`. +- Keep each level to one diagram. + +## Snippet + +```plantuml +@startuml C4_Context_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <C4/C4_Context> + +title System Context diagram for the Online Shop + +Person(customer, "Customer") +Person(support, "Customer Support") +System(shop, "Online Shop", "Sells things online.") + +System_Ext(email, "Email Service") +System_Ext(pay, "Payment Gateway") +System_Ext(ship, "Shipping Carrier") + +Rel(customer, shop, "Browses & orders") +Rel(support, shop, "Resolves issues") +Rel(shop, email, "Sends notifications") +Rel(shop, pay, "Authorizes payments") +Rel(shop, ship, "Requests shipments") +@enduml +``` + +## Common pitfalls + +- Mixing levels in one diagram (e.g., containers drawn at the context + diagram). Fix: one level per diagram. +- Omitting the `title`. Fix: always include a human-readable title + — C4 diagrams live in slide decks and docs where the title is the + captioning signal. +- Using C4 for "code-level" L4. Fix: C4 explicitly deprecates L4 in + favor of regular class diagrams. diff --git a/plantuml/skills/plantuml-authoring/diagrams/class.md b/plantuml/skills/plantuml-authoring/diagrams/class.md new file mode 100644 index 0000000..e17457f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/class.md @@ -0,0 +1,79 @@ +# Class Diagrams + +## Purpose + +Show the static structure of a software system: classes, interfaces, +their attributes and operations, and how they relate through +inheritance, association, aggregation, and composition. + +## Choose this when / avoid when + +- ✅ Explaining a domain model or an object-oriented design +- ✅ Documenting inheritance hierarchies +- ✅ Showing interface contracts between modules +- ❌ Showing runtime behavior → use `sequence` or `activity` +- ❌ Showing persistence (DB tables) → use `er` +- ❌ Documenting more than ~15 classes in one diagram → split + +## Detail-level presets + +- `minimal`: class names only, key relationships, no attributes or + operations. Audience: architect overview, exec briefing. +- `standard`: class names + public operations (no parameters if they + add noise), attributes only when they drive the design. Audience: + design review, onboarding. +- `detailed`: full signatures (name + parameters + return type), + visibility markers (`+`, `-`, `#`), stereotypes, cardinalities on + associations. Audience: implementation reference. + +## Layout tips + +- Direction: `top-to-bottom` for inheritance-dominant stories; + `left-to-right` for association-dominant. +- Group related classes with `package` blocks; collapse external + dependencies into a single `<<external>>` class. +- Use `--|>` for inheritance, `-->` for association, `*--` for + composition, `o--` for aggregation. Consistency matters more than + preference. + +## Snippet + +```plantuml +@startuml Class_Order_Domain +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "Order Domain" { + class Order { + +id: OrderId + +status: OrderStatus + +total: Money + +submit(): void + } + class OrderLine { + +sku: SKU + +quantity: int + +subtotal(): Money + } + class Customer { + +id: CustomerId + +name: string + } +} + +Customer "1" --> "*" Order : places +Order "1" *-- "*" OrderLine : contains +@enduml +``` + +## Common pitfalls + +- Using `<|--` and `<|..` interchangeably (extends vs implements). + Fix: `<|--` is extends (solid), `<|..` is implements (dashed). +- Cluttering the diagram with getters/setters. Fix: omit them at + `standard`; they add no information. +- Mixing domain classes with persistence DTOs in one diagram. + Fix: split into `domain-model.puml` and `persistence-mapping.puml`. +- Associations without roles and cardinalities at `detailed` level. + Fix: `ClassA "1" --> "*" ClassB : owns`. diff --git a/plantuml/skills/plantuml-authoring/diagrams/communication.md b/plantuml/skills/plantuml-authoring/diagrams/communication.md new file mode 100644 index 0000000..a1426b9 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/communication.md @@ -0,0 +1,99 @@ +# Communication Diagrams + +## Purpose + +An alternative view of a sequence: show participants as nodes in a +graph and messages as numbered labels on the edges. Emphasizes +structure over time. + +> **PlantUML limitation.** PlantUML has no first-class +> communication-diagram syntax. The rendered SVG metadata reports +> `data-diagram-type="CLASS"` because the engine treats this as an +> object diagram with labelled links. This skill **emulates** UML +> communication notation by combining `object` participants, +> `hide empty members`, explicit numeric prefixes on labels, and +> reply-arrow conventions. Read every snippet here as "object diagram +> styled to read as a communication diagram", not as native support. + +## Choose this when / avoid when + +- ✅ When the structural relationship between participants matters + more than the exact ordering +- ✅ When the same participants exchange messages in multiple + scenarios and you want to show the graph once +- ✅ When stakeholders specifically ask for the + numbered-graph-with-arrows idiom +- ❌ When ordering dominates → use `sequence` (PlantUML's first-class + support there is far stronger) +- ❌ When there are branches/alts → `sequence` handles them; this + emulation does not +- ❌ When you need lifelines, activations, or async distinctions → + `sequence` + +## Detail-level presets + +- `minimal`: participants + numbered messages, no attributes. +- `standard`: add argument summaries to message labels and an + explicit numbering tree (e.g. `1`, `1.1`, `1.2`, `2`). +- `detailed`: nested numbering with replies shown as dashed arrows + (`..>`) carrying return values. + +## Layout tips + +- Always include `hide empty members` — without it, every `object` + renders an empty attribute compartment and the graph becomes + visually noisy. +- Use `-->` (solid) for synchronous calls, `..>` (dashed) for + replies/return values. PlantUML accepts both on object diagrams and + the convention reads as UML communication notation. +- Encode call order in the **label**, not in the file order. Use + dotted decimals to express nesting: `1`, `1.1`, `1.2`, `2`. +- Keep message count ≤ 10; beyond that the numbers become confusing. + If you have more, you almost certainly want `sequence`. +- Quote labels that contain spaces or colons (`: "1: quote(items)"`). + +## Snippet + +```plantuml +@startuml Communication_Checkout_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Communication — Checkout (standard preset)" + +' Emulated communication diagram: object nodes + numbered labels. +' `hide empty members` strips the empty attribute compartments so +' the result reads as a graph of participants, not as data objects. +hide empty members + +object ":Cart" as cart +object ":Pricing" as pricing +object ":Orders" as orders +object ":Payments" as pay + +cart --> pricing : "1: quote(items)" +pricing ..> cart : "1.1: total" +cart --> orders : "2: submit(cart)" +orders --> pay : "2.1: authorize(total)" +pay ..> orders : "2.2: authCode" +orders ..> cart : "3: confirmation" +@enduml +``` + +## Common pitfalls + +- Treating PlantUML output as a true UML communication diagram. Fix: + remember the SVG metadata says `CLASS`/object — the notation is + emulated. If a tool downstream parses diagram type from metadata, + this will not be detected as `COMMUNICATION`. +- Confusing with sequence diagrams. Fix: if you need timing arrows, + activations, or alt/else, switch to `sequence`. +- Inconsistent numbering. Fix: use dotted decimals (`1`, `1.1`, + `1.2`, `2`) to nest sub-calls; never repeat a number. +- Forgetting `hide empty members`. Fix: without it, every object + renders an empty `{}` compartment and the diagram looks like a data + model rather than a message graph. +- Mixing solid and dashed arrows arbitrarily. Fix: solid `-->` for + calls, dashed `..>` for replies — pick the convention and apply it + uniformly. diff --git a/plantuml/skills/plantuml-authoring/diagrams/component.md b/plantuml/skills/plantuml-authoring/diagrams/component.md new file mode 100644 index 0000000..8e6e552 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/component.md @@ -0,0 +1,64 @@ +# Component Diagrams + +## Purpose + +Show the software components of a system, their provided and required +interfaces, and the connectors between them. Answers "what modules +exist and how are they wired?". + +## Choose this when / avoid when + +- ✅ Designing module boundaries in a service or application +- ✅ Showing dependencies between libraries, services, or plugins +- ✅ Contract-first design: making interfaces explicit +- ❌ Showing physical deployment → use `deployment` +- ❌ Showing runtime message exchange → use `sequence` +- ❌ Code-level class structure → use `class` + +## Detail-level presets + +- `minimal`: components only, direction of dependency, no interfaces. +- `standard`: components + provided/required interfaces (ball-and-socket + or lollipop), dependency arrows labelled with "uses". +- `detailed`: ports, grouped into `package`s by subsystem, notes on + protocol (HTTP/gRPC/AMQP) and non-functional constraints. + +## Layout tips + +- Direction: dependency direction. If A uses B, A is above (TB) or + left (LR) of B. +- Prefer LR for pipeline architectures; TB for layered. +- Use `()` interface shorthand for brevity; explicit `interface` for + clarity. + +## Snippet + +```plantuml +@startuml Component_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "Checkout" { + [Cart] as Cart + [Pricing] as Pricing + [Orders] as Orders + () "Catalog API" as CatalogAPI + () "Payments API" as PayAPI +} + +Cart -down-> Pricing : queries +Pricing -right-> CatalogAPI : reads +Cart -down-> Orders : submits +Orders -right-> PayAPI : authorizes +@enduml +``` + +## Common pitfalls + +- Confusing components with classes. Fix: components are deployable + units with interfaces; classes are code-level types. +- Drawing bidirectional arrows to mean "communicates with". Fix: + one direction per arrow; if both directions matter, draw two. +- Skipping interfaces. Fix: at `standard`+, always show the interface + at the boundary — that's the whole point of the diagram. diff --git a/plantuml/skills/plantuml-authoring/diagrams/composite-structure.md b/plantuml/skills/plantuml-authoring/diagrams/composite-structure.md new file mode 100644 index 0000000..51b4fa9 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/composite-structure.md @@ -0,0 +1,90 @@ +# Composite Structure Diagrams + +## Purpose + +Show the internal structure of a complex component: its parts, ports, +and connectors, contained within the component's boundary. The defining +notation is **ports on the boundary** — small squares straddling the +outer rectangle that expose the component's interaction points. + +## Choose this when / avoid when + +- ✅ Decomposing a component into collaborating parts that only make + sense inside it +- ✅ Documenting an ACL, adapter, or façade pattern at high level +- ✅ Showing how a component's interaction points (ports) wire to + internal parts +- ❌ Showing the component's place in a system → use `component` +- ❌ Showing classes that happen to collaborate → use `class` +- ❌ Pure dependency arrows between independent components → use + `component` (composite structure is for *internal* decomposition) + +## Detail-level presets + +- `minimal`: outer component boundary + 2–3 named parts, no ports. + Communicates the decomposition itself. +- `standard`: parts + ports on the boundary + connectors between ports + and parts. Adds the defining-notation signal of the type. +- `detailed`: protocol stereotypes on connectors, multiplicities on + ports, internal sub-parts where useful. Add 1–2 `note` directives + if intent isn't obvious from structure. + +## Layout tips + +- The enclosing component is one big `component "..." { ... }` block. +- **Ports** sit on the outer boundary. PlantUML's port keyword is the + canonical syntax: `port "name" as id` declared *inside* the + enclosing component renders as a small square on the boundary. +- Inner **parts** are `[Part]` rectangles inside the component block + (no need for a `<<part>>` stereotype unless you want it explicit). +- Connectors use plain arrows or undirected lines `--`. Reserve `-->` + for delegation/dependency direction within the boundary. +- Keep internal parts to ≤ 7 — beyond that, extract a sub-component + with its own composite-structure diagram. + +## Snippet + +```plantuml +@startuml CompositeStructure_PaymentGateway_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Payment Gateway — internal composition (standard)" + +component "Payment Gateway" { + port "pay-in" as PI + port "pay-out" as PO + + [Router] as R + [Authorizer] as A + [Anti-Fraud] as F + [Settlement] as S + + PI --> R + R --> A + A --> F : checks + A --> S : on success + S --> PO +} +@enduml +``` + +## Common pitfalls + +- **Using lollipops `() "name"` as if they were ports.** Fix: lollipops + denote *provided/required interfaces* in component diagrams, not + ports in composite-structure. Use `port "name" as id` inside the + component block — it renders as a small square on the boundary, + which is the defining UML composite-structure notation. +- Indistinguishable from a component diagram. Fix: emphasize the + boundary (one outer component) and keep all parts *inside*. If + parts also live independently outside the boundary, you want + `component`, not composite-structure. +- Ports drifting *inside* the container instead of on the boundary. + Fix: declare ports immediately at the start of the component block + (before parts) and connect them to parts with arrows; the layout + engine then anchors them on the boundary. +- Missing decline/error path. Fix: at `standard`+, show alternatives + explicitly (e.g., `A --> S : on success` paired with `A --> PO : + denied` or a separate `denied-out` port). diff --git a/plantuml/skills/plantuml-authoring/diagrams/deployment.md b/plantuml/skills/plantuml-authoring/diagrams/deployment.md new file mode 100644 index 0000000..979aba3 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/deployment.md @@ -0,0 +1,66 @@ +# Deployment Diagrams + +## Purpose + +Show where software components physically run: nodes (hosts, pods, +containers, VMs), artifacts deployed on them, and the communication +paths between nodes. + +## Choose this when / avoid when + +- ✅ Explaining environment topology to ops/SRE +- ✅ Documenting cloud architecture (regions, AZs, services) +- ✅ Capacity or HA reviews +- ❌ Logical component wiring → use `component` +- ❌ Network packet flows → use `nwdiag-family` + +## Detail-level presets + +- `minimal`: top-level nodes (e.g., "API Tier", "DB", "Queue"), + connection labels, no artifacts. +- `standard`: nodes + deployed artifacts, connection protocols, + grouped by environment or region. +- `detailed`: ports, versions, redundancy (active/passive), specific + instance types/sizes. + +## Layout tips + +- Direction: follow traffic (user → edge → app → data) LR or TB as + suits the shape. +- Nest nodes to show containment (`cloud { node { … } }`). +- Use stereotypes `<<AWS>>`, `<<Kubernetes>>` to hint platform. + +## Snippet + +```plantuml +@startuml Deployment_Prod +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +cloud "AWS eu-west-1" { + node "ALB" as ALB + node "ECS Fargate" as ECS { + artifact "api:1.4.2" as API + } + database "RDS PostgreSQL" as DB + queue "SQS orders" as Q +} + +actor User +User --> ALB : HTTPS +ALB --> API : HTTP/2 +API --> DB : TCP/5432 +API --> Q : HTTPS (publish) +@enduml +``` + +## Common pitfalls + +- Conflating logical and physical. Fix: a deployment diagram shows + *where things run*; logical wiring belongs in `component`. +- Missing protocols/ports. Fix: label every connection with its + protocol at `standard` and up. +- Over-nesting (cloud → region → AZ → VPC → subnet → node) until the + diagram is unreadable. Fix: omit layers that don't support the + message. diff --git a/plantuml/skills/plantuml-authoring/diagrams/er.md b/plantuml/skills/plantuml-authoring/diagrams/er.md new file mode 100644 index 0000000..01e7d65 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/er.md @@ -0,0 +1,73 @@ +# Entity-Relationship Diagrams + +## Purpose + +Show a data model: entities, their attributes, and the relationships +(cardinalities) between them. + +## Choose this when / avoid when + +- ✅ Database schema design +- ✅ Domain model focused on data rather than behavior +- ✅ Explaining a normalized model to an audience familiar with ERDs +- ❌ When behavior matters → use `class` +- ❌ Physical DB (indexes, partitions) → use `deployment` or custom + docs + +## Detail-level presets + +- `minimal`: entities as boxes with name + primary key, crow's-foot + relationships, no attributes. +- `standard`: primary + foreign keys, a handful of business-key + attributes, cardinalities on both ends. +- `detailed`: all attributes with types and nullability, junction + entities for many-to-many. + +## Layout tips + +- PlantUML's `entity` keyword or Chen ER notation; prefer `entity` + + standard crow's-foot unless stakeholders expect Chen. +- Direction: LR for wide schemas; TB for hierarchical. +- Keep to ≤ 15 entities per diagram. + +## Snippet + +```plantuml +@startuml ER_OrderModel +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +entity "Customer" as c { + *id : uuid <<PK>> + -- + name : text + email : text +} +entity "Order" as o { + *id : uuid <<PK>> + -- + *customer_id : uuid <<FK>> + status : text + total : numeric +} +entity "OrderLine" as ol { + *id : uuid <<PK>> + -- + *order_id : uuid <<FK>> + *sku : text + qty : int +} + +c ||--o{ o +o ||--o{ ol +@enduml +``` + +## Common pitfalls + +- Modeling behavior (methods) on entities. Fix: this is an ER + diagram, not a class diagram. +- Missing cardinalities. Fix: always show both ends. +- Skipping junction entities for many-to-many. Fix: explicit junction + entity is the canonical form. diff --git a/plantuml/skills/plantuml-authoring/diagrams/gantt.md b/plantuml/skills/plantuml-authoring/diagrams/gantt.md new file mode 100644 index 0000000..2df4f1f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/gantt.md @@ -0,0 +1,66 @@ +# Gantt Charts + +## Purpose + +Visualize a project timeline: tasks, durations, dependencies, and +milestones. + +## Choose this when / avoid when + +- ✅ Inline Gantt in a doc (for quick timeline communication); PPTX + slide with phasing +- ✅ Shareable with non-PMO audiences +- ❌ Detailed PM needs → use your actual PM tool (JIRA, MS Project); + PlantUML Gantt is illustrative only +- ❌ Resource levelling, earned value → out of scope + +## Detail-level presets + +- `minimal`: 3–5 top-level phases on a bar, no dependencies. +- `standard`: tasks grouped by phase, dependencies (`-> then`, + `starts at`), milestones (`* …`). +- `detailed`: resources, percentage complete, today line, color + coding per stream. + +## Layout tips + +- `@startgantt` / `@endgantt`. +- Use `Project starts YYYY-MM-DD`. +- `-- Phase --` separators structure the chart. +- Keep to ≤ 20 tasks; beyond that, use a real PM tool. + +## Snippet + +```plantuml +@startgantt Gantt_Release_Q3 +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +Project starts 2026-07-01 + +-- Discovery -- +[Stakeholders] lasts 10 days +[Research] lasts 10 days and starts at [Stakeholders]'s end + +-- Build -- +[Backend] lasts 20 days and starts at [Research]'s end +[Frontend] lasts 15 days and starts 5 days after [Backend]'s start +[Integration] lasts 10 days and starts at [Frontend]'s end + +-- Release -- +[QA] lasts 7 days and starts at [Integration]'s end +[Rollout] lasts 3 days and starts at [QA]'s end + +[Launch] happens at [Rollout]'s end +@endgantt +``` + +## Common pitfalls + +- Using PlantUML Gantt for actual PM. Fix: it's illustrative; for + critical-path analysis use `pmo-pert-estimate` (Excel) or a PM + tool. +- Forgetting `Project starts`. Fix: always set a start date. +- Dependencies via comment instead of syntax. Fix: use `starts at + [X]'s end`. diff --git a/plantuml/skills/plantuml-authoring/diagrams/interaction-overview.md b/plantuml/skills/plantuml-authoring/diagrams/interaction-overview.md new file mode 100644 index 0000000..58ee8d6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/interaction-overview.md @@ -0,0 +1,111 @@ +# Interaction Overview Diagrams + +## Purpose + +An overview where each major interaction is a `ref over` block in a +top-level sequence, allowing branching via `alt/else` at sequence +level. The result reads as a "map" of sub-interactions: each `ref` +points to a separate, fully-detailed sequence diagram, and the +top-level control flow shows how those sub-interactions chain +together. + +## Note on PlantUML limitations + +Interaction overview is **not** a first-class diagram type in +PlantUML. There is no `ref(...)` activity-node function — writing +`:ref(Sequence_X);` in an activity diagram renders the parenthesised +text literally, it does not create a UML interaction-reference node. + +Two workarounds exist: + +- **Sequence + `ref over` (recommended, used here):** model the + overview as a sequence diagram in which every "step" is a + `ref over A, B : SubInteraction` block. Branching uses `alt/else` + between refs. This is valid PlantUML, renders cleanly, and is what + reviewers expect. +- **Activity with stereotyped nodes:** keep activity syntax but use + `:Sub-interaction A<<ref>>;` or `:[ref] Sub-interaction A;` to + signal "this node is a reference". Closer to UML 2.x interaction + overview spec, but still a labelling convention rather than a + first-class construct. + +If you need a true UML 2.x interaction overview diagram, render it +outside PlantUML. + +## Choose this when / avoid when + +- ✅ A top-level "map" of several sequence diagrams, with branching + between them +- ✅ Orchestrating choreographies where the control flow between + sub-interactions is the story +- ✅ Onboarding readers who need the big picture before drilling into + individual sequences +- ❌ A single interaction — use `sequence` +- ❌ A business process with swimlanes / parallel paths — use + `activity` +- ❌ A static call graph between components — use `component` + +## Detail-level presets + +- `minimal`: only `ref over` blocks, linear order, no branching. +- `standard`: `ref over` blocks plus one or two `alt/else` branches + for the main decision points. +- `detailed`: multiple branches, `par` for concurrent + sub-interactions, and short inline messages between refs to clarify + hand-offs. + +## Layout tips + +- Direction: TB (sequence default). The overview reads top-to-bottom + like any sequence. +- Keep the participant list short — typically the actors and the one + or two systems that span every sub-interaction. Per-sub-interaction + participants live inside the referenced sequences, not here. +- Name each `ref` identically to the target `.puml` file (e.g. + `Sequence_SignUp`) for greppable traceability. +- Use `== Phase ==` separators if the chain has clear lifecycle + phases (e.g. Acquisition / Activation / Retention). + +## Snippet + +```plantuml +@startuml InteractionOverview_Onboarding_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Onboarding — Interaction Overview (standard)" + +actor Customer +participant "Web App" as Web +participant "Identity API" as Identity + +== Sign-up == +ref over Customer, Web, Identity : Sequence_SignUp + +alt email verified + == Activation == + ref over Customer, Web, Identity : Sequence_ProfileSetup + ref over Customer, Web : Sequence_FirstPurchase +else email not verified + ref over Customer, Identity : Sequence_ResendVerification +end +@enduml +``` + +## Common pitfalls + +- Using `:ref(Sequence_X);` inside an activity diagram. Fix: PlantUML + has no such function; the parentheses render literally. Use + `ref over <participants> : Sequence_X` in a sequence diagram + instead (see snippet). +- Re-drawing the referenced sequences inline. Fix: that defeats the + purpose; keep refs opaque and link out to the dedicated sequence + diagram. +- Missing link from ref label to the actual diagram. Fix: name the + ref identically to the target `.puml` file for greppable + traceability. +- Listing every participant of every sub-interaction at the top. + Fix: keep the overview's participant list to the spanning + actors/systems; sub-interaction-specific participants belong inside + the referenced sequences. diff --git a/plantuml/skills/plantuml-authoring/diagrams/json-yaml.md b/plantuml/skills/plantuml-authoring/diagrams/json-yaml.md new file mode 100644 index 0000000..60522ed --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/json-yaml.md @@ -0,0 +1,100 @@ +# JSON / YAML Diagrams + +## Purpose + +Render JSON or YAML data as a structured diagram. Useful for +documenting API response shapes, config formats, or domain examples +inline in specs. + +## Choose this when / avoid when + +- ✅ Embedding an API example or config sample in a doc +- ✅ Illustrating nested structures with obvious hierarchy +- ❌ Showing schemas/relations → use `er` or `class` +- ❌ Huge blobs: use an appendix or external file + +## Detail-level presets + +- `minimal`: one top-level key with a few children. +- `standard`: realistic example with 2–3 levels of nesting. +- `detailed`: highlight fields with `<#color>` for annotations; + include comments. + +## Layout tips + +- `@startjson` / `@endjson` for JSON; `@startyaml` / `@endyaml` for + YAML. +- Keep the example ≤ 30 lines; beyond that, point to an external + file. + +## Known limitations (PlantUML 1.2026.x) + +Two parser quirks worth knowing about: + +**1) Includes must be OUTSIDE `@startjson`.** The JSON parser treats +everything between `@startjson` and `@endjson` as JSON data — including +preprocessor directives like `!include`. Place the target/include chain +*before* `@startjson`: + +```plantuml +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +@startjson JSON_OrderResponse +{ ... } +@endjson +``` + +If you put `!include` inside the `@startjson` block, both PNG and SVG +render the *"Your data does not sound like JSON data"* error stub. + +**2) PNG render must NOT use `-S<setting>=...` CLI flags.** Any +`-Sscale`, `-SdefaultFontSize`, etc. on the command line forces the +JSON parser into a degraded path that emits the same error stub. SVG +rendering is unaffected by `-S`. + +For PNG-target render of a JSON diagram: + +```bash +plantuml -tpng diagram.puml # OK +plantuml -tpng -Sscale=3 diagram.puml # BROKEN — produces error stub +``` + +If you need scale/dpi override, set `skinparam dpi <N>` inside the +include chain instead of the CLI flag. The `_targets/docx.puml` +template already does this. + +## Snippet + +```plantuml +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +@startjson JSON_OrderResponse +{ + "id": "o_771", + "status": "PAID", + "customer": { + "id": "c_42", + "tier": "GOLD" + }, + "lines": [ + {"sku": "SKU-A", "qty": 2}, + {"sku": "SKU-B", "qty": 1} + ], + "total": 128.90 +} +@endjson +``` + +## Common pitfalls + +- Using JSON/YAML diagrams as data storage. Fix: it's for + illustration; the authoritative schema lives elsewhere (OpenAPI, + JSON Schema). +- Dropping commas/quotes and breaking the render. Fix: the snippet + must be valid JSON/YAML; pass through a linter. +- Putting `!include` between `@startjson` and the JSON body. Fix: + move includes *before* `@startjson`. See Known limitations above. +- Rendering JSON to PNG with `-Sscale=N` or other `-S` CLI flags. + Fix: drop the flag, or render to SVG. See Known limitations. diff --git a/plantuml/skills/plantuml-authoring/diagrams/mindmap.md b/plantuml/skills/plantuml-authoring/diagrams/mindmap.md new file mode 100644 index 0000000..9fae89a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/mindmap.md @@ -0,0 +1,84 @@ +# Mind Maps + +## Purpose + +Hierarchical concept map radiating from a central node. Good for +brainstorming, note-taking, or executive-facing conceptual overviews. + +## Choose this when / avoid when + +- ✅ Capturing a brainstorm or a concept decomposition +- ✅ High-level overview slides +- ❌ Project decomposition with ownership/effort → use `wbs` +- ❌ Process flow → use `activity` + +## Detail-level presets + +- `minimal`: 2 levels (root + children). +- `standard`: 3 levels, max ~6 children per node. +- `detailed`: 4 levels with notes; beyond that the map becomes noise. + +## Layout tips + +- Use `@startmindmap` / `@endmindmap`. +- `*` for right branches, `-` for left. Balance both sides. +- Leaf nodes can carry a short note with `:caption:` syntax. + +## Render-engine note (PlantUML 1.2026.x) + +PlantUML 1.2026.x mindmap rendering does **not reserve enough left-side +padding**, especially when branches grow leftward (`-` prefix). The SVG +viewBox can start at a negative `x`, and PNG output clips the leftmost +labels. Adversarial reviewers consistently flag this as a defect. + +Add explicit canvas padding at the top of any mindmap that uses +left-side branches: + +```plantuml +@startmindmap Mindmap_<Subject> +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +skinparam padding 24 ' prevents left-edge clipping +… +@endmindmap +``` + +24 px is sufficient for typical labels; increase to 40+ if your labels +are long. + +## Snippet + +```plantuml +@startmindmap Mindmap_PlatformObjectives +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +skinparam padding 24 + +* Platform Objectives +** Reliability +*** SLO 99.9% +*** Disaster recovery +** Performance +*** Latency p95 < 200 ms +*** Throughput +30% +** Security +*** OWASP compliance +*** Audit trail +-- Cost +--- Cloud spend -15% +-- Team +--- Hiring plan +--- Training +@endmindmap +``` + +## Common pitfalls + +- Using a mindmap as a project plan. Fix: use WBS. +- Unbalanced trees (all branches right). Fix: alternate left/right. +- **Left-side branch labels get clipped at canvas edge.** Fix: add + `skinparam padding 24` (or larger). See Render-engine note above. diff --git a/plantuml/skills/plantuml-authoring/diagrams/nwdiag-family.md b/plantuml/skills/plantuml-authoring/diagrams/nwdiag-family.md new file mode 100644 index 0000000..9fadc05 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/nwdiag-family.md @@ -0,0 +1,55 @@ +# Network Diagrams (nwdiag family) + +## Purpose + +Network topology: networks, hosts, subnets, and links. PlantUML +supports `nwdiag`, `rackdiag`, `packetdiag` via the legacy blockdiag +compat layer. + +## Choose this when / avoid when + +- ✅ Network architecture: subnets, zones, interconnects +- ✅ Rack layout documentation +- ❌ Cloud topology at service level → use `deployment` +- ❌ Application-level communication → use `sequence` or `component` + +## Detail-level presets + +- `minimal`: networks + hosts, no addressing. +- `standard`: CIDR on networks, IP/interface on hosts, zones. +- `detailed`: VLANs, ports, trunk/access, HA pairs. + +## Layout tips + +- `@startuml` + `nwdiag { … }` block. +- Order networks vertically (DMZ top, internal bottom). +- Keep to ≤ 4 networks per diagram. + +## Snippet + +```plantuml +@startnwdiag Nwdiag_Topology +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +network dmz { + address = "192.168.10.0/24" + web01 [address = "192.168.10.11"] + web02 [address = "192.168.10.12"] +} +network internal { + address = "10.0.0.0/16" + web01 [address = "10.0.1.11"] + web02 [address = "10.0.1.12"] + app01 [address = "10.0.2.21"] + db01 [address = "10.0.3.31"] +} +@endnwdiag +``` + +## Common pitfalls + +- Using nwdiag for cloud deployment. Fix: cloud services don't map + well to subnets; use a `deployment` diagram with cloud nodes. +- Overloading with too many addresses. Fix: show representative hosts; + refer to a source-of-truth IPAM system for the complete list. diff --git a/plantuml/skills/plantuml-authoring/diagrams/object.md b/plantuml/skills/plantuml-authoring/diagrams/object.md new file mode 100644 index 0000000..fb403c6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/object.md @@ -0,0 +1,70 @@ +# Object Diagrams + +## Purpose + +Show a snapshot of runtime objects and their links at a particular +moment. A class diagram describes the rule; an object diagram +illustrates one instance of that rule. + +## Choose this when / avoid when + +- ✅ Concretising a complex class diagram with an example +- ✅ Explaining a corner case ("what the state looks like after X") +- ✅ Test case documentation +- ❌ Showing general structure → use `class` +- ❌ Temporal flow → use `sequence` + +## Detail-level presets + +- `minimal`: objects with names and types; no attributes. +- `standard`: objects with key attribute values. +- `detailed`: all attribute values, link labels, snapshot timestamp + in a note. + +## Layout tips + +- Use the same orientation as the related class diagram so the reader + sees the mapping. +- Mark object names in lowercase to distinguish from class types + (UML convention: `anOrder:Order`). + +## Snippet + +```plantuml +@startuml Object_Checkout_State +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +object "anne:Customer" as anne { + id = 42 + tier = GOLD +} +object "o_771:Order" as o771 { + id = 771 + status = SUBMITTED + total = 128.90 +} +object "ol_1:OrderLine" as ol1 { + sku = "SKU-A" + qty = 2 +} +object "ol_2:OrderLine" as ol2 { + sku = "SKU-B" + qty = 1 +} + +anne --> o771 +o771 --> ol1 +o771 --> ol2 +@enduml +``` + +## Common pitfalls + +- Using object diagrams where a class diagram would do. Fix: prefer + class unless the point is genuinely a snapshot. +- Omitting the class type after the colon. Fix: `name:ClassName` is + the standard form. +- Showing too many objects. Fix: ≤ 8 objects per diagram; more than + that defeats the purpose of illustration. diff --git a/plantuml/skills/plantuml-authoring/diagrams/package.md b/plantuml/skills/plantuml-authoring/diagrams/package.md new file mode 100644 index 0000000..ea68b2a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/package.md @@ -0,0 +1,72 @@ +# Package Diagrams + +## Purpose + +Show the package/namespace structure of a codebase and dependencies +between packages. Answers "how is this codebase organized and what +depends on what?". + +## Choose this when / avoid when + +- ✅ Explaining directory/module organization +- ✅ Identifying dependency cycles +- ✅ Architectural review ("is this layered correctly?") +- ❌ Showing class-level detail → use `class` +- ❌ Showing deployment units → use `component` or `deployment` + +## Detail-level presets + +- `minimal`: packages as boxes, high-level dependency arrows. +- `standard`: nested packages, dependency arrows labelled with the + reason (`uses`, `imports`, `extends`). +- `detailed`: each package annotated with a short description; cyclic + dependencies called out with notes and red color. + +## Layout tips + +- Direction: TB — layers flow top to bottom (presentation → app → + domain → infra). +- Use `<<layer>>` stereotypes to make the layering explicit. +- Color-code by layer (semantic color, per principle #6). + +## Snippet + +```plantuml +@startuml Package_Layers +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "presentation" <<layer>> { + package "web" as web + package "api" as api +} +package "application" <<layer>> { + package "commands" as cmd + package "queries" as qry +} +package "domain" <<layer>> { + package "order" as ord + package "pricing" as prc +} +package "infrastructure" <<layer>> + +web --> api +api --> cmd +api --> qry +cmd --> ord +qry --> ord +ord --> prc +cmd --> "infrastructure" +qry --> "infrastructure" +@enduml +``` + +## Common pitfalls + +- Drawing a package diagram that is actually a component diagram. Fix: + packages are namespaces; components are deployable units. +- Arrow direction reversed. Fix: arrow points *from* the package that + imports *to* the package that defines. +- Undocumented cycles. Fix: if there's a cycle, highlight it and add a + note — never pretend it doesn't exist. diff --git a/plantuml/skills/plantuml-authoring/diagrams/profile.md b/plantuml/skills/plantuml-authoring/diagrams/profile.md new file mode 100644 index 0000000..72a3701 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/profile.md @@ -0,0 +1,75 @@ +# Profile Diagrams + +## Purpose + +Show UML extensions: custom stereotypes, tagged values, and +constraints that adapt UML to a specific domain or tool. Rare in +application development; common in modeling frameworks. + +## Choose this when / avoid when + +- ✅ Documenting a DSL or modeling convention your team invented +- ✅ Extending UML for a specific industry (telecom, automotive, …) +- ❌ Any ordinary design communication — prefer a plain class or + component diagram with `<<stereotype>>` labels inline +- ❌ If the audience doesn't already know what a UML profile is + +## Detail-level presets + +- `minimal`: the profile box + one or two stereotypes. +- `standard`: all stereotypes, base UML metaclasses they extend, + tagged values. +- `detailed`: OCL constraints, iconography references. + +## Layout tips + +- Two-column layout: profile on the left, base UML elements on the + right, extension arrows crossing between. +- Explicit `<<profile>>`, `<<metaclass>>`, `<<stereotype>>` + stereotypes on every element — the notation is too niche to rely + on visual conventions. + +## Snippet + +```plantuml +@startuml Profile_BankingExtensions_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Banking Profile — UML 2 Profile Extensions (standard)" + +package "Banking Profile" <<profile>> { + class Account <<stereotype>> + class LedgerEntry <<stereotype>> +} +package "UML" { + class Class <<metaclass>> + class Association <<metaclass>> +} + +Account --|> Class : <<extend>> +LedgerEntry --|> Class : <<extend>> +LedgerEntry --|> Association : <<extend>> +@enduml +``` + +## Common pitfalls + +- Using profile diagrams to document domain models (which is what + `<<stereotype>>` *tagging* in class diagrams is for). Fix: only use + profile diagrams to document the profile itself, not uses of it. +- Omitting the base UML elements the stereotypes extend. Fix: always + show the extension arrow to the metaclass. +- Drawing the stereotype-to-metaclass relation as `..|>` (interface + realization: dashed line + hollow triangle). That notation means + "implements an interface" and is wrong for profiles. UML 2 mandates + the **Extension** arrow — solid line, filled triangle — pointing + from the stereotype to the metaclass, conventionally labeled + `<<extend>>`. In PlantUML, use `Stereotype --|> Metaclass : <<extend>>` + (solid generalization arrow with the extend keyword as the label). +- Encoding the stereotype keyword inside the class label, e.g. + `class "<<stereotype>>\nAccount" as StAccount`. PlantUML supports + stereotypes natively: `class Account <<stereotype>>`. The native + form renders the guillemets in the canonical position above the + name and avoids the alias indirection. diff --git a/plantuml/skills/plantuml-authoring/diagrams/salt.md b/plantuml/skills/plantuml-authoring/diagrams/salt.md new file mode 100644 index 0000000..fe1c6d1 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/salt.md @@ -0,0 +1,69 @@ +# Salt (UI Wireframes) + +## Purpose + +Low-fidelity UI mockups: forms, windows, dialogs rendered as ASCII- +art-like grids inside PlantUML. + +## Choose this when / avoid when + +- ✅ Quick wireframes embedded in a design doc +- ✅ Specifying field layouts without a design tool +- ❌ High-fidelity UI: use Figma/Sketch +- ❌ Interaction design: Salt is static + +## Detail-level presets + +- `minimal`: one screen with labels only. +- `standard`: one screen with fields, buttons, menu. +- `detailed`: multiple screens (use `newpage`); tab bars, trees, + grids. + +## Layout tips + +- `@startsalt` / `@endsalt` (or `@startuml` + `salt` keyword block). +- Use `{` grid layouts with `|` column separators. +- Prefix field types: `[Button]`, `"Label"`, `(Radio)`, `<Link>`, + `[Text field ]`. + +## Known limitations (PlantUML 1.2026.x) + +The `newpage` directive does **not produce separate PNG files** when +rendering with `plantuml -tpng`. Only the first page of a multi-page +salt diagram is emitted. SVG output similarly bundles all pages but +most viewers display only the first. + +Workaround: split a multi-page wireframe into **separate +`@startsalt`/`@endsalt` blocks** in the same `.puml` file (PlantUML +emits one image per top-level block), or into **separate `.puml` +files** for each page. Each block then renders to its own PNG/SVG and +is independently embeddable in docs. + +## Snippet + +```plantuml +@startsalt Salt_LoginDialog +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +{ + Sign in to the Shop + -- + Email | "you@example.com " + Password | "*************** " + [ ] Remember me + [Sign in] | [Forgot password] +} +@endsalt +``` + +## Common pitfalls + +- Mistaking Salt for design. Fix: Salt communicates layout intent; + visual design lives elsewhere. +- Omitting field types. Fix: explicit `[…]` / `"…"` / `(…)` signals + prevent ambiguity. +- **Using `newpage` and expecting individual page renders.** Fix: + split into separate `@startsalt` blocks. See Known limitations + above. diff --git a/plantuml/skills/plantuml-authoring/diagrams/sequence.md b/plantuml/skills/plantuml-authoring/diagrams/sequence.md new file mode 100644 index 0000000..1938edd --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/sequence.md @@ -0,0 +1,80 @@ +# Sequence Diagrams + +## Purpose + +Show the ordered exchange of messages between participants +(actors/systems/components) over time. + +## Choose this when / avoid when + +- ✅ Explaining request/response flows, async interactions, timing +- ✅ Documenting a protocol or a use case scenario end-to-end +- ✅ Onboarding developers to an unfamiliar flow +- ❌ Static structure → use `class` or `component` +- ❌ State changes inside a single object → use `state` +- ❌ Business process with branches and parallel paths dominant → + consider `activity` + +## Detail-level presets + +- `happy-path` (minimal equivalent): 2–5 participants, main flow only, + no errors, ≤ 12 messages. +- `with-alternatives` (standard): add `alt/else/opt` for key + branches; ≤ 20 messages. +- `exhaustive` (detailed): all return arrows, timeouts, errors, + sub-sequences via `ref`. Usually a red flag — reconsider splitting. + +## Layout tips + +- Always TB (the default). Participants left-to-right in call-order. +- Group related exchanges with `== Phase ==` separators. +- `activate`/`deactivate` explicitly for overlapping calls to avoid + ambiguous lifelines. +- Distinguish sync (`->`) from async (`->>`) consistently. + +## Snippet + +```plantuml +@startuml Sequence_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +actor Customer +participant "Web App" as Web +participant "Orders API" as API +database "Orders DB" as DB +queue "Events" as MQ + +Customer -> Web : Place order +activate Web +Web -> API : POST /orders +activate API +API -> DB : INSERT order (PENDING) +API -> MQ : order.created +API --> Web : 201 Created +deactivate API + +alt payment authorised + MQ -> API : payment.ok + API -> DB : UPDATE status=PAID +else payment failed + MQ -> API : payment.ko + API -> DB : UPDATE status=CANCELLED +end +Web --> Customer : Confirmation page +deactivate Web +@enduml +``` + +## Common pitfalls + +- Participants named after HTTP verbs. Fix: use domain names + (actors/systems); HTTP bindings go in message labels only if + relevant. +- Mixing sync/async without visual distinction. Fix: `->` for sync, + `->>` for async. +- More than ~25 messages in one diagram. Fix: split by phase into + multiple diagrams, or use `ref over A, B : <subflow>`. +- No `activate/deactivate` when lifelines overlap. Fix: explicit + activations disambiguate. diff --git a/plantuml/skills/plantuml-authoring/diagrams/state.md b/plantuml/skills/plantuml-authoring/diagrams/state.md new file mode 100644 index 0000000..4b5b4a7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/state.md @@ -0,0 +1,86 @@ +# State Diagrams + +## Purpose + +Show the lifecycle of a single entity: its possible states, +transitions between them, and the events that trigger those +transitions. + +## Choose this when / avoid when + +- ✅ Order lifecycle, account lifecycle, document workflow +- ✅ Any finite-state machine +- ❌ Cross-object processes → use `activity` +- ❌ Message exchange → use `sequence` + +## Detail-level presets + +- `minimal`: states + transitions labeled with the event. +- `standard`: states with entry/exit actions, guards on transitions, + composite states for groupings. +- `detailed`: internal transitions, history pseudostates, concurrent + regions. + +## Layout tips + +- Direction: LR when the flow is mostly linear; TB for branching. +- Use `[*]` for initial and final pseudostates. +- Compose related states with nested `state "…" { … }`. +- Keep to ≤ 8 states at the top level. + +## Render-engine note (PlantUML 1.2026.x) + +The default `smetana` layout engine (set in `_layout.puml`) **cannot render +state diagrams that use composite states with concurrent regions** (`||` +parallel composition). It emits `CucaDiagramFileMakerSmetana::exportGroup +issue` and silently drops entire regions from the PNG, while keeping the +syntax in the source — so the diagram looks complete but isn't. + +For state diagrams with concurrent regions, override the layout engine +right after the include chain: + +```plantuml +@startuml State_Order_Lifecycle +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!pragma layout dot ' override smetana for concurrent regions +… +@enduml +``` + +The `dot` engine (Graphviz) handles parallel regions correctly; it is +slower but reliable. Apply this override per file, not at the template +level — non-concurrent state diagrams render fine on smetana. + +## Snippet + +```plantuml +@startuml State_Order_Lifecycle +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +[*] --> Draft +Draft --> Submitted : submit() +Submitted --> Paid : paymentOk +Submitted --> Cancelled : paymentKo +Paid --> Shipped : ship() +Shipped --> Delivered : carrierEvent +Delivered --> [*] +Cancelled --> [*] +@enduml +``` + +## Common pitfalls + +- States that are actually activities ("Processing…" with multiple + internal steps). Fix: a state is a *situation*, not a task. +- Unlabeled transitions. Fix: every transition needs an event name + (what triggers it). +- Unreachable or orphan states. Fix: every state must be reachable + from `[*]` and have a path to `[*]` (or be explicitly terminal). +- **Concurrent regions silently disappear with smetana.** Fix: add + `!pragma layout dot` after the include chain (see Render-engine + note above). Verify by checking that `*.png.stderr` contains no + `UNKNOWN ENTITY` lines after rendering. diff --git a/plantuml/skills/plantuml-authoring/diagrams/timing.md b/plantuml/skills/plantuml-authoring/diagrams/timing.md new file mode 100644 index 0000000..3e07288 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/timing.md @@ -0,0 +1,62 @@ +# Timing Diagrams + +## Purpose + +Show how the state of one or more participants changes over a time +axis, including absolute/relative time constraints. + +## Choose this when / avoid when + +- ✅ Hardware signals, protocol timing, real-time systems +- ✅ Explaining lifecycle overlaps across processes +- ❌ Logical ordering without time scale → use `sequence` +- ❌ State transitions of a single entity without time → use `state` + +## Detail-level presets + +- `minimal`: single participant, state changes only. +- `standard`: multiple participants, time constraints labelled + (`@0`, `@+5`). +- `detailed`: numeric time axis, overlapping states, messages + between participants aligned on the axis. + +## Layout tips + +- Timing notation is niche; always add a short legend note. +- Use `concise` for quick overviews, `robust` for full state detail. +- Keep the time axis short and scaled to the story. + +## Snippet + +```plantuml +@startuml Timing_BatteryCharging +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +robust "Battery" as B +concise "Charger" as C + +@0 +B is Empty +C is Idle + +@+1 +C is Detected +B is Charging + +@+10 +B is Full +C is Trickle + +@+11 +C is Idle +@enduml +``` + +## Common pitfalls + +- Using timing where a sequence would do. Fix: prefer `sequence` + unless the time axis is genuinely meaningful. +- Missing legend. Fix: add a note explaining the `robust`/`concise` + distinction if the reader isn't familiar. diff --git a/plantuml/skills/plantuml-authoring/diagrams/usecase.md b/plantuml/skills/plantuml-authoring/diagrams/usecase.md new file mode 100644 index 0000000..1947489 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/usecase.md @@ -0,0 +1,66 @@ +# Use Case Diagrams + +## Purpose + +Show user goals that a system fulfills, the actors involved, and +relationships between use cases (`include`, `extend`). + +## Choose this when / avoid when + +- ✅ Requirements discovery with stakeholders +- ✅ Scoping a release ("which goals are in, which out") +- ❌ Implementation design — use cases give *what*, not *how* +- ❌ Workflow detail → use `activity` + +## Detail-level presets + +- `minimal`: actors + use cases as ovals, simple arrows. +- `standard`: grouping with system boundary rectangle, + `<<include>>`/`<<extend>>` relationships. +- `detailed`: generalization between actors or use cases, notes + describing the preconditions/postconditions per use case. + +## Layout tips + +- Actors on the left and right; use cases in the middle. +- System boundary rectangle encloses the use cases. +- Primary actor(s) on the left, secondary/support on the right. + +## Snippet + +```plantuml +@startuml UseCase_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +left to right direction +actor Customer +actor "Customer Support" as CS + +rectangle "Online Shop" { + (Browse catalog) as UC1 + (Place order) as UC2 + (Track shipment) as UC3 + (Return item) as UC4 + (Process refund) as UC5 +} + +Customer --> UC1 +Customer --> UC2 +Customer --> UC3 +Customer --> UC4 +CS --> UC5 +UC4 ..> UC5 : <<include>> +@enduml +``` + +## Common pitfalls + +- Mixing use cases with system functions ("login", "retrieve data"). + Fix: a use case delivers value *to an actor*; "login" is a means, + not a goal. +- Over-decomposition with `<<include>>`. Fix: use sparingly; a use + case diagram is a map, not a call graph. +- Too many actors. Fix: collapse secondary actors into a category + ("External Systems"), or split the diagram by business area. diff --git a/plantuml/skills/plantuml-authoring/diagrams/wbs.md b/plantuml/skills/plantuml-authoring/diagrams/wbs.md new file mode 100644 index 0000000..6578f1a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/diagrams/wbs.md @@ -0,0 +1,57 @@ +# Work Breakdown Structure + +## Purpose + +Decompose a project into deliverables/work packages in a tree. Each +node is a unit of work, not a concept. + +## Choose this when / avoid when + +- ✅ Project planning, PMO deliverables +- ✅ Estimation scaffolding (pairs with `pmo-pert-estimate` skill) +- ❌ Concept maps → use `mindmap` +- ❌ Process flow → use `activity` + +## Detail-level presets + +- `minimal`: 2 levels, deliverables only. +- `standard`: 3 levels, work packages with owner tag. +- `detailed`: 4 levels, estimate in the node label (e.g., `[5d]`). + +## Layout tips + +- `@startwbs`/`@endwbs`. +- Use WBS codes (1, 1.1, 1.1.1) as a prefix for traceability. +- Keep the tree balanced; a lopsided WBS is a sign of missing work. + +## Snippet + +```plantuml +@startwbs WBS_NewFeature +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +* 1. New Feature +** 1.1 Discovery +*** 1.1.1 Stakeholder interviews +*** 1.1.2 Competitive analysis +** 1.2 Design +*** 1.2.1 UX wireframes +*** 1.2.2 Technical design +** 1.3 Build +*** 1.3.1 Backend +*** 1.3.2 Frontend +*** 1.3.3 Integration +** 1.4 Release +*** 1.4.1 QA +*** 1.4.2 Rollout +@endwbs +``` + +## Common pitfalls + +- WBS with non-deliverable nodes ("meetings", "reviews"). Fix: those + are activities, not work packages. Put them in a plan, not a WBS. +- No WBS codes. Fix: codes are what make the WBS referenceable + elsewhere (budget lines, Gantt tasks). diff --git a/plantuml/skills/plantuml-authoring/principles.md b/plantuml/skills/plantuml-authoring/principles.md new file mode 100644 index 0000000..9ed4478 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/principles.md @@ -0,0 +1,184 @@ +# PlantUML Authoring — Design Principles + +Universal principles. Loaded on any authoring task. Each principle has a +rationale and a short bad-vs-good example. + +## 1. Audience-first + +**Rule:** The first question before drawing anything is "who reads this?" +Executive, architect, dev, QA, auditor? Detail level, vocabulary, and +notation follow from the answer. + +**Why:** A sequence diagram that satisfies a dev ("show every return +arrow") suffocates an executive; one that pleases an executive ("just +happy path") leaves the dev guessing. + +**Bad:** Drawing "a login sequence" without asking. You default to +developer-grade detail and the PM can't read it. +**Good:** Ask. Or infer from context (PRD → exec-leaning; runbook → +ops-leaning; design doc → dev-leaning) and state the assumption +in a top-of-file comment. + +## 2. One diagram = one message + +**Rule:** Every `.puml` communicates exactly one thing. If you catch +yourself adding a subtitle to explain "also this", split. + +**Why:** A diagram is read in one glance; if the glance yields two +takeaways, none of them lands. + +**Bad:** A class diagram that shows both the domain model *and* the +persistence mapping. +**Good:** Two files, `domain-model.puml` and `persistence-mapping.puml`, +cross-referenced with `note` directives. + +## 3. Progressive disclosure + +**Rule:** Overview first in its own diagram; drill-down in separate +diagrams linked from the overview. Never cram drill-downs into the +overview. + +When generating multiple detail levels (`minimal` / `standard` / +`detailed`) of the same diagram, the levels MUST share a common spine: +each step adds depth along the *same* axes, never new orthogonal +content. `detailed` is a strict superset of `standard` along the +already-present axes; `standard` is a strict superset of `minimal`. +A reader who sees `minimal → standard → detailed` should be able to +predict `detailed`'s structure from `standard`'s. + +**Why:** A 40-node component diagram is unreadable regardless of layout. +Context + container + component (C4-style) is readable at each level. +Adversarial reviewers consistently flag detail levels that introduce +new entities at higher tiers as "three different diagrams of roughly +the same topic, not a zoom-in on one model". + +**Detailed is the riskiest preset.** If `detailed` degrades readability +or introduces semantic ambiguity vs `standard`, prefer `standard` and +split the extra content into a separate sibling diagram. More density +≠ more value. + +**Bad:** One component diagram with 40 components and 80 arrows. +**Bad:** `standard` shows 4 services; `detailed` adds a saga + queue + +DR replica + observability stack — 4 new categories at once, with no +intermediate. Reader cannot trace the gradient. +**Good:** A 5-container overview; each container expanded into its own +component diagram. +**Good:** `minimal` 3 entities, `standard` 5 entities, `detailed` 7 +entities — same entities present at every tier, more attributes / +notes / relations added. + +## 4. Layout follows flow, not convenience + +**Rule:** The diagram's layout encodes its semantics. Temporal flows go +top-to-bottom. Dependency flows go in the direction of the dependency. +Class inheritance trees may use left-to-right if that's the story. + +**Why:** Fighting the default layout wastes hours; aligning with +semantics makes the default layout look right. + +**Bad:** A sequence with participants in random order. +**Good:** A sequence with participants in *call order*, left to right. + +## 5. Names over technology + +**Rule:** Label nodes, arrows, and messages with domain concepts, not +tech. "Order submitted" > "POST /api/v1/orders". Tech names appear only +when the diagram *is about* tech (deployment, component-level HTTP). + +**Why:** A diagram about business logic littered with HTTP verbs +confuses the signal. If the arrow label reads like an API spec, the +diagram is pretending to be something it isn't. + +**Bad:** `Web → API : POST /orders` in a business-process sequence. +**Good:** `Web → API : Place order` (plus a note if the HTTP binding +matters). + +## 6. Color is semantic, not decorative + +**Rule:** Color carries information: category, lifecycle, severity, team +ownership. If removing color leaves the message intact, the color wasn't +semantic — remove it. + +**Why:** Decorative color increases cognitive load and clashes with +accessibility. Semantic color compresses information. + +**Bad:** Every class in a different random color. +**Good:** Red for deprecated, amber for in-progress, green for stable +— *and nothing else*. + +## 7. Annotations replace re-drawing + +**Rule:** When you want to say "in case X, this differs": add a `note`, +don't draw a second parallel diagram showing the same thing with one +arrow swapped. + +**Why:** Two near-identical diagrams force the reader to diff them +mentally. A note on one diagram is explicit. + +**Bad:** `sequence-happy.puml` + `sequence-error.puml`, 90% identical. +**Good:** One sequence with `alt/else` or with a `note right of …` +describing the error path. + +## 8. Title every diagram + +**Rule:** Every `.puml` carries a `title "Human readable caption"` +directive on its own line, in addition to the `@startuml <id>` token. + +**Why:** PlantUML names the rendered image after the `@startuml` id, +not the source filename — so the id must be unique per file (otherwise +two diagrams in the same output dir overwrite each other). The id is +a tech identifier; the `title` is what the reader sees in a DOCX +caption, a slide header, or a PDF figure list. Most adversarial +review failures around "no caption" trace back to authors using the +id as if it were the title. + +**Conventions:** +- `@startuml <Type>_<Subject>_<Variant>` — unique, mechanically derived, + matches filename for batch rendering. +- `title "<Subject> — <intent>"` — human, e.g. `title "Checkout — happy + path"`. Italian/other-locale titles only when the project's `Label + language` Policy key is non-`en`. + +**Bad:** `@startuml Sequence_Checkout` in three sibling files; no +`title`. All three render to `Sequence_Checkout.png`. +**Good:** `@startuml Sequence_Checkout_Standard` + `title "Checkout — +happy path with payment alternatives"`. + +## 9. One convention per diagram + +**Rule:** Pick a labeling, naming, or notation convention before +drawing, and don't drift mid-diagram. + +**Why:** Mixing `submit()` (method call) with `paymentOk` (event name) +on the same sequence forces the reader to context-switch on every +arrow. Same for ArchiMate `Triggering` ↔ `Assignment` for the same +relation type, or for prefix-style ids (`o_771`) interleaved with +natural names (`anne`). + +**How to apply:** +- Decide upfront: methods or events, prefixed or natural ids, sync or + async arrows, `<<include>>` or `..>` for the same semantic. +- If a diagram needs two conventions (rare), separate them spatially + (e.g., one swimlane per convention) and note why. + +## 10. Diagrams are code + +**Rule:** Version the `.puml` source. Rendered PNG/SVG/PDF are build +artifacts and go into `/dist`, `/build`, or `.gitignore`d directories +— unless a specific workflow (legal sign-off, offline review, print +deliverables) requires checking them in. + +**Why:** Binary artifacts blow up git history; regenerating from source +is cheap. + +**Bad:** Committing both `foo.puml` and `foo.png`, updating them by hand +out of sync. +**Good:** Commit `foo.puml` only; render on demand (CI or `plantuml- +convert`). + +--- + +**Usage in the workflow:** these principles are checks applied during +step 4 ("Emit `.puml`") and step 5 ("Validate") of the SKILL.md +workflow. An adversarial reviewer (test harness) uses them as evaluation +axes. diff --git a/plantuml/skills/plantuml-authoring/project-config.md b/plantuml/skills/plantuml-authoring/project-config.md new file mode 100644 index 0000000..447f046 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/project-config.md @@ -0,0 +1,195 @@ +# Project Configuration + +How a project declares its PlantUML identity (styling, target, brand) and +how the skill bootstraps / syncs that configuration. + +## Source of truth: CLAUDE.md + +Every configured project has a "PlantUML Policy" section in its +CLAUDE.md. This section is human-readable, machine-parseable by the +skill, and takes precedence over any file in `.plantuml/`. + +### Section schema + +```markdown +## PlantUML Policy + +- **Primary target**: `docx` # web | docx | pdf | pptx +- **Additional targets**: `web` # optional, comma-separated +- **Theme**: `cerulean-outline` # PlantUML built-in theme name OR `custom` +- **Layout engine**: `smetana` # smetana | elk | dot +- **Default direction**: `top-to-bottom` # top-to-bottom | left-to-right +- **Default detail level**: `standard` # minimal | standard | detailed +- **Label language**: `en` # en | it | fr | … +- **Brand colors**: + - primary: `#0B5FFF` + - accent: `#F59E0B` + - neutral: `#475569` + - surface: `#F8FAFC` + - danger: `#DC2626` +- **Font family**: `Inter, Arial, sans-serif` +- **Base font size**: `14` +- **Max width (docx)**: `5800` +- **Recorded on**: `YYYY-MM-DD` +``` + +### Parsing rules + +- Keys are the text between `**…**:` markers, case-sensitive. +- Values are everything after `:` until `#` (comment) or end-of-line, + trimmed. +- Nested lists (brand colors) are parsed as sub-key → value. +- Missing optional keys fall back to documented defaults; missing + required keys (Primary target, Theme, Brand colors) trigger + bootstrap. + +## Generated artifacts: `.plantuml/` + +From the Policy the skill generates (and keeps in sync) the directory +`.plantuml/` at the project root, containing: + +``` +.plantuml/ +├── _base.puml ← single include entry-point +├── _brand.puml ← !$primary, !$accent, ... variables +├── _fonts.puml ← font family + size +├── _theme.puml ← !theme + global skinparam +├── _layout.puml ← direction + !pragma layout +└── _targets/ + ├── web.puml + ├── docx.puml + ├── pdf.puml + └── pptx.puml +``` + +Treat `.plantuml/` like a lockfile: it is always regenerable from the +Policy. Do not hand-edit files there; edit the Policy and sync. + +## Sync semantics + +- **Policy present, `.plantuml/` absent:** regenerate everything. +- **Policy present, `.plantuml/` present:** diff generated content vs + existing files. If different, overwrite with a diff shown to the + user in dry-run first. +- **Policy absent, `.plantuml/` present:** reverse-init — extract a + Policy draft from existing files and ask the user to review it + before adding to CLAUDE.md. +- **Policy absent, `.plantuml/` absent:** bootstrap dialog (see below). + +## Bootstrap dialog (progressive) + +Trigger: first `.puml` authoring request in a project where both Policy +and `.plantuml/` are missing. + +Script: + +1. Ask the user: *"This project has no PlantUML configuration. Do you + want a shared project setup (recommended) or a one-shot diagram + with inline defaults?"* +2. On **one-shot**: generate the diagram inline with safe defaults + (`theme plain`, target `docx`, font `Inter, 14`, no brand colors). + Prepend comment: `' TODO: run /plantuml-init to share styling`. + Skip to rendering. +3. On **setup**: run the wizard: + - *"Primary render target? (web / docx / pdf / pptx)"* + - *"Theme: built-in PlantUML theme name, or `custom`?"* + - If `custom`: ask for 5 brand color hex values (primary, + accent, neutral, surface, danger). + - *"Font family? (default: Inter, Arial, sans-serif)"* + - *"Label language? (en / it / fr / … — default: en)"* + - *"Default detail level? (minimal / standard / detailed — default + standard)"* +4. Generate the CLAUDE.md Policy section (append if CLAUDE.md exists, + create it otherwise). +5. Generate `.plantuml/` (see next section). +6. Generate the requested diagram using the freshly configured setup. +7. Confirm to user: *"Setup complete. Policy in CLAUDE.md + `.plantuml/` + generated. First diagram at <path>."* + +## Generation details + +### `_brand.puml` + +Emit one `!$variable` per brand color + a `skinparam` mapping to hint +themes that honor variable-based palettes. Example output: + +```plantuml +' Auto-generated from CLAUDE.md "PlantUML Policy". Do not edit by hand. +!$primary = "#0B5FFF" +!$accent = "#F59E0B" +!$neutral = "#475569" +!$surface = "#F8FAFC" +!$danger = "#DC2626" +``` + +### `_fonts.puml` + +```plantuml +skinparam defaultFontName "Inter, Arial, sans-serif" +skinparam defaultFontSize 14 +``` + +### `_theme.puml` + +If Policy theme is a built-in: +```plantuml +!theme cerulean-outline +skinparam backgroundColor $surface +skinparam ArrowColor $neutral +skinparam DefaultTextAlignment center +``` + +If `custom`, skip `!theme` and emit a full skinparam block derived from +brand variables (class background `$surface`, class border `$neutral`, +highlight `$accent`, error `$danger`, etc.). The emitter is documented +in the templates. + +### `_layout.puml` + +```plantuml +!pragma layout smetana +skinparam linetype ortho +skinparam shadowing true +' direction is set per-diagram unless overridden here +``` + +If the Policy `Default direction` is `left-to-right`, append +`left to right direction` on its own line. + +### `_base.puml` + +```plantuml +' Auto-generated from CLAUDE.md "PlantUML Policy". +' Single include entry-point. Do not add target-specific overrides here. +!include _brand.puml +!include _fonts.puml +!include _theme.puml +!include _layout.puml +``` + +### `_targets/<target>.puml` + +See the templates directory for each target's exact content. Example +for `docx`: + +```plantuml +skinparam shadowing false +skinparam defaultFontSize 14 +skinparam dpi 150 +' No hyperlink support in flat PNG rendering; nothing to toggle. +``` + +## Reverse-init (Policy missing, .plantuml/ exists) + +Script parses existing `.plantuml/` to reconstruct a Policy draft: + +- `_brand.puml` → brand colors. +- `_fonts.puml` → font family + base size. +- `_theme.puml` → theme name (grep first `!theme …` line) or `custom`. +- `_layout.puml` → layout engine + direction. +- `_targets/` listing → Primary target (first alphabetically found) + + additional targets (others). +- Max width: inferred from `_targets/docx.puml` `defaultMaxWidth` if + present, else default 5800. + +The user reviews the draft, edits, accepts → Policy added to CLAUDE.md. diff --git a/plantuml/skills/plantuml-authoring/render-profiles.md b/plantuml/skills/plantuml-authoring/render-profiles.md new file mode 100644 index 0000000..5d9b939 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/render-profiles.md @@ -0,0 +1,103 @@ +# Render Profiles + +Target-aware rendering from neutral sources. The `.puml` source is +target-agnostic; the `PLANTUML_TARGET` env var selects a target at render +time, and the project's `.plantuml/_targets/<target>.puml` is included +accordingly. + +## Supported targets + +| Target | Format | Scale | DPI | Font size | Max px width | Direction | Hyperlinks | Shadowing | +|--------|--------|-------|------|-----------|--------------|---------------|------------|-----------| +| `web` | svg | n/a | n/a | 14 | — | top-to-bottom | enabled | true | +| `docx` | png | 3 | 150 | 14 | 5800 | top-to-bottom | disabled | false | +| `pdf` | pdf | n/a | 300 | 12 | — | top-to-bottom | enabled | true | +| `pptx` | png | 3 | 150 | 18 | 4800 (16:9) | left-to-right | disabled | false | + +**Rationale per target:** +- **web**: SVG preserves quality at any zoom, supports hyperlinks. No + DPI concept; font size 14 reads well in browsers. +- **docx**: PNG at scale 3 / 150 DPI looks crisp in Word at 100% zoom + without bloating the document. Shadowing off = cleaner in print. + Max 5800 px keeps a single diagram within A4 portrait at ~150 DPI. +- **pdf**: Direct PDF output keeps vectors. 300 DPI is print-friendly. +- **pptx**: 16:9 slides, landscape direction by default. Larger font + size because slides are seen from distance. + +## Invocation pattern + +The skill composes the call to `plantuml-convert` like this: + +```bash +# 1. Pick target from CLAUDE.md Policy or user override. +TARGET="docx" # or web | pdf | pptx + +# 2. Export env var so !getenv("PLANTUML_TARGET") in the .puml resolves. +export PLANTUML_TARGET="$TARGET" + +# 3. Map target → plantuml-convert CLI args. +case "$TARGET" in + web) FMT=svg; SCALE_ARGS="";; + docx) FMT=png; SCALE_ARGS="-Sscale=3";; + pdf) FMT=pdf; SCALE_ARGS="";; + pptx) FMT=png; SCALE_ARGS="-Sscale=3";; +esac + +# 4. Invoke plantuml (plantuml-convert skill handles the actual call). +OUT="$(pwd)/dist/diagrams" +mkdir -p "$OUT" +plantuml "-t$FMT" $SCALE_ARGS -o "$OUT" "$SOURCE.puml" +``` + +**Important:** `PLANTUML_TARGET` must be set *before* `plantuml` runs; +PlantUML evaluates `%getenv(...)` at parse time. If it is unset the +source file's `!include` of `_targets/$target.puml` will fail — +this is intentional: it forces the caller to declare a target. + +## Default target resolution + +1. If user specified a target explicitly in the request → use it. +2. Else if CLAUDE.md Policy has `Primary target: <X>` → use X. +3. Else if Policy has `Additional targets:` → use the first one. +4. Else → `docx` (the enterprise-defaults fallback). + +## Multi-target rendering + +If the project's Policy lists additional targets (e.g., `Primary target: +docx`, `Additional targets: web`), the skill renders each diagram once +per target by looping: + +```bash +for T in docx web; do + export PLANTUML_TARGET="$T" + # … render all .puml into dist/$T/ +done +``` + +Output is kept in per-target subdirectories so consumers can pick the +right format. + +## Max-width post-check (docx, pptx) + +After rendering PNG, run: + +```bash +W=$(identify -format "%w" "out.png") +[ "$W" -le "$MAX" ] || echo "WARN: $out.png is $W px, max is $MAX" +``` + +Over-wide diagrams are a warning, not an error: the author may have +intentionally laid out a wide landscape that the consumer will scale to +fit. The skill flags the warning and asks the author whether to re-lay +out (change direction, split the diagram) or accept. + +## Adding a new target + +1. Append a row to the table above. +2. Create `templates/_targets/<name>.puml` with the target-specific + `skinparam` overrides. +3. Add the `case` branch to the invocation pattern. +4. Re-run the test suite to validate the new target end-to-end. + +No changes needed in individual diagram files — that is the whole point +of keeping sources target-neutral. diff --git a/plantuml/skills/plantuml-authoring/scripts/aggregate-reviews.sh b/plantuml/skills/plantuml-authoring/scripts/aggregate-reviews.sh new file mode 100755 index 0000000..8d14be3 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/scripts/aggregate-reviews.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Aggregate per-variant review.md files into a run-level _report.md. +# Usage: aggregate-reviews.sh <run-dir> +# +# Verdict matching is tolerant: reviewers used many non-standard +# strings in run-01 (REJECT, PASS-WITH-RESERVATIONS, ACCEPT WITH +# CHANGES, FAIL ON RENDERING, ACCEPTABLE-WITH-FIXES …). The classifier +# normalizes them to PASS / WARN / FAIL / UNKNOWN. +set -euo pipefail + +RUN_DIR="${1:-$(cat /tmp/plantuml-tests-latest 2>/dev/null || true)}" +[ -n "$RUN_DIR" ] && [ -d "$RUN_DIR" ] || { echo "Usage: $0 <run-dir>"; exit 1; } + +pass=0; warn=0; fail=0; unknown=0 +fails_list="" +unknowns_list="" +issues_file=$(mktemp) +trap "rm -f $issues_file" EXIT + +# Map a verdict string to one of PASS / WARN / FAIL / UNKNOWN. +classify() { + local v + v=$(echo "$1" | tr '[:lower:]' '[:upper:]') + case "$v" in + *FAIL*|*REJECT*|*BLOCK*) echo FAIL ;; + *WARN*|*RESERVATION*|*CONDITIONAL*|*WITH-CHANGE*|*WITH-FIX*|*ACCEPTABLE*|*PASS-WITH*) echo WARN ;; + *PASS*|*ACCEPT*|*OK*) echo PASS ;; + "") echo UNKNOWN ;; + *) echo UNKNOWN ;; + esac +} + +shopt -s nullglob +for review in "$RUN_DIR"/*/*/review.md; do + raw=$(grep -m1 -iE "^(verdict)[[:space:]]*[:|]" "$review" 2>/dev/null \ + | sed -E 's/^[Vv]erdict[[:space:]]*[:|][[:space:]]*//; s/[*_`]//g; s/[[:space:]]+$//') + cls=$(classify "$raw") + case "$cls" in + PASS) pass=$((pass+1));; + WARN) warn=$((warn+1));; + FAIL) fail=$((fail+1)); fails_list="${fails_list}- $review (\"$raw\")"$'\n';; + UNKNOWN) unknown=$((unknown+1)); unknowns_list="${unknowns_list}- $review (raw=\"$raw\")"$'\n';; + esac + + awk -F'|' ' + /^\|/ && /(BLOCKER|HIGH|MEDIUM|LOW)/ { + sev=$2; axis=$3; + gsub(/^[[:space:]]+|[[:space:]]+$/, "", sev); + gsub(/^[[:space:]]+|[[:space:]]+$/, "", axis); + if (sev != "" && axis != "") print sev"\t"axis; + } + ' "$review" >> "$issues_file" +done +shopt -u nullglob + +total=$((pass + warn + fail + unknown)) +top10=$(sort "$issues_file" | uniq -c | sort -rn | head -10 || true) + +{ + echo "# Adversarial Review Report — $(basename "$RUN_DIR")" + echo + echo "## Counters" + echo + echo "- PASS: $pass" + echo "- WARN (PASS-WITH-WARNINGS / RESERVATIONS / etc): $warn" + echo "- FAIL (FAIL / REJECT / BLOCK): $fail" + echo "- UNKNOWN (no verdict line found): $unknown" + echo "- Total reviews scanned: $total" + echo + echo "## Failures requiring fix" + echo + if [ -n "$fails_list" ]; then printf "%s" "$fails_list"; else echo "None."; fi + echo + echo "## Reviews with unparseable verdict" + echo + if [ -n "$unknowns_list" ]; then printf "%s" "$unknowns_list"; else echo "None."; fi + echo + echo "## Top 10 systemic issues (count · severity · axis)" + echo + echo '```' + [ -n "$top10" ] && echo "$top10" || echo "No issues recorded." + echo '```' + echo + echo "## Exit" + echo + if [ "$fail" -eq 0 ]; then + echo "OK — suite passes (warnings may remain; see WARN list above)." + else + echo "BLOCKED — fix failures." + fi +} > "$RUN_DIR/_report.md" + +echo "Report: $RUN_DIR/_report.md" +[ "$fail" -eq 0 ] diff --git a/plantuml/skills/plantuml-authoring/scripts/generate-variants.sh b/plantuml/skills/plantuml-authoring/scripts/generate-variants.sh new file mode 100755 index 0000000..9aef863 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/scripts/generate-variants.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Copy the canonical test-variants into a fresh timestamped run dir. +set -euo pipefail + +SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VARIANTS_DIR="$SKILL_DIR/test-variants" + +if [ ! -d "$VARIANTS_DIR" ] || [ -z "$(ls -A "$VARIANTS_DIR" 2>/dev/null)" ]; then + echo "ERROR: $VARIANTS_DIR is missing or empty. Run seed-variants.sh first." + exit 1 +fi + +RUN_DATE="$(date +%Y-%m-%d)" +# Next run number (ls failure on no-match is fine under pipefail with || true) +existing=$( (ls -d "$HOME/temp/plantuml-tests/${RUN_DATE}-run-"* 2>/dev/null || true) | wc -l | awk '{print $1}') +next=$(printf '%02d' $((existing + 1))) +RUN_DIR="$HOME/temp/plantuml-tests/${RUN_DATE}-run-${next}" +mkdir -p "$RUN_DIR" +echo "Run directory: $RUN_DIR" + +for type_dir in "$VARIANTS_DIR"/*/; do + type_name=$(basename "$type_dir") + for variant in minimal standard detailed; do + src="$type_dir/$variant.puml" + if [ -f "$src" ]; then + dst="$RUN_DIR/$type_name/$variant" + mkdir -p "$dst" + cp "$src" "$dst/" + else + echo "SKIP: $type_name/$variant (no source)" + fi + done +done + +cat > "$RUN_DIR/README.md" <<EOF +# PlantUML Test Run — $(date -u +"%Y-%m-%dT%H:%M:%SZ") + +Variants generated from $VARIANTS_DIR. + +Next steps: + 1. bash scripts/render-variants.sh $RUN_DIR + 2. Dispatch adversarial reviewers (see SKILL.md "Test harness") + 3. bash scripts/aggregate-reviews.sh $RUN_DIR +EOF + +echo "$RUN_DIR" > /tmp/plantuml-tests-latest +echo "$RUN_DIR" diff --git a/plantuml/skills/plantuml-authoring/scripts/render-variants.sh b/plantuml/skills/plantuml-authoring/scripts/render-variants.sh new file mode 100755 index 0000000..662e577 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/scripts/render-variants.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Render every variant in a run directory to SVG + PNG. +# Usage: render-variants.sh <run-dir> +set -euo pipefail + +RUN_DIR="${1:-$(cat /tmp/plantuml-tests-latest 2>/dev/null || true)}" +[ -n "$RUN_DIR" ] && [ -d "$RUN_DIR" ] || { echo "Usage: $0 <run-dir>"; exit 1; } + +failed=0 +for puml in "$RUN_DIR"/*/*/*.puml; do + dir=$(dirname "$puml") + base=$(basename "$puml" .puml) + echo "Rendering $puml" + + # SVG (web target) + if ! PLANTUML_TARGET=web plantuml -tsvg -o "$dir" "$puml" 2>"$dir/$base.svg.stderr"; then + echo " SVG render FAILED (see $dir/$base.svg.stderr)" + failed=$((failed + 1)) + fi + + # PNG (docx target) + if ! PLANTUML_TARGET=docx plantuml -tpng -Sscale=3 -o "$dir" "$puml" 2>"$dir/$base.png.stderr"; then + echo " PNG render FAILED (see $dir/$base.png.stderr)" + failed=$((failed + 1)) + fi +done + +# Post-check: PNG max width for docx target +echo +echo "Checking PNG widths (docx max 5800 px)…" +for png in "$RUN_DIR"/*/*/*.png; do + [ -f "$png" ] || continue + w=$(identify -format "%w" "$png" 2>/dev/null || echo 0) + if [ "$w" -gt 5800 ]; then + echo " WARN: $png is ${w}px wide" + fi +done + +echo +if [ "$failed" -gt 0 ]; then + echo "Render complete with $failed failure(s)." + exit 1 +else + echo "Render complete; no failures." +fi diff --git a/plantuml/skills/plantuml-authoring/scripts/run-test-suite.sh b/plantuml/skills/plantuml-authoring/scripts/run-test-suite.sh new file mode 100755 index 0000000..9cc7f87 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/scripts/run-test-suite.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Top-level orchestrator. Steps: +# 1. Generate variants (copy from test-variants/ into a fresh run dir) +# 2. Render to SVG + PNG +# 3. (Agent responsibility) Dispatch adversarial reviewers — NOT done here +# 4. Aggregate reviews (must be run after reviews exist) +set -euo pipefail + +SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SCRIPTS="$SKILL_DIR/scripts" + +echo "1/3 Generating variants…" +"$SCRIPTS/generate-variants.sh" +RUN_DIR="$(cat /tmp/plantuml-tests-latest)" + +echo +echo "2/3 Rendering…" +"$SCRIPTS/render-variants.sh" "$RUN_DIR" || echo "(render had failures — see above)" + +echo +echo "3/3 Reviewer dispatch is an agent step." +echo "See SKILL.md §\"Test harness — reviewer dispatch\" for the protocol." +echo +echo "After agent produces review.md files, run:" +echo " $SCRIPTS/aggregate-reviews.sh $RUN_DIR" +echo +echo "Run directory: $RUN_DIR" diff --git a/plantuml/skills/plantuml-authoring/scripts/seed-variants.sh b/plantuml/skills/plantuml-authoring/scripts/seed-variants.sh new file mode 100755 index 0000000..aa065f3 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/scripts/seed-variants.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Seed test-variants/<type>/{minimal,standard,detailed}.puml from the +# snippet inside each diagrams/<type>.md. +# Standard = snippet as-is (with .plantuml/ rewritten to absolute paths). +# Minimal and detailed are seeded from the standard with a TODO marker; +# they require hand-editing per type. +set -euo pipefail + +SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VAR_DIR="$SKILL_DIR/test-variants" +DIAG_DIR="$SKILL_DIR/diagrams" +TPL_DIR="$SKILL_DIR/templates" + +mkdir -p "$VAR_DIR" + +for f in "$DIAG_DIR"/*.md; do + type=$(basename "$f" .md) + [ "$type" = "INDEX" ] && continue + + # Extract the first @start.../@end... block from the file. + block=$(awk ' + /^@start(uml|mindmap|wbs|gantt|salt|json|yaml|nwdiag)/ { p=1 } + p { print } + /^@end(uml|mindmap|wbs|gantt|salt|json|yaml|nwdiag)/ { if (p) { p=0; exit } } + ' "$f") + + if [ -z "$block" ]; then + echo "SKIP $type: no @start/@end block" + continue + fi + + mkdir -p "$VAR_DIR/$type" + + # Rewrite relative .plantuml/ paths to the skill's templates dir so + # variants render without requiring a project setup. + std="$VAR_DIR/$type/standard.puml" + echo "$block" | sed " + s|!include \.plantuml/_base\.puml|!include $TPL_DIR/_base.puml|g + s|!include \.plantuml/_targets/\$target\.puml|!include $TPL_DIR/_targets/\$target.puml|g + " > "$std" + + # Minimal and detailed variants require hand-editing. + if [ ! -f "$VAR_DIR/$type/minimal.puml" ]; then + cp "$std" "$VAR_DIR/$type/minimal.puml" + printf "\n' TODO: reduce to minimal variant (<= 1/3 of standard)\n" >> "$VAR_DIR/$type/minimal.puml" + fi + if [ ! -f "$VAR_DIR/$type/detailed.puml" ]; then + cp "$std" "$VAR_DIR/$type/detailed.puml" + printf "\n' TODO: expand to detailed variant (1.5-2x standard with notes)\n" >> "$VAR_DIR/$type/detailed.puml" + fi + + echo "Seeded $type" +done + +echo +echo "Done. Hand-edit minimal and detailed per type before running the test suite." diff --git a/plantuml/skills/plantuml-authoring/templates/_base.puml b/plantuml/skills/plantuml-authoring/templates/_base.puml new file mode 100644 index 0000000..6fa6e53 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_base.puml @@ -0,0 +1,16 @@ +' Base — auto-generated from CLAUDE.md "PlantUML Policy". +' Single include entry-point. Do NOT add target-specific overrides here; +' those live in _targets/<target>.puml and are included per-diagram via +' the PLANTUML_TARGET env var. + +' Fallback for $target if PLANTUML_TARGET env var is unset, so the diagram +' still renders (web-target defaults) instead of failing on a missing +' _targets/.puml include. +!if (%not(%variable_exists("$target")) || $target == "") + !$target = "web" +!endif + +!include _brand.puml +!include _fonts.puml +!include _theme.puml +!include _layout.puml diff --git a/plantuml/skills/plantuml-authoring/templates/_brand.puml b/plantuml/skills/plantuml-authoring/templates/_brand.puml new file mode 100644 index 0000000..04805f3 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_brand.puml @@ -0,0 +1,8 @@ +' Brand colors — auto-generated from CLAUDE.md "PlantUML Policy". +' Do not edit by hand. Re-run plantuml-init to regenerate. + +!$primary = "#0B5FFF" +!$accent = "#F59E0B" +!$neutral = "#475569" +!$surface = "#F8FAFC" +!$danger = "#DC2626" diff --git a/plantuml/skills/plantuml-authoring/templates/_fonts.puml b/plantuml/skills/plantuml-authoring/templates/_fonts.puml new file mode 100644 index 0000000..10d7d6c --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_fonts.puml @@ -0,0 +1,4 @@ +' Fonts — auto-generated from CLAUDE.md "PlantUML Policy". + +skinparam defaultFontName "Inter, Arial, sans-serif" +skinparam defaultFontSize 14 diff --git a/plantuml/skills/plantuml-authoring/templates/_layout.puml b/plantuml/skills/plantuml-authoring/templates/_layout.puml new file mode 100644 index 0000000..ce12bfb --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_layout.puml @@ -0,0 +1,13 @@ +' Layout — auto-generated from CLAUDE.md "PlantUML Policy". + +' smetana is faster but breaks on concurrent state regions and complex +' compositions. Diagram types that need richer layout (state with +' parallel regions, deeply-nested classes) should override per file +' with `!pragma layout dot` after this include. +!pragma layout smetana +skinparam linetype ortho +skinparam shadowing true +skinparam minClassWidth 90 +skinparam nodesep 60 +skinparam ranksep 80 +skinparam padding 8 diff --git a/plantuml/skills/plantuml-authoring/templates/_targets/docx.puml b/plantuml/skills/plantuml-authoring/templates/_targets/docx.puml new file mode 100644 index 0000000..a2cbf3c --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_targets/docx.puml @@ -0,0 +1,5 @@ +' Target: docx (PNG embedded in Word). +skinparam shadowing false +skinparam defaultFontSize 14 +skinparam dpi 150 +' No hyperlink rendering in flat PNG; keep labels self-explanatory. diff --git a/plantuml/skills/plantuml-authoring/templates/_targets/pdf.puml b/plantuml/skills/plantuml-authoring/templates/_targets/pdf.puml new file mode 100644 index 0000000..ef10f00 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_targets/pdf.puml @@ -0,0 +1,4 @@ +' Target: pdf (vector, print-safe). +skinparam shadowing true +skinparam defaultFontSize 12 +skinparam dpi 300 diff --git a/plantuml/skills/plantuml-authoring/templates/_targets/pptx.puml b/plantuml/skills/plantuml-authoring/templates/_targets/pptx.puml new file mode 100644 index 0000000..db1c182 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_targets/pptx.puml @@ -0,0 +1,5 @@ +' Target: pptx (PNG on 16:9 slides). +skinparam shadowing false +skinparam defaultFontSize 18 +skinparam dpi 150 +left to right direction diff --git a/plantuml/skills/plantuml-authoring/templates/_targets/web.puml b/plantuml/skills/plantuml-authoring/templates/_targets/web.puml new file mode 100644 index 0000000..e17cedd --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_targets/web.puml @@ -0,0 +1,4 @@ +' Target: web (SVG, interactive). +skinparam shadowing true +skinparam defaultFontSize 14 +' Hyperlinks are honored by SVG viewers; no additional toggle needed. diff --git a/plantuml/skills/plantuml-authoring/templates/_theme.puml b/plantuml/skills/plantuml-authoring/templates/_theme.puml new file mode 100644 index 0000000..122cb40 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/templates/_theme.puml @@ -0,0 +1,172 @@ +' Theme — auto-generated from CLAUDE.md "PlantUML Policy". +' This is the `custom` variant; when Policy names a built-in theme the +' init script replaces this file with a one-liner `!theme <name>` plus +' a background color override. + +skinparam backgroundColor $surface +skinparam ArrowColor $neutral +skinparam DefaultTextAlignment center + +skinparam class { + BackgroundColor $surface + BorderColor $neutral + HeaderBackgroundColor $primary + AttributeFontColor $neutral + ArrowColor $neutral +} + +skinparam object { + BackgroundColor $surface + BorderColor $neutral + HeaderBackgroundColor $primary + AttributeFontColor $neutral + ArrowColor $neutral +} + +skinparam entity { + BackgroundColor $surface + BorderColor $neutral + HeaderBackgroundColor $primary + AttributeFontColor $neutral +} + +' PlantUML 1.2026.2 ignores skinparam *HeaderBackgroundColor / *FontColor +' for class/object/entity headers — they are governed by the new <style> +' mechanism. We override here so class/object/entity names render in +' white on the branded header band. +<style> +classDiagram { + class { + header { + FontColor #FFFFFF + } + } + entity { + header { + FontColor #FFFFFF + } + } +} +objectDiagram { + object { + BackgroundColor $surface + LineColor $neutral + header { + BackgroundColor $primary + FontColor #FFFFFF + } + } +} +</style> + +skinparam component { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam note { + BackgroundColor #FFFBEB + BorderColor $accent +} + +skinparam stereotype { + CBackgroundColor $accent + CBorderColor $neutral +} + +' Sequence-diagram and actor-bearing elements (sequence, communication, +' usecase) — without these the brand surface/neutral fall back to +' PlantUML defaults (off-white / gray) and the diagrams look +' unbranded compared to class/component. + +skinparam actor { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam participant { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam sequence { + ArrowColor $neutral + LifeLineBorderColor $neutral + LifeLineBackgroundColor $surface + ParticipantBackgroundColor $surface + ParticipantBorderColor $neutral + ParticipantFontColor $neutral + ActorBackgroundColor $surface + ActorBorderColor $neutral + ActorFontColor $neutral + GroupBackgroundColor $surface + GroupBorderColor $neutral + BoxBorderColor $neutral +} + +skinparam database { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam queue { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam usecase { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam state { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral + ArrowColor $neutral + StartColor $primary + EndColor $neutral +} + +skinparam activity { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral + StartColor $primary + EndColor $neutral + DiamondBackgroundColor $surface + DiamondBorderColor $neutral + DiamondFontColor $neutral + ArrowColor $neutral + BarColor $accent +} + +skinparam package { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam node { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam cloud { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} + +skinparam artifact { + BackgroundColor $surface + BorderColor $neutral + FontColor $neutral +} diff --git a/plantuml/skills/plantuml-authoring/test-variants/activity/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/activity/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/activity/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/activity/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/activity/detailed.puml new file mode 100644 index 0000000..45f49df --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/activity/detailed.puml @@ -0,0 +1,46 @@ +@startuml Activity_Return_Process +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +start +:Receive return request; + +partition "Customer Care" { + :Validate RMA token; + if (Token valid?) then (yes) + else (no) + :Reject and notify; + stop + endif +} + +partition "Warehouse" { + :Schedule pickup; + :Receive package; + :Inspect item; + switch (Item condition?) + case (resellable) + :Restock; + case (refurbishable) + :Send to refurb; + case (damaged) + :Scrap; + endswitch +} + +partition "Finance" { + fork + :Issue refund; + fork again + :Update accounting; + fork again + :Reverse loyalty points; + end fork +} + +partition "Customer Care" { + :Notify customer; +} +stop +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/activity/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/activity/minimal.puml new file mode 100644 index 0000000..9a921cb --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/activity/minimal.puml @@ -0,0 +1,12 @@ +@startuml Activity_Return_Process +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +start +:Receive return request; +:Inspect item; +:Issue refund; +:Notify customer; +stop +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/activity/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/activity/standard.puml new file mode 100644 index 0000000..05a4d1b --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/activity/standard.puml @@ -0,0 +1,22 @@ +@startuml Activity_Return_Process_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Returns process" + +start +:Receive return request; +partition "Warehouse" { + :Inspect item; + if (Item sellable?) then (yes) + :Restock; + else (no) + :Scrap; + endif +} +partition "Finance" { + :Issue refund; +} +:Notify customer; +stop +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/archimate/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/archimate/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/archimate/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/archimate/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/archimate/detailed.puml new file mode 100644 index 0000000..a2b15e9 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/archimate/detailed.puml @@ -0,0 +1,56 @@ +@startuml Archimate_OrderManagement +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <archimate/Archimate> + +Business_Actor(customer, "Customer") +Business_Actor(support, "Support Agent") +Business_Process(place_order, "Place Order") +Business_Process(handle_return, "Handle Return") +Business_Service(checkout_svc, "Checkout (Business)") + +Application_Service(order_svc, "Order Service") +Application_Service(pay_svc, "Payment Service") +Application_Service(ship_svc, "Shipping Service") +Application_Component(order_app, "Order App") +Application_Component(pay_app, "Payment App") +Application_Component(ship_app, "Shipping App") +Application_DataObject(order_data, "Order") + +Technology_Service(k8s_svc, "Container Hosting") +Technology_Node(k8s, "Kubernetes Cluster") +Technology_Node(rds, "RDS PostgreSQL") + +Rel_Triggering(customer, place_order) +Rel_Triggering(customer, handle_return) +Rel_Assignment(support, handle_return) +Rel_Serving(checkout_svc, place_order) +Rel_Serving(order_svc, place_order) +Rel_Serving(pay_svc, place_order) +Rel_Serving(ship_svc, place_order) +Rel_Realization(order_app, order_svc) +Rel_Realization(pay_app, pay_svc) +Rel_Realization(ship_app, ship_svc) +Rel_Access(order_app, order_data) +Rel_Serving(k8s_svc, order_app) +Rel_Assignment(k8s, order_app) +Rel_Assignment(k8s, pay_app) +Rel_Assignment(k8s, ship_app) +Rel_Assignment(rds, order_data) + +note right of order_svc + Aligned with the Order + bounded context. +end note + +note bottom of k8s + 3 worker nodes per AZ; + HPA on CPU and queue depth. +end note + +note left of customer + External business actor; + may also use the mobile app. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/archimate/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/archimate/minimal.puml new file mode 100644 index 0000000..df4262e --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/archimate/minimal.puml @@ -0,0 +1,11 @@ +@startuml Archimate_OrderManagement +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <archimate/Archimate> + +Business_Actor(customer, "Customer") +Business_Process(place_order, "Place Order") + +Rel_Triggering(customer, place_order) +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/archimate/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/archimate/standard.puml new file mode 100644 index 0000000..e044d16 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/archimate/standard.puml @@ -0,0 +1,18 @@ +@startuml Archimate_OrderManagement_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Order management capability map" +!include <archimate/Archimate> + +Business_Actor(customer, "Customer") +Business_Process(place_order, "Place Order") +Application_Service(order_svc, "Order Service") +Application_Component(order_app, "Order App") +Technology_Node(k8s, "Kubernetes Cluster") + +Rel_Triggering(customer, place_order) +Rel_Serving(order_svc, place_order) +Rel_Realization(order_app, order_svc) +Rel_Assignment(k8s, order_app) +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/c4/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/c4/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/c4/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/c4/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/c4/detailed.puml new file mode 100644 index 0000000..80c90ff --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/c4/detailed.puml @@ -0,0 +1,49 @@ +@startuml C4_Context_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <C4/C4_Context> + +title System Context diagram for the Online Shop + +Person(customer, "Customer", "Buys products") +Person(support, "Customer Support", "Resolves customer issues") +Person(merch, "Merchandiser", "Curates the catalog") +Person(admin, "System Admin", "Operates the platform") + +System(shop, "Online Shop", "Sells things online.") + +System_Ext(email, "Email Service", "Transactional email") +System_Ext(pay, "Payment Gateway", "Authorizes & captures") +System_Ext(ship, "Shipping Carrier", "Delivers parcels") +System_Ext(tax, "Tax Service", "Computes VAT/sales tax") +System_Ext(analytics,"Analytics Platform", "Web & event analytics") +System_Ext(idp, "Identity Provider", "OIDC SSO") + +Rel(customer, shop, "Browses & orders", "HTTPS") +Rel(support, shop, "Resolves issues", "HTTPS") +Rel(merch, shop, "Curates catalog", "HTTPS") +Rel(admin, shop, "Operates", "HTTPS") + +Rel(shop, email, "Sends notifications", "SMTP/HTTPS") +Rel(shop, pay, "Authorizes payments", "HTTPS") +Rel(shop, ship, "Requests shipments", "HTTPS") +Rel(shop, tax, "Computes taxes", "HTTPS") +Rel(shop, analytics, "Streams events", "HTTPS") +Rel(shop, idp, "Authenticates users", "OIDC") + +note right of shop + Bounded by the e-commerce + business capability map. +end note + +note bottom of pay + PCI-DSS scope offloaded to + this provider. +end note + +note left of customer + Anonymous browsing allowed; + checkout requires login. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/c4/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/c4/minimal.puml new file mode 100644 index 0000000..b5039ab --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/c4/minimal.puml @@ -0,0 +1,13 @@ +@startuml C4_Context_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <C4/C4_Context> + +title Online Shop - Context (minimal) + +Person(customer, "Customer") +System(shop, "Online Shop") + +Rel(customer, shop, "Browses & orders") +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/c4/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/c4/standard.puml new file mode 100644 index 0000000..1db9162 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/c4/standard.puml @@ -0,0 +1,22 @@ +@startuml C4_Context_Shop_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!include <C4/C4_Context> + +title System Context diagram for the Online Shop + +Person(customer, "Customer") +Person(support, "Customer Support") +System(shop, "Online Shop", "Sells things online.") + +System_Ext(email, "Email Service") +System_Ext(pay, "Payment Gateway") +System_Ext(ship, "Shipping Carrier") + +Rel(customer, shop, "Browses & orders") +Rel(support, shop, "Resolves issues") +Rel(shop, email, "Sends notifications") +Rel(shop, pay, "Authorizes payments") +Rel(shop, ship, "Requests shipments") +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/class/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/class/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/class/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/class/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/class/detailed.puml new file mode 100644 index 0000000..219fb52 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/class/detailed.puml @@ -0,0 +1,70 @@ +@startuml Class_Order_Domain +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "Order Domain" { + class Order { + +id: OrderId + +status: OrderStatus + +total: Money + +placedAt: DateTime + +submit(): void + +cancel(reason: string): void + } + class OrderLine { + +sku: SKU + +quantity: int + +unitPrice: Money + +subtotal(): Money + } + class Customer { + +id: CustomerId + +name: string + +email: Email + +tier: CustomerTier + } + class Address { + +street: string + +city: string + +zip: string + +country: string + } + enum OrderStatus { + DRAFT + SUBMITTED + PAID + SHIPPED + DELIVERED + CANCELLED + } + interface PricingPolicy { + +price(line: OrderLine): Money + } + class Money <<value object>> { + +amount: decimal + +currency: Currency + } +} + +Customer "1" --> "*" Order : places +Customer "1" --> "*" Address : owns +Order "1" *-- "*" OrderLine : contains +Order --> OrderStatus +Order ..> PricingPolicy : uses +OrderLine --> Money + +note right of Order + Aggregate root for the + order lifecycle. +end note + +note bottom of PricingPolicy + Strategy: tier-based or + promo-driven pricing. +end note + +note left of Money + Immutable value object. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/class/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/class/minimal.puml new file mode 100644 index 0000000..9eeda62 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/class/minimal.puml @@ -0,0 +1,10 @@ +@startuml Class_Order_Domain +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +class Order +class Customer + +Customer --> Order : places +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/class/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/class/standard.puml new file mode 100644 index 0000000..b248064 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/class/standard.puml @@ -0,0 +1,27 @@ +@startuml Class_Order_Domain_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Order domain model" + +package "Order Domain" { + class Order { + +id: OrderId + +status: OrderStatus + +total: Money + +submit(): void + } + class OrderLine { + +sku: SKU + +quantity: int + +subtotal(): Money + } + class Customer { + +id: CustomerId + +name: string + } +} + +Customer "1" --> "*" Order : places +Order "1" *-- "*" OrderLine : contains +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/communication/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/communication/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/communication/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/communication/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/communication/detailed.puml new file mode 100644 index 0000000..9501710 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/communication/detailed.puml @@ -0,0 +1,28 @@ +@startuml Communication_Checkout_Detailed +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Communication — Checkout (detailed)" + +hide empty members + +object ":Cart" as cart +object ":Pricing" as pricing +object ":Tax" as tax +object ":Orders" as orders +object ":Payments" as pay +object ":Inventory" as inv + +cart --> pricing : "1: quote(items)" +pricing --> tax : "1.1: applyTax(subtotal)" +tax ..> pricing : "1.1.1: total" +pricing ..> cart : "1.2: priced" +cart --> inv : "2: reserve(skus)" +inv ..> cart : "2.1: reservationId" +cart --> orders : "3: submit(cart, reservationId)" +orders --> pay : "3.1: authorize(total)" +pay ..> orders : "3.2: authCode" +orders --> inv : "3.3: commit(reservationId)" +orders ..> cart : "4: confirmation" +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/communication/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/communication/minimal.puml new file mode 100644 index 0000000..16f1f18 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/communication/minimal.puml @@ -0,0 +1,15 @@ +@startuml Communication_Checkout_Minimal +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Communication — Checkout (minimal)" + +hide empty members + +object ":Cart" as cart +object ":Orders" as orders + +cart --> orders : "1: submit(cart)" +orders ..> cart : "2: confirmation" +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/communication/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/communication/standard.puml new file mode 100644 index 0000000..c06031a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/communication/standard.puml @@ -0,0 +1,24 @@ +@startuml Communication_Checkout_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Communication — Checkout (standard preset)" + +' Emulated communication diagram: object nodes + numbered labels. +' `hide empty members` strips the empty attribute compartments so +' the result reads as a graph of participants, not as data objects. +hide empty members + +object ":Cart" as cart +object ":Pricing" as pricing +object ":Orders" as orders +object ":Payments" as pay + +cart --> pricing : "1: quote(items)" +pricing ..> cart : "1.1: total" +cart --> orders : "2: submit(cart)" +orders --> pay : "2.1: authorize(total)" +pay ..> orders : "2.2: authCode" +orders ..> cart : "3: confirmation" +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/component/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/component/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/component/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/component/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/component/detailed.puml new file mode 100644 index 0000000..309c1ee --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/component/detailed.puml @@ -0,0 +1,51 @@ +@startuml Component_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "Checkout" { + [Cart] as Cart + [Pricing] as Pricing + [Orders] as Orders + [Promotions] as Promo + [Inventory] as Inv + [Notifier] as Notif + () "Catalog API" as CatalogAPI + () "Payments API" as PayAPI + () "Shipping API" as ShipAPI + () "Email API" as EmailAPI +} + +[Customer Portal] as Portal +database "Orders DB" as DB +queue "Events" as MQ + +Portal -down-> Cart : add/remove +Cart -down-> Pricing : queries +Cart -down-> Promo : applies +Pricing -right-> CatalogAPI : reads +Promo -right-> CatalogAPI : reads +Cart -down-> Orders : submits +Orders -right-> PayAPI : authorizes +Orders -down-> Inv : reserves +Orders -down-> DB : persists +Orders -down-> MQ : publishes +MQ -right-> Notif : consumes +Notif -right-> EmailAPI : sends +Orders -right-> ShipAPI : requests delivery + +note right of Cart + Stateful per-session + bounded context. +end note + +note bottom of Orders + Aggregate root; emits domain + events to MQ on commit. +end note + +note left of Promo + Read-only against the + catalog; eventual consistency. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/component/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/component/minimal.puml new file mode 100644 index 0000000..eeccfa6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/component/minimal.puml @@ -0,0 +1,10 @@ +@startuml Component_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +[Cart] as Cart +[Orders] as Orders + +Cart --> Orders : submits +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/component/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/component/standard.puml new file mode 100644 index 0000000..4a7a870 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/component/standard.puml @@ -0,0 +1,19 @@ +@startuml Component_Checkout_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Checkout component graph" + +package "Checkout" { + [Cart] as Cart + [Pricing] as Pricing + [Orders] as Orders + () "Catalog API" as CatalogAPI + () "Payments API" as PayAPI +} + +Cart -down-> Pricing : queries +Pricing -right-> CatalogAPI : reads +Cart -down-> Orders : submits +Orders -right-> PayAPI : authorizes +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/composite-structure/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/composite-structure/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/detailed.puml new file mode 100644 index 0000000..43ba509 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/detailed.puml @@ -0,0 +1,31 @@ +@startuml CompositeStructure_PaymentGateway_Detailed +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Payment Gateway — detailed composition" + +component "Payment Gateway" { + port "pay-in" as PI + port "pay-out" as PO + port "events-out" as EO + + [Router] as R + [Authorizer] as A + [Anti-Fraud] as F + [Settlement] as S + [Audit Log] as AL + + PI --> R + R --> A + A --> F : checks + A --> S : on success + A --> PO : denied + S --> PO : approved + S --> AL : audit trail + AL --> EO : settlement.posted +} + +note bottom of F : Anti-fraud is colocated with the gateway in this deployment;\nin a federated model it would sit outside the boundary. +note right of S : PCI-DSS scope ends here; audit log is post-settlement. +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/composite-structure/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/minimal.puml new file mode 100644 index 0000000..db968ec --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/minimal.puml @@ -0,0 +1,16 @@ +@startuml CompositeStructure_PaymentGateway_Minimal +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Payment Gateway — minimal composition" + +component "Payment Gateway" { + port "pay-in" as PI + [Authorizer] as A + port "pay-out" as PO + + PI --> A + A --> PO +} +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/composite-structure/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/standard.puml new file mode 100644 index 0000000..20ce38d --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/composite-structure/standard.puml @@ -0,0 +1,23 @@ +@startuml CompositeStructure_PaymentGateway_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Payment Gateway — internal composition (standard)" + +component "Payment Gateway" { + port "pay-in" as PI + port "pay-out" as PO + + [Router] as R + [Authorizer] as A + [Anti-Fraud] as F + [Settlement] as S + + PI --> R + R --> A + A --> F : checks + A --> S : on success + S --> PO +} +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/deployment/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/deployment/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/deployment/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/deployment/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/deployment/detailed.puml new file mode 100644 index 0000000..61d8be5 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/deployment/detailed.puml @@ -0,0 +1,55 @@ +@startuml Deployment_Prod +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +actor User + +cloud "AWS eu-west-1" { + node "Route 53" as DNS + node "CloudFront CDN" as CDN + node "ALB" as ALB + node "ECS Fargate api" as ECS_API { + artifact "api:1.4.2" as API + } + node "ECS Fargate worker" as ECS_W { + artifact "worker:1.4.2" as Worker + } + database "RDS PostgreSQL" as DB + database "ElastiCache Redis" as Cache + queue "SQS orders" as Q + node "S3 assets" as S3 + node "Secrets Manager" as Secrets +} + +cloud "AWS eu-west-1 (DR)" { + database "RDS Replica" as DBR +} + +User --> DNS : DNS lookup +DNS --> CDN : ALIAS +CDN --> ALB : HTTPS origin +CDN --> S3 : static assets +ALB --> API : HTTP/2 +API --> DB : TCP/5432 +API --> Cache : TCP/6379 +API --> Q : HTTPS (publish) +API --> Secrets : HTTPS (read) +Q --> Worker : HTTPS (consume) +Worker --> DB : TCP/5432 +DB ..> DBR : streaming replication + +note right of API + Auto-scales 2-20 tasks + on CPU/queue depth. +end note + +note bottom of DB + Multi-AZ, daily snapshots, + PITR retained 14 days. +end note + +note left of Secrets + Rotated every 30 days. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/deployment/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/deployment/minimal.puml new file mode 100644 index 0000000..dda5e17 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/deployment/minimal.puml @@ -0,0 +1,12 @@ +@startuml Deployment_Prod +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +actor User +node "API" as API +database "DB" as DB + +User --> API +API --> DB +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/deployment/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/deployment/standard.puml new file mode 100644 index 0000000..4af64bd --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/deployment/standard.puml @@ -0,0 +1,21 @@ +@startuml Deployment_Prod_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Production deployment topology" + +cloud "AWS eu-west-1" { + node "ALB" as ALB + node "ECS Fargate" as ECS { + artifact "api:1.4.2" as API + } + database "RDS PostgreSQL" as DB + queue "SQS orders" as Q +} + +actor User +User --> ALB : HTTPS +ALB --> API : HTTP/2 +API --> DB : TCP/5432 +API --> Q : HTTPS (publish) +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/er/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/er/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/er/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/er/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/er/detailed.puml new file mode 100644 index 0000000..4ddbffb --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/er/detailed.puml @@ -0,0 +1,87 @@ +@startuml ER_OrderModel +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +entity "Customer" as c { + *id : uuid <<PK>> + -- + name : text + email : text + tier : text + created_at : timestamptz +} +entity "Address" as a { + *id : uuid <<PK>> + -- + *customer_id : uuid <<FK>> + street : text + city : text + zip : text + country : char(2) +} +entity "Order" as o { + *id : uuid <<PK>> + -- + *customer_id : uuid <<FK>> + *shipping_address_id : uuid <<FK>> + status : text + total : numeric + placed_at : timestamptz +} +entity "OrderLine" as ol { + *id : uuid <<PK>> + -- + *order_id : uuid <<FK>> + *product_id : uuid <<FK>> + qty : int + unit_price : numeric +} +entity "Product" as p { + *id : uuid <<PK>> + -- + sku : text <<UQ>> + name : text + active : boolean +} +entity "Payment" as pay { + *id : uuid <<PK>> + -- + *order_id : uuid <<FK>> + status : text + amount : numeric + authorized_at : timestamptz +} +entity "Shipment" as s { + *id : uuid <<PK>> + -- + *order_id : uuid <<FK>> + carrier : text + tracking : text + status : text +} + +c ||--o{ o +c ||--o{ a +a ||--o{ o : ships to +o ||--o{ ol +p ||--o{ ol +o ||--o| pay +o ||--o{ s + +note right of o + status in + (DRAFT, SUBMITTED, PAID, + SHIPPED, DELIVERED, CANCELLED) +end note + +note bottom of p + Soft-deleted via active=false; + historic order lines preserved. +end note + +note left of pay + At most one successful + payment per order. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/er/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/er/minimal.puml new file mode 100644 index 0000000..8110e7f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/er/minimal.puml @@ -0,0 +1,15 @@ +@startuml ER_OrderModel +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +entity "Customer" as c { + *id : uuid <<PK>> +} +entity "Order" as o { + *id : uuid <<PK>> + *customer_id : uuid <<FK>> +} + +c ||--o{ o +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/er/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/er/standard.puml new file mode 100644 index 0000000..0a0f945 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/er/standard.puml @@ -0,0 +1,30 @@ +@startuml ER_OrderModel_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Order data model" + +entity "Customer" as c { + *id : uuid <<PK>> + -- + name : text + email : text +} +entity "Order" as o { + *id : uuid <<PK>> + -- + *customer_id : uuid <<FK>> + status : text + total : numeric +} +entity "OrderLine" as ol { + *id : uuid <<PK>> + -- + *order_id : uuid <<FK>> + *sku : text + qty : int +} + +c ||--o{ o +o ||--o{ ol +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/gantt/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/gantt/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/gantt/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/gantt/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/gantt/detailed.puml new file mode 100644 index 0000000..2c5eb0a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/gantt/detailed.puml @@ -0,0 +1,39 @@ +@startgantt Gantt_Release_Q3 +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +Project starts 2026-07-01 +saturday are closed +sunday are closed + +today is 30 days after start + +-- Discovery -- +[Stakeholders] lasts 10 days +[Research] lasts 10 days and starts at [Stakeholders]'s end +[Discovery sign-off] happens at [Research]'s end + +-- Design -- +[UX wireframes] lasts 8 days and starts at [Research]'s end +[Tech design] lasts 8 days and starts at [Research]'s end +[Design review] lasts 2 days and starts at [UX wireframes]'s end +[Design sign-off] happens at [Design review]'s end + +-- Build -- +[Backend] lasts 20 days and starts at [Design sign-off]'s end +[Frontend] lasts 15 days and starts 5 days after [Backend]'s start +[Integration] lasts 10 days and starts at [Frontend]'s end +[Code freeze] happens at [Integration]'s end + +-- Release -- +[QA] lasts 7 days and starts at [Code freeze]'s end +[Security review] lasts 3 days and starts at [Code freeze]'s end +[Rollout] lasts 3 days and starts at [QA]'s end +[Launch] happens at [Rollout]'s end +[Post-launch] lasts 5 days and starts at [Launch]'s end + +[QA] is colored in LightCoral/Coral +[Backend] is colored in LightBlue/SteelBlue +[Frontend] is colored in LightGreen/SeaGreen +@endgantt diff --git a/plantuml/skills/plantuml-authoring/test-variants/gantt/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/gantt/minimal.puml new file mode 100644 index 0000000..74574be --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/gantt/minimal.puml @@ -0,0 +1,11 @@ +@startgantt Gantt_Release_Q3 +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +Project starts 2026-07-01 + +[Discovery] lasts 10 days +[Build] lasts 30 days +[Release] lasts 7 days +@endgantt diff --git a/plantuml/skills/plantuml-authoring/test-variants/gantt/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/gantt/standard.puml new file mode 100644 index 0000000..e587248 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/gantt/standard.puml @@ -0,0 +1,23 @@ +@startgantt Gantt_Release_Q3_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Q3 release timeline" + +Project starts 2026-07-01 + +-- Discovery -- +[Stakeholders] lasts 10 days +[Research] lasts 10 days and starts at [Stakeholders]'s end + +-- Build -- +[Backend] lasts 20 days and starts at [Research]'s end +[Frontend] lasts 15 days and starts 5 days after [Backend]'s start +[Integration] lasts 10 days and starts at [Frontend]'s end + +-- Release -- +[QA] lasts 7 days and starts at [Integration]'s end +[Rollout] lasts 3 days and starts at [QA]'s end + +[Launch] happens at [Rollout]'s end +@endgantt diff --git a/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/detailed.puml new file mode 100644 index 0000000..2912551 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/detailed.puml @@ -0,0 +1,35 @@ +@startuml InteractionOverview_Onboarding_Detailed +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Onboarding — Interaction Overview (detailed)" + +actor Customer +participant "Web App" as Web +participant "Identity API" as Identity +participant "Billing API" as Billing +participant "Email Service" as Email + +== Acquisition == +ref over Customer, Web, Identity : Sequence_LandingPage +ref over Customer, Web, Identity : Sequence_SignUp + +alt email verified within 24h + == Activation == + ref over Customer, Web, Identity : Sequence_ProfileSetup + + par concurrent welcome flow + ref over Web, Email : Sequence_WelcomeEmail + end + ref over Customer, Web, Billing : Sequence_FirstPurchase +else verified late + ref over Customer, Identity, Email : Sequence_ResendVerification + ref over Customer, Web, Identity : Sequence_ProfileSetup +else not verified at all + ref over Customer, Email : Sequence_AbandonReminder +end + +== Retention == +ref over Customer, Web, Billing : Sequence_LoyaltyEnrollment +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/minimal.puml new file mode 100644 index 0000000..bcb6e9f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/minimal.puml @@ -0,0 +1,13 @@ +@startuml InteractionOverview_Onboarding_Minimal +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Onboarding — Interaction Overview (minimal)" + +actor Customer +participant "Web App" as Web + +ref over Customer, Web : Sequence_SignUp +ref over Customer, Web : Sequence_FirstPurchase +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/standard.puml new file mode 100644 index 0000000..fd1ecc0 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/interaction-overview/standard.puml @@ -0,0 +1,22 @@ +@startuml InteractionOverview_Onboarding_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Onboarding — Interaction Overview (standard)" + +actor Customer +participant "Web App" as Web +participant "Identity API" as Identity + +== Sign-up == +ref over Customer, Web, Identity : Sequence_SignUp + +alt email verified + == Activation == + ref over Customer, Web, Identity : Sequence_ProfileSetup + ref over Customer, Web : Sequence_FirstPurchase +else email not verified + ref over Customer, Identity : Sequence_ResendVerification +end +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/json-yaml/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/json-yaml/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/detailed.puml new file mode 100644 index 0000000..b981118 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/detailed.puml @@ -0,0 +1,66 @@ +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +@startjson JSON_OrderResponse_Detailed +{ + "id": "o_771", + "status": "PAID", + "placedAt": "2026-04-24T10:00:00Z", + "customer": { + "id": "c_42", + "tier": "GOLD", + "contact": { + "email": "anne@example.com", + "phone": "+39 02 1234567" + }, + "addresses": [ + { + "type": "shipping", + "street": "Via Roma 1", + "city": "Milano", + "zip": "20100", + "country": "IT" + }, + { + "type": "billing", + "street": "Via Verdi 3", + "city": "Milano", + "zip": "20121", + "country": "IT" + } + ] + }, + "lines": [ + { + "sku": "SKU-A", + "qty": 2, + "unitPrice": 39.95, + "discounts": [ + {"code": "SUMMER10", "amount": 4.00} + ] + }, + { + "sku": "SKU-B", + "qty": 1, + "unitPrice": 49.00, + "discounts": [] + } + ], + "payment": { + "id": "pay_001", + "method": "card", + "status": "AUTHORIZED", + "captured": false + }, + "shipment": { + "carrier": "UPS", + "tracking": "1Z999AA10123456784", + "events": [ + {"at": "2026-04-24T11:00Z", "status": "LABEL_CREATED"}, + {"at": "2026-04-24T15:00Z", "status": "PICKED_UP"} + ] + }, + "total": 128.90, + "currency": "EUR" +} +@endjson diff --git a/plantuml/skills/plantuml-authoring/test-variants/json-yaml/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/minimal.puml new file mode 100644 index 0000000..198b978 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/minimal.puml @@ -0,0 +1,10 @@ +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +@startjson JSON_OrderResponse_Minimal +{ + "id": "o_771", + "status": "PAID", + "total": 128.90 +} +@endjson diff --git a/plantuml/skills/plantuml-authoring/test-variants/json-yaml/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/standard.puml new file mode 100644 index 0000000..9fc8d7f --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/json-yaml/standard.puml @@ -0,0 +1,18 @@ +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +@startjson JSON_OrderResponse_Standard +{ + "id": "o_771", + "status": "PAID", + "customer": { + "id": "c_42", + "tier": "GOLD" + }, + "lines": [ + {"sku": "SKU-A", "qty": 2}, + {"sku": "SKU-B", "qty": 1} + ], + "total": 128.90 +} +@endjson diff --git a/plantuml/skills/plantuml-authoring/test-variants/mindmap/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/mindmap/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/mindmap/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/mindmap/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/mindmap/detailed.puml new file mode 100644 index 0000000..60e4cd3 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/mindmap/detailed.puml @@ -0,0 +1,50 @@ +@startmindmap Mindmap_PlatformObjectives +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +caption Strategic objectives 2026 - rolling 12 months + +* Platform Objectives +** Reliability +*** SLO 99.9% +**** Multi-region failover +**** Chaos drills +*** Disaster recovery +**** RPO 5m +**** RTO 30m +** Performance +*** Latency p95 < 200 ms +**** CDN edge tuning +**** Query budgets +*** Throughput +30% +**** Async pipelines +*** Capacity +**** Auto-scaling +** Security +*** OWASP compliance +**** SAST in CI +**** DAST nightly +*** Audit trail +**** Append-only log +*** Identity +**** OIDC SSO +**** MFA enforced +-- Cost +--- Cloud spend -15% +---- FinOps tagging +---- Reserved capacity +--- Vendor consolidation +-- Team +--- Hiring plan +---- 4 SREs +---- 2 Sec engineers +--- Training +---- Internal academy +---- Conferences +-- Roadmap +--- Q1 reliability +--- Q2 performance +--- Q3 security +--- Q4 cost +@endmindmap diff --git a/plantuml/skills/plantuml-authoring/test-variants/mindmap/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/mindmap/minimal.puml new file mode 100644 index 0000000..07e8b5d --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/mindmap/minimal.puml @@ -0,0 +1,10 @@ +@startmindmap Mindmap_PlatformObjectives +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +* Platform Objectives +** Reliability +** Performance +** Security +@endmindmap diff --git a/plantuml/skills/plantuml-authoring/test-variants/mindmap/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/mindmap/standard.puml new file mode 100644 index 0000000..03c4bc6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/mindmap/standard.puml @@ -0,0 +1,22 @@ +@startmindmap Mindmap_PlatformObjectives_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Platform objectives" + +* Platform Objectives +** Reliability +*** SLO 99.9% +*** Disaster recovery +** Performance +*** Latency p95 < 200 ms +*** Throughput +30% +** Security +*** OWASP compliance +*** Audit trail +-- Cost +--- Cloud spend -15% +-- Team +--- Hiring plan +--- Training +@endmindmap diff --git a/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/detailed.puml new file mode 100644 index 0000000..a117f7e --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/detailed.puml @@ -0,0 +1,31 @@ +@startnwdiag Nwdiag_Topology +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +network internet { + address = "0.0.0.0/0" + fw [address = "203.0.113.1"] +} +network dmz { + address = "192.168.10.0/24" + fw [address = "192.168.10.1"] + web01 [address = "192.168.10.11"] + web02 [address = "192.168.10.12"] + proxy [address = "192.168.10.20"] +} +network app_tier { + address = "10.0.2.0/24" + web01 [address = "10.0.2.11"] + web02 [address = "10.0.2.12"] + app01 [address = "10.0.2.21"] + app02 [address = "10.0.2.22"] +} +network data_tier { + address = "10.0.3.0/24" + app01 [address = "10.0.3.21"] + app02 [address = "10.0.3.22"] + db01 [address = "10.0.3.31"] + db02 [address = "10.0.3.32"] + cache [address = "10.0.3.40"] +} +@endnwdiag diff --git a/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/minimal.puml new file mode 100644 index 0000000..958b012 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/minimal.puml @@ -0,0 +1,9 @@ +@startnwdiag Nwdiag_Topology +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +network dmz { + address = "192.168.10.0/24" + web01 [address = "192.168.10.11"] +} +@endnwdiag diff --git a/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/standard.puml new file mode 100644 index 0000000..b85f4d1 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/nwdiag-family/standard.puml @@ -0,0 +1,18 @@ +@startnwdiag Nwdiag_Topology_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Network topology" +network dmz { + address = "192.168.10.0/24" + web01 [address = "192.168.10.11"] + web02 [address = "192.168.10.12"] +} +network internal { + address = "10.0.0.0/16" + web01 [address = "10.0.1.11"] + web02 [address = "10.0.1.12"] + app01 [address = "10.0.2.21"] + db01 [address = "10.0.3.31"] +} +@endnwdiag diff --git a/plantuml/skills/plantuml-authoring/test-variants/object/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/object/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/object/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/object/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/object/detailed.puml new file mode 100644 index 0000000..c12b9c8 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/object/detailed.puml @@ -0,0 +1,65 @@ +@startuml Object_Checkout_State +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +object "anne:Customer" as anne { + id = 42 + tier = GOLD + email = "anne@example.com" +} +object "addr:Address" as addr { + street = "Via Roma 1" + city = "Milano" + zip = "20100" +} +object "o_771:Order" as o771 { + id = 771 + status = SUBMITTED + total = 128.90 + placedAt = "2026-04-24T10:00Z" +} +object "ol_1:OrderLine" as ol1 { + sku = "SKU-A" + qty = 2 + unitPrice = 39.95 +} +object "ol_2:OrderLine" as ol2 { + sku = "SKU-B" + qty = 1 + unitPrice = 49.00 +} +object "p_1:Payment" as p1 { + id = "pay_001" + status = AUTHORIZED + amount = 128.90 +} +object "ship_1:Shipment" as sh1 { + carrier = "UPS" + tracking = "1Z999..." + status = "PENDING" +} + +anne --> addr : ships to +anne --> o771 : places +o771 --> ol1 +o771 --> ol2 +o771 --> p1 : paid by +o771 --> sh1 : delivered via +sh1 --> addr : to + +note right of o771 + Snapshot taken right after + payment authorization. +end note + +note top of anne + GOLD customers get free + shipping on this order. +end note + +note bottom of p1 + 3DS challenge passed; the + capture is pending. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/object/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/object/minimal.puml new file mode 100644 index 0000000..8777d16 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/object/minimal.puml @@ -0,0 +1,10 @@ +@startuml Object_Checkout_State +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +object "anne:Customer" as anne +object "o_771:Order" as o771 + +anne --> o771 +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/object/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/object/standard.puml new file mode 100644 index 0000000..083f220 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/object/standard.puml @@ -0,0 +1,28 @@ +@startuml Object_Checkout_State_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Checkout state snapshot" + +object "anne:Customer" as anne { + id = 42 + tier = GOLD +} +object "o_771:Order" as o771 { + id = 771 + status = SUBMITTED + total = 128.90 +} +object "ol_1:OrderLine" as ol1 { + sku = "SKU-A" + qty = 2 +} +object "ol_2:OrderLine" as ol2 { + sku = "SKU-B" + qty = 1 +} + +anne --> o771 +o771 --> ol1 +o771 --> ol2 +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/package/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/package/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/package/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/package/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/package/detailed.puml new file mode 100644 index 0000000..7a62668 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/package/detailed.puml @@ -0,0 +1,60 @@ +@startuml Package_Layers +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "presentation" <<layer>> { + package "web" as web + package "api" as api + package "graphql" as gql +} +package "application" <<layer>> { + package "commands" as cmd + package "queries" as qry + package "sagas" as sag +} +package "domain" <<layer>> { + package "order" as ord + package "pricing" as prc + package "shipping" as shp + package "shared" as shr +} +package "infrastructure" <<layer>> { + package "persistence" as per + package "messaging" as msg + package "external" as ext +} + +web --> api +gql --> api +api --> cmd +api --> qry +cmd --> ord +qry --> ord +cmd --> sag +sag --> ord +sag --> shp +ord --> prc +ord --> shr +prc --> shr +shp --> shr +cmd --> per +qry --> per +sag --> msg +ext --> msg + +note right of ord + Domain layer must not + depend on infrastructure. +end note + +note bottom of per + Adapters only; concrete + drivers live here. +end note + +note left of web + Multiple inbound channels + share the same app layer. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/package/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/package/minimal.puml new file mode 100644 index 0000000..0b09a5e --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/package/minimal.puml @@ -0,0 +1,12 @@ +@startuml Package_Layers +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +package "presentation" as pres +package "application" as app +package "domain" as dom + +pres --> app +app --> dom +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/package/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/package/standard.puml new file mode 100644 index 0000000..19e3581 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/package/standard.puml @@ -0,0 +1,29 @@ +@startuml Package_Layers_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Layer dependencies" + +package "presentation" <<layer>> { + package "web" as web + package "api" as api +} +package "application" <<layer>> { + package "commands" as cmd + package "queries" as qry +} +package "domain" <<layer>> { + package "order" as ord + package "pricing" as prc +} +package "infrastructure" <<layer>> + +web --> api +api --> cmd +api --> qry +cmd --> ord +qry --> ord +ord --> prc +cmd --> "infrastructure" +qry --> "infrastructure" +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/profile/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/profile/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/profile/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/profile/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/profile/detailed.puml new file mode 100644 index 0000000..0a60d34 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/profile/detailed.puml @@ -0,0 +1,31 @@ +@startuml Profile_BankingExtensions_Detailed +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Banking Profile — detailed (UML 2 Profile Extensions)" + +package "Banking Profile" <<profile>> { + class Account <<stereotype>> + class LedgerEntry <<stereotype>> + class Settlement <<stereotype>> + class IBAN <<stereotype>> + class Currency <<dataType>> +} + +package "UML" { + class Class <<metaclass>> + class Association <<metaclass>> + class Property <<metaclass>> +} + +Account --|> Class : <<extend>> +LedgerEntry --|> Class : <<extend>> +LedgerEntry --|> Association : <<extend>> +Settlement --|> Class : <<extend>> +IBAN --|> Property : <<extend>> + +note right of Account : Tagged values:\n - currency: Currency\n - openedOn: Date +note right of LedgerEntry : Constraint (OCL):\n inv: amount.currency = account.currency +note bottom of IBAN : Validates against ISO 13616.\nProperties enriched with this stereotype\nare automatically format-checked. +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/profile/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/profile/minimal.puml new file mode 100644 index 0000000..847f671 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/profile/minimal.puml @@ -0,0 +1,12 @@ +@startuml Profile_BankingExtensions_Minimal +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Banking Profile — minimal" + +class Account <<stereotype>> +class Class <<metaclass>> + +Account --|> Class : <<extend>> +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/profile/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/profile/standard.puml new file mode 100644 index 0000000..21446f6 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/profile/standard.puml @@ -0,0 +1,20 @@ +@startuml Profile_BankingExtensions_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +title "Banking Profile — UML 2 Profile Extensions (standard)" + +package "Banking Profile" <<profile>> { + class Account <<stereotype>> + class LedgerEntry <<stereotype>> +} +package "UML" { + class Class <<metaclass>> + class Association <<metaclass>> +} + +Account --|> Class : <<extend>> +LedgerEntry --|> Class : <<extend>> +LedgerEntry --|> Association : <<extend>> +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/salt/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/salt/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/salt/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/salt/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/salt/detailed.puml new file mode 100644 index 0000000..429c12d --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/salt/detailed.puml @@ -0,0 +1,41 @@ +@startsalt Salt_LoginDialog +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +{ + Sign in to the Shop + -- + {/ <b>Login | Register | SSO } + -- + Email | "you@example.com " + Password | "*************** " + [ ] Remember me + -- + {SI + Region:^Europe^ + } + -- + [Sign in] | [Forgot password] | [Cancel] + -- + Need help? | <link>Contact support</link> +} +newpage +{ + MFA verification + -- + Enter the 6-digit code sent to your phone + -- + Code | "______" + -- + [Verify] | [Resend code] +} +newpage +{ + Welcome + -- + You are now signed in. + -- + [Continue to dashboard] +} +@endsalt diff --git a/plantuml/skills/plantuml-authoring/test-variants/salt/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/salt/minimal.puml new file mode 100644 index 0000000..8f38a00 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/salt/minimal.puml @@ -0,0 +1,13 @@ +@startsalt Salt_LoginDialog +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +{ + Sign in + -- + Email | " " + Password | " " + [Sign in] +} +@endsalt diff --git a/plantuml/skills/plantuml-authoring/test-variants/salt/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/salt/standard.puml new file mode 100644 index 0000000..7d57280 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/salt/standard.puml @@ -0,0 +1,15 @@ +@startsalt Salt_LoginDialog_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Login dialog wireframe" + +{ + Sign in to the Shop + -- + Email | "you@example.com " + Password | "*************** " + [ ] Remember me + [Sign in] | [Forgot password] +} +@endsalt diff --git a/plantuml/skills/plantuml-authoring/test-variants/sequence/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/sequence/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/sequence/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/sequence/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/sequence/detailed.puml new file mode 100644 index 0000000..14f4260 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/sequence/detailed.puml @@ -0,0 +1,73 @@ +@startuml Sequence_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +actor Customer +participant "Web App" as Web +participant "Orders API" as API +participant "Pricing" as Pricing +participant "Payments" as Pay +database "Orders DB" as DB +queue "Events" as MQ + +Customer -> Web : Place order +activate Web +Web -> API : POST /orders +activate API + +ref over API, Pricing : Quote_Cart +API -> Pricing : quote(items) +activate Pricing +Pricing --> API : total +deactivate Pricing + +API -> DB : INSERT order (PENDING) +API -> MQ : order.created +API --> Web : 201 Created +deactivate API + +opt customer logged in + Web -> API : GET /loyalty + API --> Web : points balance +end + +alt payment authorised + MQ -> Pay : authorize + activate Pay + Pay --> MQ : payment.ok + deactivate Pay + MQ -> API : payment.ok + activate API + API -> DB : UPDATE status=PAID + deactivate API +else payment failed + MQ -> API : payment.ko + activate API + API -> DB : UPDATE status=CANCELLED + deactivate API +else timeout + MQ -> API : payment.timeout + activate API + API -> DB : UPDATE status=PENDING_RETRY + deactivate API +end + +note right of API + Idempotent on order id; + retries are safe. +end note + +note over DB + Status changes are append-only + in the audit log table. +end note + +note left of Customer + Confirmation page also serves + as receipt (printable). +end note + +Web --> Customer : Confirmation page +deactivate Web +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/sequence/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/sequence/minimal.puml new file mode 100644 index 0000000..e14d08a --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/sequence/minimal.puml @@ -0,0 +1,14 @@ +@startuml Sequence_Checkout +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +actor Customer +participant "Web App" as Web +participant "Orders API" as API + +Customer -> Web : Place order +Web -> API : POST /orders +API --> Web : 201 Created +Web --> Customer : Confirmation +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/sequence/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/sequence/standard.puml new file mode 100644 index 0000000..653703e --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/sequence/standard.puml @@ -0,0 +1,31 @@ +@startuml Sequence_Checkout_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Checkout interaction" + +actor Customer +participant "Web App" as Web +participant "Orders API" as API +database "Orders DB" as DB +queue "Events" as MQ + +Customer -> Web : Place order +activate Web +Web -> API : POST /orders +activate API +API -> DB : INSERT order (PENDING) +API -> MQ : order.created +API --> Web : 201 Created +deactivate API + +alt payment authorised + MQ -> API : payment.ok + API -> DB : UPDATE status=PAID +else payment failed + MQ -> API : payment.ko + API -> DB : UPDATE status=CANCELLED +end +Web --> Customer : Confirmation page +deactivate Web +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/state/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/state/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/state/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/state/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/state/detailed.puml new file mode 100644 index 0000000..6f14e16 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/state/detailed.puml @@ -0,0 +1,54 @@ +@startuml State_Order_Lifecycle_Detailed +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +!pragma layout dot + +title "Order lifecycle — detailed (composite + concurrent regions)" + +[*] --> Draft + +state Draft { + Draft : entry / lockCart() + Draft : exit / freezeTotals() +} + +Draft --> Submitted : submit() [cart not empty] + +state Submitted { + Submitted : entry / publishOrderCreated() + Submitted : exit / cancelTimers() +} + +Submitted --> Paid : paymentOk +Submitted --> Cancelled : paymentKo +Submitted --> Cancelled : timeout [age > 30m] + +state Fulfilment { + [*] --> Picking + Picking --> Packing : picked + Packing --> Shipped : packed + + -- + + [*] --> Reserved + Reserved --> Allocated : stockOk + Allocated --> Released : stockKo +} + +Paid --> Fulfilment : ship() +Fulfilment --> Delivered : carrierEvent +Delivered --> [*] +Cancelled --> [*] + +note right of Submitted + Auto-cancel if no payment + in 30 minutes. +end note + +note bottom of Fulfilment + Composite state: physical + fulfilment and stock + allocation run in parallel. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/state/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/state/minimal.puml new file mode 100644 index 0000000..9db0864 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/state/minimal.puml @@ -0,0 +1,10 @@ +@startuml State_Order_Lifecycle +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +[*] --> Draft +Draft --> Submitted : submit() +Submitted --> Paid : paymentOk +Paid --> [*] +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/state/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/state/standard.puml new file mode 100644 index 0000000..e3e9da5 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/state/standard.puml @@ -0,0 +1,15 @@ +@startuml State_Order_Lifecycle_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Order lifecycle" + +[*] --> Draft +Draft --> Submitted : submit() +Submitted --> Paid : paymentOk +Submitted --> Cancelled : paymentKo +Paid --> Shipped : ship() +Shipped --> Delivered : carrierEvent +Delivered --> [*] +Cancelled --> [*] +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/timing/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/timing/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/timing/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/timing/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/timing/detailed.puml new file mode 100644 index 0000000..6a7fe88 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/timing/detailed.puml @@ -0,0 +1,39 @@ +@startuml Timing_BatteryCharging +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +robust "Battery" as B +concise "Charger" as C +concise "Thermistor" as T + +@0 +B is Empty +C is Idle +T is Cool + +@2 +C is Detected +B is Charging +T is Warm + +@5 +B is Charging +T is Hot +C is Throttled + +@10 +B is Charging +T is Warm +C is Detected + +@15 +B is Full +C is Trickle +T is Cool + +@17 +C is Idle + +highlight 2 to 15 #LightYellow : Active charge window +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/timing/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/timing/minimal.puml new file mode 100644 index 0000000..a14e55b --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/timing/minimal.puml @@ -0,0 +1,14 @@ +@startuml Timing_BatteryCharging +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +robust "Battery" as B + +@0 +B is Empty +@+1 +B is Charging +@+10 +B is Full +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/timing/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/timing/standard.puml new file mode 100644 index 0000000..0f7f3d5 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/timing/standard.puml @@ -0,0 +1,24 @@ +@startuml Timing_BatteryCharging_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Battery charging cycle" + +robust "Battery" as B +concise "Charger" as C + +@0 +B is Empty +C is Idle + +@+1 +C is Detected +B is Charging + +@+10 +B is Full +C is Trickle + +@+11 +C is Idle +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/usecase/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/usecase/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/usecase/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/usecase/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/usecase/detailed.puml new file mode 100644 index 0000000..b4a8967 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/usecase/detailed.puml @@ -0,0 +1,59 @@ +@startuml UseCase_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +left to right direction +actor Customer +actor "Guest" as Guest +actor "Customer Support" as CS +actor "Merchandiser" as Merch +actor "Payment Gateway" as Pay <<external>> + +rectangle "Online Shop" { + (Browse catalog) as UC1 + (Search products) as UC1a + (Place order) as UC2 + (Authenticate) as UCAuth + (Pay) as UCPay + (Apply coupon) as UCCoup + (Track shipment) as UC3 + (Return item) as UC4 + (Process refund) as UC5 + (Curate catalog) as UCCur + (Resolve dispute) as UCDisp +} + +Guest --> UC1 +Guest --> UC1a +Customer --> UC1 +Customer --> UC1a +Customer --> UC2 +Customer --> UC3 +Customer --> UC4 +CS --> UC5 +CS --> UCDisp +Merch --> UCCur +UCPay --> Pay + +UC2 ..> UCAuth : <<include>> +UC2 ..> UCPay : <<include>> +UC2 <.. UCCoup : <<extend>> +UC4 ..> UC5 : <<include>> +UCDisp <.. UC4 : <<extend>> + +note right of UC2 + Critical happy path; performance + budget: under 2s p95. +end note + +note bottom of UCAuth + Step-up MFA required for + amounts above 500 EUR. +end note + +note left of UC4 + RMA window: 30 days from + delivery confirmation. +end note +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/usecase/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/usecase/minimal.puml new file mode 100644 index 0000000..8e6a458 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/usecase/minimal.puml @@ -0,0 +1,16 @@ +@startuml UseCase_Shop +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +left to right direction +actor Customer + +(Browse catalog) as UC1 +(Place order) as UC2 +(Track shipment) as UC3 + +Customer --> UC1 +Customer --> UC2 +Customer --> UC3 +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/usecase/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/usecase/standard.puml new file mode 100644 index 0000000..a724ea4 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/usecase/standard.puml @@ -0,0 +1,25 @@ +@startuml UseCase_Shop_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Shop actor goals" + +left to right direction +actor Customer +actor "Customer Support" as CS + +rectangle "Online Shop" { + (Browse catalog) as UC1 + (Place order) as UC2 + (Track shipment) as UC3 + (Return item) as UC4 + (Process refund) as UC5 +} + +Customer --> UC1 +Customer --> UC2 +Customer --> UC3 +Customer --> UC4 +CS --> UC5 +UC4 ..> UC5 : <<include>> +@enduml diff --git a/plantuml/skills/plantuml-authoring/test-variants/wbs/.plantuml b/plantuml/skills/plantuml-authoring/test-variants/wbs/.plantuml new file mode 120000 index 0000000..07531b7 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/wbs/.plantuml @@ -0,0 +1 @@ +../../templates \ No newline at end of file diff --git a/plantuml/skills/plantuml-authoring/test-variants/wbs/detailed.puml b/plantuml/skills/plantuml-authoring/test-variants/wbs/detailed.puml new file mode 100644 index 0000000..e3a9669 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/wbs/detailed.puml @@ -0,0 +1,39 @@ +@startwbs WBS_NewFeature +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +* 1. New Feature\n[80 d] +** 1.1 Discovery\n[10 d] +*** 1.1.1 Stakeholder interviews\n[3 d] +*** 1.1.2 Competitive analysis\n[3 d] +*** 1.1.3 Problem framing\n[2 d] +*** 1.1.4 Success metrics\n[2 d] +** 1.2 Design\n[15 d] +*** 1.2.1 UX wireframes\n[5 d] +**** 1.2.1.1 Low-fi +**** 1.2.1.2 Hi-fi +*** 1.2.2 Technical design\n[5 d] +**** 1.2.2.1 Data model +**** 1.2.2.2 API contract +*** 1.2.3 Design review\n[2 d] +** 1.3 Build\n[40 d] +*** 1.3.1 Backend\n[20 d] +**** 1.3.1.1 API +**** 1.3.1.2 Persistence +**** 1.3.1.3 Migrations +*** 1.3.2 Frontend\n[15 d] +**** 1.3.2.1 Components +**** 1.3.2.2 Pages +*** 1.3.3 Integration\n[10 d] +**** 1.3.3.1 E2E happy path +**** 1.3.3.2 E2E edge cases +** 1.4 Release\n[10 d] +*** 1.4.1 QA\n[5 d] +**** 1.4.1.1 Regression +**** 1.4.1.2 Performance +*** 1.4.2 Rollout\n[3 d] +**** 1.4.2.1 Canary 5% +**** 1.4.2.2 Full rollout +*** 1.4.3 Post-launch monitoring\n[2 d] +@endwbs diff --git a/plantuml/skills/plantuml-authoring/test-variants/wbs/minimal.puml b/plantuml/skills/plantuml-authoring/test-variants/wbs/minimal.puml new file mode 100644 index 0000000..9776308 --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/wbs/minimal.puml @@ -0,0 +1,10 @@ +@startwbs WBS_NewFeature +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml + +* 1. New Feature +** 1.1 Discovery +** 1.2 Build +** 1.3 Release +@endwbs diff --git a/plantuml/skills/plantuml-authoring/test-variants/wbs/standard.puml b/plantuml/skills/plantuml-authoring/test-variants/wbs/standard.puml new file mode 100644 index 0000000..555f34b --- /dev/null +++ b/plantuml/skills/plantuml-authoring/test-variants/wbs/standard.puml @@ -0,0 +1,21 @@ +@startwbs WBS_NewFeature_Standard +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +title "Feature work breakdown" + +* 1. New Feature +** 1.1 Discovery +*** 1.1.1 Stakeholder interviews +*** 1.1.2 Competitive analysis +** 1.2 Design +*** 1.2.1 UX wireframes +*** 1.2.2 Technical design +** 1.3 Build +*** 1.3.1 Backend +*** 1.3.2 Frontend +*** 1.3.3 Integration +** 1.4 Release +*** 1.4.1 QA +*** 1.4.2 Rollout +@endwbs diff --git a/plantuml/skills/plantuml-convert/SKILL.md b/plantuml/skills/plantuml-convert/SKILL.md new file mode 100644 index 0000000..63a87d6 --- /dev/null +++ b/plantuml/skills/plantuml-convert/SKILL.md @@ -0,0 +1,104 @@ +--- +name: plantuml-convert +description: Convert PlantUML (.puml) files to PNG, SVG, or PDF using the CLI. Use when rendering or exporting diagrams, or when document skills (.docx, .pdf, .pptx) need diagram images as input. +compatibility: Requires plantuml and java CLI tools installed (macOS: brew install plantuml) +model: claude-haiku-4-5-20251001 +allowed-tools: Bash +--- + +# PlantUML Converter + +Convert PlantUML `.puml` files to image formats suitable for embedding in documents. + +## Prerequisites + +PlantUML CLI must be installed. On macOS: + +```bash +brew install plantuml +``` + +If `plantuml` is not found, tell the user to install it before proceeding. + +## Default Configuration + +| Setting | Default | Rationale | +|----------------|----------|--------------------------------------------------------| +| Output format | PNG | Best compatibility with Word, PowerPoint, and PDF | +| Scale | 3 | High-resolution output; crisp when printed or zoomed | + +## Conversion Command + +The core command for a single file: + +```bash +plantuml -t<format> -Sscale=<scale> -o <output_dir> <input_file> +``` + +- `<format>`: `png` (default), `svg`, `pdf`, `eps`, `txt` +- `<scale>`: integer, default `3`. Higher values = larger/crisper images. +- `<output_dir>`: **absolute path** to the output directory. PlantUML requires this to be absolute when using `-o`. +- `<input_file>`: path to the `.puml` file. + +## Workflow + +### Single file conversion + +```bash +mkdir -p /absolute/path/to/output +plantuml -tpng -Sscale=3 -o /absolute/path/to/output path/to/diagram.puml +``` + +### Batch conversion (all .puml in a directory) + +```bash +PUML_DIR="path/to/puml/files" +OUTPUT_DIR="$(pwd)/output/diagrams" +mkdir -p "$OUTPUT_DIR" +for puml in "$PUML_DIR"/*.puml; do + [ -f "$puml" ] || continue + echo "Converting: $(basename "$puml")" + plantuml -tpng -Sscale=3 -o "$OUTPUT_DIR" "$puml" +done +``` + +### SVG conversion (for web or scalable contexts) + +When the target is a web page or a context that supports vector graphics, use SVG: + +```bash +plantuml -tsvg -o /absolute/path/to/output input.puml +``` + +Note: `-Sscale` has no effect on SVG since SVG is vector-based. + +## Integration with Document Creation + +When a document skill (/docx, /pdf, /pptx) needs diagram images: + +1. **Identify** which .puml files are referenced or needed for the document. +2. **Convert** them to PNG (scale 3) into a suitable output directory. +3. **Reference** the generated PNG paths when assembling the document. + +## Output Naming + +PlantUML names output files based on the `@startuml Title` directive inside the `.puml` file, not the source filename. For example, a file `my-diagram.puml` containing `@startuml My_Diagram` produces `My_Diagram.png`. If no title is given, the output filename matches the source filename. + +## Overriding Defaults + +The user may override defaults per invocation: + +- **Format**: "convert to SVG" → use `-tsvg` +- **Scale**: "use scale 5" → use `-Sscale=5` +- **Output directory**: "put them in /tmp/diagrams" → use `-o /tmp/diagrams` + +Confirm non-default settings with the user if ambiguous. + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| `plantuml: command not found` | Run `brew install plantuml` | +| Blank or broken output | Check .puml syntax with `plantuml -checkonly file.puml` | +| Output filename unexpected | Check `@startuml Title` in the .puml file | +| Java errors | PlantUML needs Java; run `java -version` to verify | From b2e3a36ab16f12564d981f83eb7b630171a61aa4 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 12:10:26 +0200 Subject: [PATCH 02/11] feat(plantuml): bootstrap skill + /plantuml-init wrapper + policy-schema doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - plantuml/skills/plantuml-bootstrap/SKILL.md: ports ~/.claude/commands/plantuml-init.md to plugin-scoped skill; resolves templates via ${CLAUDE_PLUGIN_ROOT}; dispatches mode=bootstrap (default) and mode=reverse - plantuml/commands/plantuml-init.md: thin command wrapper that delegates to the plantuml-bootstrap skill with --reverse flag mapping - plantuml/docs/policy-schema.md: copy of plantuml-authoring/project-config.md exposed as reference doc under docs/ (original left in place for skill progressive disclosure) - plantuml/tests/smoke/test-bootstrap.sh: TDD red→green static smoke test validating SKILL.md frontmatter, ${CLAUDE_PLUGIN_ROOT} reference, and mode argument --- plantuml/commands/plantuml-init.md | 12 ++ plantuml/docs/policy-schema.md | 195 ++++++++++++++++++++ plantuml/skills/plantuml-bootstrap/SKILL.md | 172 +++++++++++++++++ plantuml/tests/smoke/test-bootstrap.sh | 25 +++ 4 files changed, 404 insertions(+) create mode 100644 plantuml/commands/plantuml-init.md create mode 100644 plantuml/docs/policy-schema.md create mode 100644 plantuml/skills/plantuml-bootstrap/SKILL.md create mode 100755 plantuml/tests/smoke/test-bootstrap.sh diff --git a/plantuml/commands/plantuml-init.md b/plantuml/commands/plantuml-init.md new file mode 100644 index 0000000..ff82ce6 --- /dev/null +++ b/plantuml/commands/plantuml-init.md @@ -0,0 +1,12 @@ +--- +description: Bootstrap a project for PlantUML authoring (delegates to the plantuml-bootstrap skill) +--- + +Invoke the `plantuml-bootstrap` skill from the `plantuml` plugin. + +If the user passed `--reverse`, pass `mode=reverse` as the skill argument. +Otherwise use the default `mode=bootstrap`. + +The skill handles wizard, generation, and validation autonomously. If the +delegation does not work in the current harness, fall back to inlining the +`plantuml-bootstrap` body (see `${CLAUDE_PLUGIN_ROOT}/skills/plantuml-bootstrap/SKILL.md`). diff --git a/plantuml/docs/policy-schema.md b/plantuml/docs/policy-schema.md new file mode 100644 index 0000000..447f046 --- /dev/null +++ b/plantuml/docs/policy-schema.md @@ -0,0 +1,195 @@ +# Project Configuration + +How a project declares its PlantUML identity (styling, target, brand) and +how the skill bootstraps / syncs that configuration. + +## Source of truth: CLAUDE.md + +Every configured project has a "PlantUML Policy" section in its +CLAUDE.md. This section is human-readable, machine-parseable by the +skill, and takes precedence over any file in `.plantuml/`. + +### Section schema + +```markdown +## PlantUML Policy + +- **Primary target**: `docx` # web | docx | pdf | pptx +- **Additional targets**: `web` # optional, comma-separated +- **Theme**: `cerulean-outline` # PlantUML built-in theme name OR `custom` +- **Layout engine**: `smetana` # smetana | elk | dot +- **Default direction**: `top-to-bottom` # top-to-bottom | left-to-right +- **Default detail level**: `standard` # minimal | standard | detailed +- **Label language**: `en` # en | it | fr | … +- **Brand colors**: + - primary: `#0B5FFF` + - accent: `#F59E0B` + - neutral: `#475569` + - surface: `#F8FAFC` + - danger: `#DC2626` +- **Font family**: `Inter, Arial, sans-serif` +- **Base font size**: `14` +- **Max width (docx)**: `5800` +- **Recorded on**: `YYYY-MM-DD` +``` + +### Parsing rules + +- Keys are the text between `**…**:` markers, case-sensitive. +- Values are everything after `:` until `#` (comment) or end-of-line, + trimmed. +- Nested lists (brand colors) are parsed as sub-key → value. +- Missing optional keys fall back to documented defaults; missing + required keys (Primary target, Theme, Brand colors) trigger + bootstrap. + +## Generated artifacts: `.plantuml/` + +From the Policy the skill generates (and keeps in sync) the directory +`.plantuml/` at the project root, containing: + +``` +.plantuml/ +├── _base.puml ← single include entry-point +├── _brand.puml ← !$primary, !$accent, ... variables +├── _fonts.puml ← font family + size +├── _theme.puml ← !theme + global skinparam +├── _layout.puml ← direction + !pragma layout +└── _targets/ + ├── web.puml + ├── docx.puml + ├── pdf.puml + └── pptx.puml +``` + +Treat `.plantuml/` like a lockfile: it is always regenerable from the +Policy. Do not hand-edit files there; edit the Policy and sync. + +## Sync semantics + +- **Policy present, `.plantuml/` absent:** regenerate everything. +- **Policy present, `.plantuml/` present:** diff generated content vs + existing files. If different, overwrite with a diff shown to the + user in dry-run first. +- **Policy absent, `.plantuml/` present:** reverse-init — extract a + Policy draft from existing files and ask the user to review it + before adding to CLAUDE.md. +- **Policy absent, `.plantuml/` absent:** bootstrap dialog (see below). + +## Bootstrap dialog (progressive) + +Trigger: first `.puml` authoring request in a project where both Policy +and `.plantuml/` are missing. + +Script: + +1. Ask the user: *"This project has no PlantUML configuration. Do you + want a shared project setup (recommended) or a one-shot diagram + with inline defaults?"* +2. On **one-shot**: generate the diagram inline with safe defaults + (`theme plain`, target `docx`, font `Inter, 14`, no brand colors). + Prepend comment: `' TODO: run /plantuml-init to share styling`. + Skip to rendering. +3. On **setup**: run the wizard: + - *"Primary render target? (web / docx / pdf / pptx)"* + - *"Theme: built-in PlantUML theme name, or `custom`?"* + - If `custom`: ask for 5 brand color hex values (primary, + accent, neutral, surface, danger). + - *"Font family? (default: Inter, Arial, sans-serif)"* + - *"Label language? (en / it / fr / … — default: en)"* + - *"Default detail level? (minimal / standard / detailed — default + standard)"* +4. Generate the CLAUDE.md Policy section (append if CLAUDE.md exists, + create it otherwise). +5. Generate `.plantuml/` (see next section). +6. Generate the requested diagram using the freshly configured setup. +7. Confirm to user: *"Setup complete. Policy in CLAUDE.md + `.plantuml/` + generated. First diagram at <path>."* + +## Generation details + +### `_brand.puml` + +Emit one `!$variable` per brand color + a `skinparam` mapping to hint +themes that honor variable-based palettes. Example output: + +```plantuml +' Auto-generated from CLAUDE.md "PlantUML Policy". Do not edit by hand. +!$primary = "#0B5FFF" +!$accent = "#F59E0B" +!$neutral = "#475569" +!$surface = "#F8FAFC" +!$danger = "#DC2626" +``` + +### `_fonts.puml` + +```plantuml +skinparam defaultFontName "Inter, Arial, sans-serif" +skinparam defaultFontSize 14 +``` + +### `_theme.puml` + +If Policy theme is a built-in: +```plantuml +!theme cerulean-outline +skinparam backgroundColor $surface +skinparam ArrowColor $neutral +skinparam DefaultTextAlignment center +``` + +If `custom`, skip `!theme` and emit a full skinparam block derived from +brand variables (class background `$surface`, class border `$neutral`, +highlight `$accent`, error `$danger`, etc.). The emitter is documented +in the templates. + +### `_layout.puml` + +```plantuml +!pragma layout smetana +skinparam linetype ortho +skinparam shadowing true +' direction is set per-diagram unless overridden here +``` + +If the Policy `Default direction` is `left-to-right`, append +`left to right direction` on its own line. + +### `_base.puml` + +```plantuml +' Auto-generated from CLAUDE.md "PlantUML Policy". +' Single include entry-point. Do not add target-specific overrides here. +!include _brand.puml +!include _fonts.puml +!include _theme.puml +!include _layout.puml +``` + +### `_targets/<target>.puml` + +See the templates directory for each target's exact content. Example +for `docx`: + +```plantuml +skinparam shadowing false +skinparam defaultFontSize 14 +skinparam dpi 150 +' No hyperlink support in flat PNG rendering; nothing to toggle. +``` + +## Reverse-init (Policy missing, .plantuml/ exists) + +Script parses existing `.plantuml/` to reconstruct a Policy draft: + +- `_brand.puml` → brand colors. +- `_fonts.puml` → font family + base size. +- `_theme.puml` → theme name (grep first `!theme …` line) or `custom`. +- `_layout.puml` → layout engine + direction. +- `_targets/` listing → Primary target (first alphabetically found) + + additional targets (others). +- Max width: inferred from `_targets/docx.puml` `defaultMaxWidth` if + present, else default 5800. + +The user reviews the draft, edits, accepts → Policy added to CLAUDE.md. diff --git a/plantuml/skills/plantuml-bootstrap/SKILL.md b/plantuml/skills/plantuml-bootstrap/SKILL.md new file mode 100644 index 0000000..a782dd0 --- /dev/null +++ b/plantuml/skills/plantuml-bootstrap/SKILL.md @@ -0,0 +1,172 @@ +--- +name: plantuml-bootstrap +description: Set up a project for PlantUML authoring — creates `## PlantUML Policy` in CLAUDE.md and materializes `.plantuml/` from the policy. Use when a project has `.puml` work but no policy, or to migrate an existing `.plantuml/` back to a policy (reverse mode). Accepts `mode=bootstrap` (default) or `mode=reverse`. +allowed-tools: Read, Write, Edit, Glob, Grep, Bash +--- + +# PlantUML Bootstrap + +Set up the **current project** for use with the `plantuml-authoring` skill. +Templates live inside this plugin; resolve them via `${CLAUDE_PLUGIN_ROOT}`. + +## Locate the templates (first Bash block in every flow) + +```bash +SKILL_DIR="${CLAUDE_PLUGIN_ROOT}/skills/plantuml-authoring" +SKILL_TPL="$SKILL_DIR/templates" +[ -d "$SKILL_TPL" ] || { echo "ERROR: plantuml-authoring assets missing in plugin"; exit 1; } +``` + +When the canonical schema for the Policy section is needed, read +`$SKILL_DIR/project-config.md` via the Read tool with that resolved path. + +## Mode dispatch + +Read the `mode` argument. Default `bootstrap`. If `reverse`, jump to the +**Reverse** section at the bottom; otherwise run **State detection** then +**Wizard**, **Append Policy**, **Generation**, **Validate**, **Confirm**. + +## State detection + +Run from the project root (`pwd`). Check four facts: + +1. Does `CLAUDE.md` exist? +2. Does `CLAUDE.md` contain `## PlantUML Policy`? +3. Does `.plantuml/` exist? +4. Does `.plantuml/_base.puml` exist? + +Branches: +- Policy ✓ + `.plantuml/` ✓ → tell the user it is already configured; ask + whether to **regenerate** `.plantuml/` from policy, or exit. Stop. +- Policy ✓ + `.plantuml/` ✗ → run **Generation** only. +- Policy ✗ + `.plantuml/` ✓ → suggest `mode=reverse` and stop. +- Policy ✗ + `.plantuml/` ✗ → run **Wizard**, then **Generation**. + +## Wizard + +Use `AskUserQuestion` if available (preferred), otherwise ask in chat. +Collect: + +1. **Primary target**: `web | docx | pdf | pptx`. Default `docx` for + enterprise contexts; `web` for OSS docs. +2. **Additional targets**: subset of the above. Optional. +3. **Theme**: a built-in PlantUML theme name (e.g. `cerulean-outline`, + `plain`, `vibrant`, `materia`) OR `custom`. If `custom`, ask for 5 + brand colors: + - primary, accent, neutral, surface, danger + - each must be a 6- or 8-digit hex (`#RRGGBB[AA]`) +4. **Font family**: default `Inter, Arial, sans-serif`. +5. **Base font size**: default `14`. +6. **Default detail level**: `minimal | standard | detailed`. Default `standard`. +7. **Label language**: ISO code (`en`, `it`, `fr`, …). Default `en`. +8. **Layout engine**: `smetana | elk | dot`. Default `smetana`. +9. **Default direction**: `top-to-bottom | left-to-right`. Default `top-to-bottom`. +10. **Max width (docx)**: pixels. Default `5800`. + +## Append Policy section to CLAUDE.md + +Format per `$SKILL_DIR/project-config.md` § "Section schema". Header: +`## PlantUML Policy`. Add `**Recorded on**: YYYY-MM-DD`. + +If `CLAUDE.md` does not exist, create it with `# <Project Name>` derived +from the directory, followed by the Policy section. Otherwise append. + +## Generation + +```bash +mkdir -p .plantuml/_targets +cp "$SKILL_TPL/_base.puml" .plantuml/_base.puml +cp "$SKILL_TPL/_layout.puml" .plantuml/_layout.puml +``` + +Per-file substitutions: + +- `.plantuml/_brand.puml`: + ```plantuml + ' Auto-generated from CLAUDE.md "PlantUML Policy". Do not edit by hand. + !$primary = "<value>" + !$accent = "<value>" + !$neutral = "<value>" + !$surface = "<value>" + !$danger = "<value>" + ``` +- `.plantuml/_fonts.puml`: + ```plantuml + skinparam defaultFontName "<font family>" + skinparam defaultFontSize <base size> + ``` +- `.plantuml/_theme.puml`: built-in theme → `!theme <name>` followed by + `skinparam backgroundColor $surface` and `skinparam ArrowColor $neutral`. + Otherwise (custom): `cp "$SKILL_TPL/_theme.puml" .plantuml/_theme.puml`. +- `.plantuml/_layout.puml`: append `left to right direction` at end if + policy direction is `left-to-right`. +- `.plantuml/_targets/<target>.puml` for each target (Primary + Additional): + ```bash + for t in <primary> <additional...>; do + cp "$SKILL_TPL/_targets/$t.puml" ".plantuml/_targets/$t.puml" + done + ``` + +## Validate + +```bash +PROJECT_ROOT="$(pwd)" +TMPDIR="$(mktemp -d -t plantuml-bootstrap-validate.XXXXXX)" +trap 'rm -rf "$TMPDIR"' EXIT + +ln -s "$PROJECT_ROOT/.plantuml" "$TMPDIR/.plantuml" + +cat > "$TMPDIR/_smoke.puml" <<'EOF' +@startuml _Smoke +!$target = %getenv("PLANTUML_TARGET") +!include .plantuml/_base.puml +!include .plantuml/_targets/$target.puml +class X +@enduml +EOF + +( cd "$TMPDIR" && PLANTUML_TARGET=<primary-target> plantuml -checkonly _smoke.puml ) +``` + +Must exit 0. + +## Confirm + +``` +PlantUML Policy added to <project-root>/CLAUDE.md +Generated <project-root>/.plantuml/: + _base.puml, _brand.puml, _fonts.puml, _theme.puml, _layout.puml, + _targets/<each>.puml + +To author: invoke plantuml-authoring or just create *.puml. +To render: invoke plantuml-convert, or run + PLANTUML_TARGET=<target> plantuml -t<format> -Sscale=3 <file>.puml +``` + +## Reverse + +(`mode=reverse`) — `.plantuml/` exists but no Policy in CLAUDE.md. +Reconstruct a draft Policy from existing files (paths relative to project +root): + +1. `.plantuml/_brand.puml` → `!$<name> = "#…"` lines as brand colors. +2. `.plantuml/_fonts.puml` → `defaultFontName` and `defaultFontSize`. +3. `.plantuml/_theme.puml` → `!theme <name>` if present, else `custom`. +4. `.plantuml/_layout.puml` → `!pragma layout`, plus `left to right + direction` presence implies LR. +5. `.plantuml/_targets/*.puml` → first alphabetically = primary, rest = additional. +6. Max width: parse `_targets/docx.puml` for `defaultMaxWidth N`, else `5800`. + +Show the draft, confirm with the user, then append the section to +CLAUDE.md as in the bootstrap flow. Do NOT regenerate `.plantuml/` — +it is already the source of truth in this direction. + +## Don'ts + +- Do NOT modify the plugin (it is read-only at runtime). +- Do NOT hardcode any path containing `/Users/<name>/` or `/home/<name>/`. +- Do NOT translate the Policy section or `.plantuml/` files — they stay + English regardless of `Label language` (which controls diagram-content + labels, not config). +- Do NOT commit secrets — brand colors are public. +- Do NOT regenerate without asking when the project is already configured. diff --git a/plantuml/tests/smoke/test-bootstrap.sh b/plantuml/tests/smoke/test-bootstrap.sh new file mode 100755 index 0000000..6d6b423 --- /dev/null +++ b/plantuml/tests/smoke/test-bootstrap.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Static smoke test for plantuml-bootstrap: validates SKILL.md exists and +# has expected frontmatter + required sections. Behavioral testing happens +# in Phase 7 manual integration. + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-bootstrap/SKILL.md" +CMD="$PLUGIN_ROOT/commands/plantuml-init.md" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" +[[ -f "$CMD" ]] || fail "missing $CMD" + +head -1 "$SKILL" | grep -q '^---$' || fail "SKILL.md missing frontmatter" +grep -qE '^name:\s*plantuml-bootstrap' "$SKILL" || fail "SKILL.md missing name" +grep -qE '^description:' "$SKILL" || fail "SKILL.md missing description" +grep -qE '^allowed-tools:.*Bash' "$SKILL" || fail "SKILL.md missing Bash in allowed-tools" + +grep -q '\${CLAUDE_PLUGIN_ROOT}' "$SKILL" || fail "SKILL.md does not reference \${CLAUDE_PLUGIN_ROOT}" +grep -qiE 'mode\s*=\s*(bootstrap|reverse)' "$SKILL" || fail "SKILL.md does not document mode argument" + +echo "PASS: plantuml-bootstrap static smoke" From 452b0c0c86c230bd0cd65c1a564d4d7f01dd53af Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 14:47:42 +0200 Subject: [PATCH 03/11] feat(plantuml): lint skill, puml-linter agent, minimal-project fixture --- plantuml/agents/puml-linter/AGENT.md | 80 +++++++++++++++++++ plantuml/skills/plantuml-lint/SKILL.md | 53 ++++++++++++ .../minimal-project/.plantuml/_base.puml | 5 ++ .../minimal-project/.plantuml/_brand.puml | 6 ++ .../minimal-project/.plantuml/_fonts.puml | 2 + .../minimal-project/.plantuml/_layout.puml | 1 + .../.plantuml/_targets/web.puml | 1 + .../minimal-project/.plantuml/_theme.puml | 3 + .../tests/fixtures/minimal-project/CLAUDE.md | 14 ++++ .../minimal-project/diagrams/Broken.puml | 3 + .../minimal-project/diagrams/Drift.puml | 4 + .../minimal-project/diagrams/Valid.puml | 6 ++ plantuml/tests/smoke/test-lint.sh | 36 +++++++++ 13 files changed, 214 insertions(+) create mode 100644 plantuml/agents/puml-linter/AGENT.md create mode 100644 plantuml/skills/plantuml-lint/SKILL.md create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_base.puml create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_brand.puml create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_fonts.puml create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_layout.puml create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_targets/web.puml create mode 100644 plantuml/tests/fixtures/minimal-project/.plantuml/_theme.puml create mode 100644 plantuml/tests/fixtures/minimal-project/CLAUDE.md create mode 100644 plantuml/tests/fixtures/minimal-project/diagrams/Broken.puml create mode 100644 plantuml/tests/fixtures/minimal-project/diagrams/Drift.puml create mode 100644 plantuml/tests/fixtures/minimal-project/diagrams/Valid.puml create mode 100755 plantuml/tests/smoke/test-lint.sh diff --git a/plantuml/agents/puml-linter/AGENT.md b/plantuml/agents/puml-linter/AGENT.md new file mode 100644 index 0000000..523fa2f --- /dev/null +++ b/plantuml/agents/puml-linter/AGENT.md @@ -0,0 +1,80 @@ +--- +name: puml-linter +description: "Lint a list of `.puml` files against PlantUML Policy invariants. Returns a JSON array of violations. Dispatched by the plantuml-lint skill in parallel batches." +model: haiku +allowed-tools: Read +--- + +# PlantUML Linter Agent + +You are a Haiku worker for the `plantuml-lint` skill. You receive a JSON +array of file paths in the user's project. Read each file and apply the +fixed rule set below. Emit a JSON array of violations. + +**Never read plugin assets.** Only the user-project paths in your input. + +## Input format + +The orchestrator passes a JSON array as your prompt context, e.g.: + +```json +{ + "project_root": "/abs/path/to/project", + "files": ["diagrams/Foo.puml", "diagrams/Bar.puml"], + "policy_present": true +} +``` + +`project_root` is absolute. `files` are relative to `project_root`. +`policy_present` indicates whether the project has a `## PlantUML Policy` +section in `CLAUDE.md` and a `.plantuml/_base.puml` — when false, rules +that depend on policy (R1, R2, R3) are suppressed. + +## Rules + +For each file, evaluate: + +- **R1 (require base include)** — file must contain + `!include` ending with `_base.puml` (or be a partial via `_*.puml` + filename convention). Suppressed if `policy_present=false`. +- **R2 (no inline skinparam duplication)** — `skinparam` lines that the + base sets are forbidden: `defaultFontName`, `defaultFontSize`, + `backgroundColor`, `ArrowColor`. Suppressed if `policy_present=false`. +- **R3 (no hex color literals)** — pattern `#[0-9A-Fa-f]{6,8}` outside of + a `' …` comment. Allowed only inside `_*.puml` policy partials. + Suppressed if `policy_present=false`. +- **R4 (filename matches title)** — first `@startuml <title>` directive + must match the file's basename without extension. Title is required. +- **R5 (single startuml/enduml)** — exactly one `@startuml … @enduml` + block per file. + +## Output format + +JSON only — no prose, no markdown. Schema: + +```json +[ + { + "file": "diagrams/Drift.puml", + "rule": "R3", + "severity": "error", + "message": "hex color literal `#FF00FF` (line 3) — use brand variable like $primary", + "line": 3 + } +] +``` + +Empty array `[]` if no violations. + +## Severity + +- `error` — R4, R5, or any rule violation in a file that has a Policy. +- `warning` — R1, R2, R3 in a file that explicitly opts out via + `' lint-disable: R<n>` comment within the first 10 lines. + +## Don'ts + +- Do NOT write to disk. +- Do NOT read files outside `files`. +- Do NOT emit anything besides the JSON array. +- Do NOT invent rules beyond R1–R5. diff --git a/plantuml/skills/plantuml-lint/SKILL.md b/plantuml/skills/plantuml-lint/SKILL.md new file mode 100644 index 0000000..deb3edf --- /dev/null +++ b/plantuml/skills/plantuml-lint/SKILL.md @@ -0,0 +1,53 @@ +--- +name: plantuml-lint +description: Check `.puml` files for PlantUML Policy drift, broken includes, and invariant violations (hardcoded colors, missing `_base.puml`, filename≠title, duplicated skinparams). Use when reviewing diagrams for compliance. +allowed-tools: Read, Glob, Grep, Bash, Task +--- + +# PlantUML Lint + +Static lint over `.puml` files in the current project. + +## Usage + +Default: lint every `.puml` and `.plantuml` and `.iuml` under the project +root, excluding `.plantuml/_*.puml` (those are policy partials, not +authored diagrams). + +Custom path: a single file, glob, or directory passed as argument. + +## Flow + +1. **Detect policy presence**: + ```bash + grep -q '^## PlantUML Policy' CLAUDE.md 2>/dev/null && \ + test -f .plantuml/_base.puml && policy_present=true || policy_present=false + ``` +2. **Enumerate files** via `Glob`, excluding `.plantuml/_*.puml`. +3. **Batch** files into chunks of ≤10. +4. **Dispatch** each batch to `puml-linter` (agent) via `Task`. Pass the + batch + `project_root` (absolute, from `pwd`) + `policy_present` as + the prompt. Run batches in parallel. +5. **Aggregate** the JSON arrays into one. Sort by `file` then `line`. +6. **Render** a table for the user: + + ``` + file | rule | sev | message + ----------------- | ---- | ----- | ------- + diagrams/Drift.puml | R3 | error | hex color #FF00FF (line 3) — use $primary + ``` + + Plus a footer: `N error(s), M warning(s) across K file(s) checked`. + +## Exit semantics + +If any error: skill ends with non-zero summary ("lint failed: N errors"). +Otherwise: "lint passed: K files clean". + +## Notes + +- The agents return JSON, never prose. Reject and re-dispatch a batch + whose output does not parse as JSON. +- Do NOT auto-fix. This skill is read-only. +- For ad-hoc single-file lint, you may apply the rules inline (skipping + the agent dispatch) when N=1 — minor optimization, not required. diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_base.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_base.puml new file mode 100644 index 0000000..d4bce14 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_base.puml @@ -0,0 +1,5 @@ +' Auto-generated from PlantUML Policy. Do not edit by hand. +!include _brand.puml +!include _fonts.puml +!include _theme.puml +!include _layout.puml diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_brand.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_brand.puml new file mode 100644 index 0000000..cc326d8 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_brand.puml @@ -0,0 +1,6 @@ +' Auto-generated from PlantUML Policy. Do not edit by hand. +!$primary = "#1A73E8" +!$accent = "#FBBC04" +!$neutral = "#5F6368" +!$surface = "#FFFFFF" +!$danger = "#D93025" diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_fonts.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_fonts.puml new file mode 100644 index 0000000..5533c34 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_fonts.puml @@ -0,0 +1,2 @@ +skinparam defaultFontName "Inter, Arial, sans-serif" +skinparam defaultFontSize 14 diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_layout.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_layout.puml new file mode 100644 index 0000000..c7fb41e --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_layout.puml @@ -0,0 +1 @@ +!pragma layout smetana diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_targets/web.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_targets/web.puml new file mode 100644 index 0000000..e1f7578 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_targets/web.puml @@ -0,0 +1 @@ +' Web target — no extra constraints. diff --git a/plantuml/tests/fixtures/minimal-project/.plantuml/_theme.puml b/plantuml/tests/fixtures/minimal-project/.plantuml/_theme.puml new file mode 100644 index 0000000..a73c777 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/.plantuml/_theme.puml @@ -0,0 +1,3 @@ +!theme plain +skinparam backgroundColor $surface +skinparam ArrowColor $neutral diff --git a/plantuml/tests/fixtures/minimal-project/CLAUDE.md b/plantuml/tests/fixtures/minimal-project/CLAUDE.md new file mode 100644 index 0000000..6c02e1b --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/CLAUDE.md @@ -0,0 +1,14 @@ +# minimal-project + +Fixture for plantuml plugin smoke tests. + +## PlantUML Policy +- **Primary target**: web +- **Theme**: plain +- **Font family**: Inter, Arial, sans-serif +- **Base font size**: 14 +- **Default detail level**: standard +- **Label language**: en +- **Layout engine**: smetana +- **Default direction**: top-to-bottom +- **Recorded on**: 2026-04-25 diff --git a/plantuml/tests/fixtures/minimal-project/diagrams/Broken.puml b/plantuml/tests/fixtures/minimal-project/diagrams/Broken.puml new file mode 100644 index 0000000..3d79c68 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/diagrams/Broken.puml @@ -0,0 +1,3 @@ +@startuml Broken +class +@enduml diff --git a/plantuml/tests/fixtures/minimal-project/diagrams/Drift.puml b/plantuml/tests/fixtures/minimal-project/diagrams/Drift.puml new file mode 100644 index 0000000..fc53df0 --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/diagrams/Drift.puml @@ -0,0 +1,4 @@ +@startuml WrongTitle +skinparam defaultFontSize 12 +class Quux #FF00FF +@enduml diff --git a/plantuml/tests/fixtures/minimal-project/diagrams/Valid.puml b/plantuml/tests/fixtures/minimal-project/diagrams/Valid.puml new file mode 100644 index 0000000..f81adeb --- /dev/null +++ b/plantuml/tests/fixtures/minimal-project/diagrams/Valid.puml @@ -0,0 +1,6 @@ +@startuml Valid +!include ../.plantuml/_base.puml +class Foo +class Bar +Foo --> Bar +@enduml diff --git a/plantuml/tests/smoke/test-lint.sh b/plantuml/tests/smoke/test-lint.sh new file mode 100755 index 0000000..3a2aa03 --- /dev/null +++ b/plantuml/tests/smoke/test-lint.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Static smoke for plantuml-lint: validates skill + agent files exist +# with expected structure. Behavioral testing of the lint output happens +# in Phase 7 manual integration. + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-lint/SKILL.md" +AGENT="$PLUGIN_ROOT/agents/puml-linter/AGENT.md" +FIXTURE="$PLUGIN_ROOT/tests/fixtures/minimal-project" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" +[[ -f "$AGENT" ]] || fail "missing $AGENT" +[[ -d "$FIXTURE/.plantuml" ]] || fail "fixture .plantuml/ missing" +[[ -f "$FIXTURE/diagrams/Valid.puml" ]] || fail "fixture Valid.puml missing" +[[ -f "$FIXTURE/diagrams/Drift.puml" ]] || fail "fixture Drift.puml missing" + +# Skill frontmatter +grep -qE '^name:\s*plantuml-lint' "$SKILL" || fail "skill name wrong" +grep -qE '^allowed-tools:.*Task' "$SKILL" || fail "skill missing Task" + +# Agent frontmatter +grep -qE '^name:\s*puml-linter' "$AGENT" || fail "agent name wrong" +grep -qE '^model:\s*haiku' "$AGENT" || fail "agent missing haiku model" + +# Spot-check that the fixture's Drift file would actually produce +# violations: it contains a hex literal AND a hardcoded skinparam. +grep -qE '#[0-9A-Fa-f]{6}' "$FIXTURE/diagrams/Drift.puml" || \ + fail "fixture Drift.puml does not contain a hex literal" +grep -qE 'skinparam defaultFontSize' "$FIXTURE/diagrams/Drift.puml" || \ + fail "fixture Drift.puml does not contain duplicated skinparam" + +echo "PASS: plantuml-lint static smoke" From 962d61eb61dc2e8c0b58b37a0ac3667b1a24dc8a Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 14:53:14 +0200 Subject: [PATCH 04/11] fix(plantuml): loosen lint smoke regex and clarify policy detection --- plantuml/skills/plantuml-lint/SKILL.md | 7 +++++-- plantuml/tests/smoke/test-lint.sh | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plantuml/skills/plantuml-lint/SKILL.md b/plantuml/skills/plantuml-lint/SKILL.md index deb3edf..75e46b9 100644 --- a/plantuml/skills/plantuml-lint/SKILL.md +++ b/plantuml/skills/plantuml-lint/SKILL.md @@ -20,8 +20,11 @@ Custom path: a single file, glob, or directory passed as argument. 1. **Detect policy presence**: ```bash - grep -q '^## PlantUML Policy' CLAUDE.md 2>/dev/null && \ - test -f .plantuml/_base.puml && policy_present=true || policy_present=false + if grep -q '^## PlantUML Policy' CLAUDE.md 2>/dev/null && test -f .plantuml/_base.puml; then + policy_present=true + else + policy_present=false + fi ``` 2. **Enumerate files** via `Glob`, excluding `.plantuml/_*.puml`. 3. **Batch** files into chunks of ≤10. diff --git a/plantuml/tests/smoke/test-lint.sh b/plantuml/tests/smoke/test-lint.sh index 3a2aa03..63c4019 100755 --- a/plantuml/tests/smoke/test-lint.sh +++ b/plantuml/tests/smoke/test-lint.sh @@ -30,7 +30,7 @@ grep -qE '^model:\s*haiku' "$AGENT" || fail "agent missing haiku model" # violations: it contains a hex literal AND a hardcoded skinparam. grep -qE '#[0-9A-Fa-f]{6}' "$FIXTURE/diagrams/Drift.puml" || \ fail "fixture Drift.puml does not contain a hex literal" -grep -qE 'skinparam defaultFontSize' "$FIXTURE/diagrams/Drift.puml" || \ - fail "fixture Drift.puml does not contain duplicated skinparam" +grep -qE 'skinparam (defaultFontName|defaultFontSize|backgroundColor|ArrowColor)' "$FIXTURE/diagrams/Drift.puml" || \ + fail "fixture Drift.puml does not contain a base-set skinparam" echo "PASS: plantuml-lint static smoke" From f8e99f047fbf7dfb4c49a31d34d8e599ff15d0a0 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 14:56:18 +0200 Subject: [PATCH 05/11] feat(plantuml): validate skill, renderer + visual-checker agents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Phase 4 components for render-aware validation: - plantuml-validate skill (orchestrator): mode=check|bless, level=checkonly|svg-hash|png-perceptual, parallel Task dispatch to puml-renderer in batches of ≤8, optional puml-visual-checker on bless - puml-renderer agent (Haiku): handles checkonly and svg-hash levels, returns JSON status per (file, target) cell - puml-visual-checker agent (Sonnet): vision-capable smoke check for color, font, layout on rendered PNG baselines - test-validate.sh: static smoke test (TDD red→green) --- plantuml/agents/puml-renderer/AGENT.md | 73 ++++++++++++++++++++ plantuml/agents/puml-visual-checker/AGENT.md | 59 ++++++++++++++++ plantuml/skills/plantuml-validate/SKILL.md | 62 +++++++++++++++++ plantuml/tests/smoke/test-validate.sh | 25 +++++++ 4 files changed, 219 insertions(+) create mode 100644 plantuml/agents/puml-renderer/AGENT.md create mode 100644 plantuml/agents/puml-visual-checker/AGENT.md create mode 100644 plantuml/skills/plantuml-validate/SKILL.md create mode 100755 plantuml/tests/smoke/test-validate.sh diff --git a/plantuml/agents/puml-renderer/AGENT.md b/plantuml/agents/puml-renderer/AGENT.md new file mode 100644 index 0000000..a85b68d --- /dev/null +++ b/plantuml/agents/puml-renderer/AGENT.md @@ -0,0 +1,73 @@ +--- +name: puml-renderer +description: "Render or validate a single (file, target) pair and compare against a baseline. Returns a JSON status. Dispatched by plantuml-validate in parallel." +model: haiku +allowed-tools: Bash +--- + +# PlantUML Renderer Agent + +Haiku worker for `plantuml-validate`. You receive a JSON object describing +one rendering job, run the corresponding `plantuml` command, and emit a +JSON status. + +**Never read plugin assets.** Only the user-project paths in your input. + +## Input + +```json +{ + "project_root": "/abs/path", + "file": "diagrams/Foo.puml", + "target": "web", + "level": "checkonly", + "mode": "check", + "baseline": "tests/plantuml-baselines/Foo--web.checkonly" +} +``` + +`level` is one of `checkonly | svg-hash | png-perceptual`. +`mode` is `check` or `bless`. + +## Behavior by level + +- **checkonly**: run + ```bash + ( cd "$project_root" && PLANTUML_TARGET="$target" plantuml -checkonly "$file" ) + ``` + Result is a single string: `"ok"` (exit 0) or the stderr (exit ≠ 0). + - `mode=bless`: write `"ok"` (or stderr) to `baseline`. Status: `blessed`. + - `mode=check`: read `baseline`. If equal to current → `pass`. Else `fail`. + +- **svg-hash**: run + ```bash + ( cd "$project_root" && PLANTUML_TARGET="$target" plantuml -tsvg -pipe < "$file" \ + | sed -E 's/<\!--.*-->//g' \ + | shasum -a 256 \ + | awk '{print $1}' ) + ``` + - `mode=bless`: write the hash to `baseline`. Status: `blessed`. + - `mode=check`: compare to baseline. Equal → `pass`. Different → `fail`. + +- **png-perceptual**: out of scope for this agent in v1.0.0 — return + `{status: "unsupported"}`. + +## Output + +```json +{ + "file": "diagrams/Foo.puml", + "target": "web", + "level": "checkonly", + "status": "pass", + "diff_summary": "" +} +``` + +`status` ∈ `pass | fail | blessed | missing-baseline | unsupported | error`. +`diff_summary` is empty on pass/blessed; a one-line description on fail/error. + +## Don'ts + +- Do NOT read plugin assets. Only `project_root` paths. +- Do NOT emit anything besides the JSON object. diff --git a/plantuml/agents/puml-visual-checker/AGENT.md b/plantuml/agents/puml-visual-checker/AGENT.md new file mode 100644 index 0000000..a7f0f98 --- /dev/null +++ b/plantuml/agents/puml-visual-checker/AGENT.md @@ -0,0 +1,59 @@ +--- +name: puml-visual-checker +description: "Build-time smoke check on a rendered diagram image. Verifies (1) Policy primary color is visibly present, (2) declared font is applied, (3) layout has no obvious overflow or label collision. Returns a per-check JSON verdict. Not user-facing in v1.0.0." +model: sonnet +allowed-tools: Read +--- + +# PlantUML Visual Checker Agent + +Sonnet vision-capable worker for build-time smoke tests. You receive an +image path and a small slice of the project's PlantUML Policy. Read the +image and emit three pass/fail verdicts. + +**Never read plugin assets.** Only the user-project paths in your input. + +## Input + +```json +{ + "image_path": "/abs/path/to/baseline.png", + "policy": { + "primary_color": "#1A73E8", + "font_family": "Inter, Arial, sans-serif" + } +} +``` + +## Checks + +1. **color**: is a color clearly matching `primary_color` (within ~10% + perceptual tolerance) visible somewhere in the image — typically on + class headers, arrow accents, or borders? +2. **font**: does the rendered text suggest the declared font family + (sans-serif, geometric proportions consistent with Inter / Arial)? + If the image is too small to tell, return `inconclusive` rather than + `fail`. +3. **layout**: any obvious overflow (text spilling outside boxes), label + clipping, severe collision between elements, or unreadable rendering? + +## Output + +```json +{ + "image": "baseline.png", + "checks": { + "color": {"verdict": "pass", "note": ""}, + "font": {"verdict": "pass", "note": ""}, + "layout": {"verdict": "pass", "note": ""} + } +} +``` + +`verdict` ∈ `pass | fail | inconclusive`. + +## Constraints + +- This is a SHALLOW smoke check, not a design review. +- Return `inconclusive` rather than `fail` when uncertain. +- Output JSON only. diff --git a/plantuml/skills/plantuml-validate/SKILL.md b/plantuml/skills/plantuml-validate/SKILL.md new file mode 100644 index 0000000..1afbd5b --- /dev/null +++ b/plantuml/skills/plantuml-validate/SKILL.md @@ -0,0 +1,62 @@ +--- +name: plantuml-validate +description: Render or check `.puml` files for all declared targets and verify they produce stable output against committed baselines. Use to catch syntax breakage and rendering regressions. Accepts `mode=check|bless` (default `check`) and `level=checkonly|svg-hash|png-perceptual` (default `checkonly`). +allowed-tools: Read, Glob, Bash, Task +--- + +# PlantUML Validate + +Render-aware validation across `.puml` × declared targets, with three +levels of stringency. + +## Args + +- `mode=check` (default) — compare current render against baseline. +- `mode=bless` — capture current render as new baseline. +- `level=checkonly` (default) — `plantuml -checkonly`, deterministic + cross-machine, no image output. +- `level=svg-hash` — render SVG, normalize, hash. Stable cross-machine if + fonts are pinned. +- `level=png-perceptual` — opt-in, fragile across systems. Not implemented + in v1.0.0; the renderer agent returns `unsupported`. + +## Flow + +1. **Read Policy** from `CLAUDE.md` § "PlantUML Policy". Extract Primary + + Additional targets. Abort with a clear message if Policy missing. +2. **Enumerate** `.puml` files (excluding `.plantuml/_*.puml`). +3. **Compute matrix** `file × target`. +4. **Compute baseline path** for each cell: + `tests/plantuml-baselines/<file-stem>--<target>.<level>` +5. **For `mode=check`**: if any baseline is missing, abort and tell the + user `"no baselines found, run with mode=bless to capture current state"`. + (If only some are missing, list them; do not silently skip.) +6. **Dispatch** each cell to `puml-renderer` via `Task` in parallel batches + of ≤8. +7. **Aggregate** statuses. Render a table. +8. **For `mode=bless`** at level `svg-hash` or higher (i.e. when actual + images exist): also dispatch `puml-visual-checker` on the freshly + rendered images (1 per (file, target)) and require all checks `pass` + or `inconclusive`. A `fail` from visual-checker downgrades the bless + to `warning` and asks the user to confirm. + +## Output + +``` +Validate (mode=check, level=checkonly) +file | target | status | note +----------------- | ------ | ------------- | ---- +diagrams/Foo.puml | web | pass | +diagrams/Bar.puml | docx | fail | exited 1: syntax error line 5 + +3 pass, 1 fail across 4 cells +``` + +## Notes + +- Baselines live in `tests/plantuml-baselines/` of the user's project, + intended to be gitcommitted. +- The skill never silently overwrites baselines — only `mode=bless` + writes. +- For projects without a Policy, this skill exits with `"validate + requires a PlantUML Policy — run /plantuml-init first"`. diff --git a/plantuml/tests/smoke/test-validate.sh b/plantuml/tests/smoke/test-validate.sh new file mode 100755 index 0000000..521e6a2 --- /dev/null +++ b/plantuml/tests/smoke/test-validate.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-validate/SKILL.md" +RENDERER="$PLUGIN_ROOT/agents/puml-renderer/AGENT.md" +VISUAL="$PLUGIN_ROOT/agents/puml-visual-checker/AGENT.md" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" +[[ -f "$RENDERER" ]] || fail "missing $RENDERER" +[[ -f "$VISUAL" ]] || fail "missing $VISUAL" + +grep -qE '^name:\s*plantuml-validate' "$SKILL" || fail "skill name wrong" +grep -qiE 'mode\s*=\s*(bless|check)' "$SKILL" || fail "skill missing mode arg doc" +grep -qiE 'level\s*=\s*(checkonly|svg-hash|png-perceptual)' "$SKILL" || fail "skill missing level arg doc" + +grep -qE '^name:\s*puml-renderer' "$RENDERER" || fail "renderer name wrong" +grep -qE '^model:\s*haiku' "$RENDERER" || fail "renderer not haiku" + +grep -qE '^name:\s*puml-visual-checker' "$VISUAL" || fail "visual-checker name wrong" +grep -qE '^model:\s*sonnet' "$VISUAL" || fail "visual-checker not sonnet" + +echo "PASS: plantuml-validate static smoke" From cc2b660121a27fe39fed71cbe8bc51dc165fdb79 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:01:24 +0200 Subject: [PATCH 06/11] fix(plantuml): clarify validate visual-smoke flow, baseline path collision, renderer fail diff --- plantuml/agents/puml-renderer/AGENT.md | 11 +++++++++-- plantuml/skills/plantuml-validate/SKILL.md | 20 ++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/plantuml/agents/puml-renderer/AGENT.md b/plantuml/agents/puml-renderer/AGENT.md index a85b68d..43d68f3 100644 --- a/plantuml/agents/puml-renderer/AGENT.md +++ b/plantuml/agents/puml-renderer/AGENT.md @@ -37,15 +37,22 @@ JSON status. ``` Result is a single string: `"ok"` (exit 0) or the stderr (exit ≠ 0). - `mode=bless`: write `"ok"` (or stderr) to `baseline`. Status: `blessed`. - - `mode=check`: read `baseline`. If equal to current → `pass`. Else `fail`. + - `mode=check`: read `baseline`. If equal to current → `pass`. Else + `fail` — populate `diff_summary` with the first 120 chars of the + current run's output (the stderr that diverges from baseline). - **svg-hash**: run ```bash ( cd "$project_root" && PLANTUML_TARGET="$target" plantuml -tsvg -pipe < "$file" \ - | sed -E 's/<\!--.*-->//g' \ + | tr '\n' ' ' \ + | sed -E 's/<!--[^>]*-->//g' \ | shasum -a 256 \ | awk '{print $1}' ) ``` + Note: the `tr` flattens multi-line SVG to a single line so the comment + strip handles cross-line PlantUML banners; the `[^>]*` non-greedy form + avoids over-matching when multiple comments appear on the same line. + `awk` is intentionally POSIX (no GNU-only constructs). - `mode=bless`: write the hash to `baseline`. Status: `blessed`. - `mode=check`: compare to baseline. Equal → `pass`. Different → `fail`. diff --git a/plantuml/skills/plantuml-validate/SKILL.md b/plantuml/skills/plantuml-validate/SKILL.md index 1afbd5b..f33f29e 100644 --- a/plantuml/skills/plantuml-validate/SKILL.md +++ b/plantuml/skills/plantuml-validate/SKILL.md @@ -27,18 +27,26 @@ levels of stringency. 2. **Enumerate** `.puml` files (excluding `.plantuml/_*.puml`). 3. **Compute matrix** `file × target`. 4. **Compute baseline path** for each cell: - `tests/plantuml-baselines/<file-stem>--<target>.<level>` + `tests/plantuml-baselines/<flat-relpath>--<target>.<level>` + where `<flat-relpath>` is the file path relative to the project root with + `/` replaced by `__` and the `.puml` extension dropped (e.g. + `diagrams/auth/Foo.puml` → `diagrams__auth__Foo`). This avoids silent + collisions when two diagrams share a stem in different directories. 5. **For `mode=check`**: if any baseline is missing, abort and tell the user `"no baselines found, run with mode=bless to capture current state"`. (If only some are missing, list them; do not silently skip.) 6. **Dispatch** each cell to `puml-renderer` via `Task` in parallel batches of ≤8. 7. **Aggregate** statuses. Render a table. -8. **For `mode=bless`** at level `svg-hash` or higher (i.e. when actual - images exist): also dispatch `puml-visual-checker` on the freshly - rendered images (1 per (file, target)) and require all checks `pass` - or `inconclusive`. A `fail` from visual-checker downgrades the bless - to `warning` and asks the user to confirm. +8. **Visual smoke (build-time only)** — applies to `mode=bless` and + only at `level=svg-hash` (or `png-perceptual` once implemented). The + svg-hash baseline itself is not viewable, so the skill must perform + a one-shot auxiliary PNG render per `(file, target)` (e.g. + `plantuml -tpng -Sscale=3 -o <tmp> <file>`) and dispatch + `puml-visual-checker` on each PNG. The PNGs are transient — they + are NOT stored as baselines. All checks must be `pass` or + `inconclusive`; a `fail` downgrades the bless to a warning and + prompts the user to confirm. ## Output From f160e603a4e4418b03cfa7d6b063769a7282cb0f Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:03:50 +0200 Subject: [PATCH 07/11] feat(plantuml): review and advisor skills (Sonnet, interactive) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add plantuml-review and plantuml-advisor skills with explicit model:sonnet, no Task dispatch, structured output sections, and static smoke tests (TDD red→green for both). --- plantuml/skills/plantuml-advisor/SKILL.md | 53 ++++++++++++++++++++ plantuml/skills/plantuml-review/SKILL.md | 60 +++++++++++++++++++++++ plantuml/tests/smoke/test-advisor.sh | 18 +++++++ plantuml/tests/smoke/test-review.sh | 22 +++++++++ 4 files changed, 153 insertions(+) create mode 100644 plantuml/skills/plantuml-advisor/SKILL.md create mode 100644 plantuml/skills/plantuml-review/SKILL.md create mode 100755 plantuml/tests/smoke/test-advisor.sh create mode 100755 plantuml/tests/smoke/test-review.sh diff --git a/plantuml/skills/plantuml-advisor/SKILL.md b/plantuml/skills/plantuml-advisor/SKILL.md new file mode 100644 index 0000000..5e736cd --- /dev/null +++ b/plantuml/skills/plantuml-advisor/SKILL.md @@ -0,0 +1,53 @@ +--- +name: plantuml-advisor +description: Advise on diagram-type fit for a `.puml` — confirm the current type is right, or suggest a better one with rationale and a migration sketch. Use when an author is unsure whether class, component, sequence, or another type fits the intent best. +model: sonnet +allowed-tools: Read +--- + +# PlantUML Advisor + +Type-fit advice for a single `.puml`. Interactive, no agent. + +## Flow + +1. Read the file. Identify the current diagram type from the leading + directives (`@startuml`, `class`/`component`/`participant`/etc.). +2. Read the user's intent: any heading comment (`' purpose:`), file path + context, and the elements present (entities, relationships). +3. Cross-reference with `principles.md` of the inherited + `plantuml-authoring` skill: + `${CLAUDE_PLUGIN_ROOT}/skills/plantuml-authoring/principles.md` + and the `diagrams/INDEX.md` decision table for type selection. +4. Produce structured advice in four sections (below). + +## Output sections + +### 1. Current type + +Name the type the file uses today, with one-sentence justification of how +you classified it. + +### 2. Intent reading + +What you understand the diagram is trying to communicate, in 1–2 +sentences. Explicitly note where the intent is ambiguous and what +clarification would help. + +### 3. Recommended type + +`Same` (no change needed) or `<better-type>`. Cite the relevant rule from +`principles.md` (e.g., "principles.md §'Behavior over structure' suggests +sequence over class for runtime interactions"). One-paragraph rationale. + +### 4. Migration sketch + +If recommended type ≠ current type: a minimal `@startuml` skeleton in the +new type, listing which existing elements survive (and how they map) and +which ones disappear. Skip this section if no change is recommended. + +## Tone + +Authoritative but humble — when intent is genuinely unclear, ask for +confirmation rather than guess. End with a one-line verdict: +`Type is fit` / `Consider switching to <type>` / `Need user clarification`. diff --git a/plantuml/skills/plantuml-review/SKILL.md b/plantuml/skills/plantuml-review/SKILL.md new file mode 100644 index 0000000..576c9ac --- /dev/null +++ b/plantuml/skills/plantuml-review/SKILL.md @@ -0,0 +1,60 @@ +--- +name: plantuml-review +description: Review a single PlantUML diagram for clarity, type-fit, layout, and readability. Use when an author wants qualitative feedback before merging or sharing. +model: sonnet +allowed-tools: Read, Bash +--- + +# PlantUML Review + +Qualitative review of a single `.puml` file. Interactive, no agent. + +## Input + +A file path. If multiple are passed, review them sequentially (one per +turn). + +## Flow + +1. Run `plantuml -checkonly <file>` first. If it errors, surface the + syntax error and stop — there is nothing meaningful to review. +2. Read the file. +3. If a `## PlantUML Policy` section exists in the project's CLAUDE.md, + read it (for label-language, detail-level expectations). +4. Read the relevant `diagrams/<type>.md` from the inherited + `plantuml-authoring` skill at + `${CLAUDE_PLUGIN_ROOT}/skills/plantuml-authoring/diagrams/<type>.md` + for the type-specific principles. +5. Produce structured feedback in four sections (below). + +## Output sections + +### 1. Type fit + +Is the chosen diagram type (class, component, sequence, …) right for what +the diagram is trying to communicate? If not, name the better type and +why. (For deeper "should I switch?" guidance, suggest invoking +`plantuml-advisor`.) + +### 2. Detail level + +Compare the actual element count and label density against the Policy's +default detail level (minimal | standard | detailed). Flag if too sparse +or too busy for the audience. + +### 3. Layout + +Crossings, awkward arrow paths, overflow risk in the declared targets, +left-to-right vs top-to-bottom appropriateness. One concrete suggestion if +issues are found. + +### 4. Labels & language + +Consistency with Policy `Label language`. Tone/abbreviations consistent +across labels. Mixed-case proper nouns intentional. + +## Tone + +Direct and specific. Cite line numbers. Do not rewrite the diagram — +suggest changes textually. End with a one-line verdict: `LGTM` / +`Minor suggestions` / `Recommend rework`. diff --git a/plantuml/tests/smoke/test-advisor.sh b/plantuml/tests/smoke/test-advisor.sh new file mode 100755 index 0000000..b8bd9f2 --- /dev/null +++ b/plantuml/tests/smoke/test-advisor.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-advisor/SKILL.md" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" + +grep -qE '^name:\s*plantuml-advisor' "$SKILL" || fail "skill name wrong" +grep -qE '^model:\s*sonnet' "$SKILL" || fail "skill not sonnet" +for section in "Current type" "Intent reading" "Recommended type" "Migration sketch"; do + grep -q "$section" "$SKILL" || fail "SKILL.md does not document section '$section'" +done +grep -q 'principles.md' "$SKILL" || fail "advisor must reference principles.md" + +echo "PASS: plantuml-advisor static smoke" diff --git a/plantuml/tests/smoke/test-review.sh b/plantuml/tests/smoke/test-review.sh new file mode 100755 index 0000000..5ca8d26 --- /dev/null +++ b/plantuml/tests/smoke/test-review.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-review/SKILL.md" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" + +grep -qE '^name:\s*plantuml-review' "$SKILL" || fail "skill name wrong" +grep -qE '^model:\s*sonnet' "$SKILL" || fail "skill not sonnet" +# Required output sections +for section in "Type fit" "Detail level" "Layout" "Labels"; do + grep -q "$section" "$SKILL" || fail "SKILL.md does not document section '$section'" +done +# No agent dispatch — review is interactive +if grep -qE '^allowed-tools:.*Task' "$SKILL"; then + fail "review must NOT use Task" +fi + +echo "PASS: plantuml-review static smoke" From 6d2dcaf52fc2bd6a6b6266b501bf84e19a2dd503 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:08:12 +0200 Subject: [PATCH 08/11] fix(plantuml): correct advisor citation example, add no-Task guard --- plantuml/skills/plantuml-advisor/SKILL.md | 7 ++++--- plantuml/tests/smoke/test-advisor.sh | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plantuml/skills/plantuml-advisor/SKILL.md b/plantuml/skills/plantuml-advisor/SKILL.md index 5e736cd..b867295 100644 --- a/plantuml/skills/plantuml-advisor/SKILL.md +++ b/plantuml/skills/plantuml-advisor/SKILL.md @@ -36,9 +36,10 @@ clarification would help. ### 3. Recommended type -`Same` (no change needed) or `<better-type>`. Cite the relevant rule from -`principles.md` (e.g., "principles.md §'Behavior over structure' suggests -sequence over class for runtime interactions"). One-paragraph rationale. +`Same` (no change needed) or `<better-type>`. Cite the relevant principle +from `principles.md` by section number and title (e.g., +"`principles.md §4 Layout follows flow, not convenience` argues for a +sequence diagram when ordering is the message"). One-paragraph rationale. ### 4. Migration sketch diff --git a/plantuml/tests/smoke/test-advisor.sh b/plantuml/tests/smoke/test-advisor.sh index b8bd9f2..a8b22ba 100755 --- a/plantuml/tests/smoke/test-advisor.sh +++ b/plantuml/tests/smoke/test-advisor.sh @@ -15,4 +15,9 @@ for section in "Current type" "Intent reading" "Recommended type" "Migration ske done grep -q 'principles.md' "$SKILL" || fail "advisor must reference principles.md" +# No agent dispatch — advisor is interactive +if grep -qE '^allowed-tools:.*Task' "$SKILL"; then + fail "advisor must NOT use Task" +fi + echo "PASS: plantuml-advisor static smoke" From 64cd1cdba441afa4355241c2496ab2705acc87bb Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:10:44 +0200 Subject: [PATCH 09/11] feat(plantuml): migrate skill + puml-migrator agent with manual-edit detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Phase 6: - plantuml-migrate skill (Sonnet orchestrator): reads Policy from CLAUDE.md, computes expected .plantuml/ content in-memory, detects manual edits via SHA-256 hash divergence (shasum -a 256, BSD-compatible), presents three options on divergence (promote-to-policy / overwrite / abort), dispatches puml-migrator per .puml file in parallel, and runs plantuml -checkonly. - puml-migrator agent (Haiku worker): applies declarative edit plans (replace_include, rename_var, update_target_directive) and returns a JSON status with applied/skipped arrays. - Static smoke test (TDD red→green). --- plantuml/agents/puml-migrator/AGENT.md | 60 ++++++++++++++++++++++ plantuml/skills/plantuml-migrate/SKILL.md | 61 +++++++++++++++++++++++ plantuml/tests/smoke/test-migrate.sh | 20 ++++++++ 3 files changed, 141 insertions(+) create mode 100644 plantuml/agents/puml-migrator/AGENT.md create mode 100644 plantuml/skills/plantuml-migrate/SKILL.md create mode 100755 plantuml/tests/smoke/test-migrate.sh diff --git a/plantuml/agents/puml-migrator/AGENT.md b/plantuml/agents/puml-migrator/AGENT.md new file mode 100644 index 0000000..e3f67df --- /dev/null +++ b/plantuml/agents/puml-migrator/AGENT.md @@ -0,0 +1,60 @@ +--- +name: puml-migrator +description: "Apply a declarative edit plan to a single `.puml` file. Returns a JSON status of applied/skipped operations. Dispatched by plantuml-migrate." +model: haiku +allowed-tools: Read, Edit +--- + +# PlantUML Migrator Agent + +Haiku worker for `plantuml-migrate`. You receive one file and a list of +edit operations. Apply them mechanically. Emit a JSON status. + +**Never read plugin assets.** Only the user-project paths in your input. + +## Input + +```json +{ + "project_root": "/abs/path", + "file": "diagrams/Foo.puml", + "edit_plan": [ + {"op": "replace_include", "from": "_theme_v1.puml", "to": "_theme_v2.puml"}, + {"op": "rename_var", "from": "$oldname", "to": "$newname"}, + {"op": "update_target_directive", "to": "docx"} + ] +} +``` + +Supported ops: +- `replace_include` — replace `!include <from>` with `!include <to>`. +- `rename_var` — replace all occurrences of `<from>` with `<to>` (case-sensitive). +- `update_target_directive` — replace `!$target = "..."` with `!$target = "<to>"`. + +## Behavior + +For each op in order: +1. Locate the relevant lines via Read. +2. Apply via Edit if matches found; else mark skipped (`reason: "no match"`). +3. After all ops applied, re-read the file to confirm syntactic plausibility + (closing tags `@enduml` still present, no doubled directives). + +## Output + +```json +{ + "file": "diagrams/Foo.puml", + "applied": [{"op": "replace_include", "from": "_theme_v1.puml", "to": "_theme_v2.puml"}], + "skipped": [{"op": "rename_var", "from": "$x", "reason": "no match"}], + "error": null +} +``` + +`error` is null on success or a one-line message on failure. + +## Don'ts + +- Do NOT invent ops not listed. +- Do NOT modify files outside `file`. +- Do NOT touch `.plantuml/_*.puml` policy partials. +- Do NOT emit anything besides the JSON object. diff --git a/plantuml/skills/plantuml-migrate/SKILL.md b/plantuml/skills/plantuml-migrate/SKILL.md new file mode 100644 index 0000000..1190b8a --- /dev/null +++ b/plantuml/skills/plantuml-migrate/SKILL.md @@ -0,0 +1,61 @@ +--- +name: plantuml-migrate +description: Apply a Policy change across all `.puml` files in the project (theme switch, target add/remove, brand colors update). Use after editing `## PlantUML Policy` in CLAUDE.md. Detects manual edits to `.plantuml/_*.puml` and halts to prompt before overwriting. +allowed-tools: Read, Edit, Glob, Bash, Task +--- + +# PlantUML Migrate + +Propagate a Policy change across the project's `.plantuml/` and `.puml` +files. + +## Flow + +1. **Read current Policy** from `CLAUDE.md` § "PlantUML Policy". Abort if + absent. +2. **Compute expected `.plantuml/`** files as if the Policy were freshly + materialized (run the same generation steps as `plantuml-bootstrap`, + but in-memory, producing strings rather than disk writes). +3. **Manual-edit detection (hash divergence)** — for each existing + `.plantuml/_brand.puml`, `_theme.puml`, `_fonts.puml`, `_layout.puml`: + compare its on-disk SHA-256 to the SHA-256 of the in-memory expected + content. Use `shasum -a 256` (BSD-compatible). +4. **If any divergence is detected**, list the divergent files and + present three options: + - **promote-to-policy** — re-engineer Policy fields from the on-disk + content (calls the `mode=reverse` flow of `plantuml-bootstrap` + conceptually). + - **overwrite** — proceed with regeneration, discarding manual edits. + - **abort** — exit with status 1. + Use `AskUserQuestion` if available; else inline chat. Do NOT proceed + without explicit user choice. +5. **Apply changes**: + - Regenerate divergent or stale `.plantuml/_*.puml` from the expected + content. + - Add/remove `_targets/<t>.puml` per current Policy targets vs files + present. +6. **Edit-plan for `.puml`**: if the regeneration changed anything that + authored diagrams reference (rare — only include filename rename or + target directive change), build an `edit_plan` and dispatch + `puml-migrator` per file in parallel. +7. **Validate**: run `plantuml -checkonly` over every `.puml` file. Report + any failure. + +## Output + +``` +Migration summary +- regenerated: .plantuml/_brand.puml, .plantuml/_theme.puml +- targets added: docx +- diagrams edited: 0 +- checkonly: 12 ok, 0 errors + +Migration complete. +``` + +## Notes + +- The skill is **stateful**: it modifies the user's project. Always + produce a summary so the user can review. +- For projects without a Policy: aborts with `"migrate requires a + PlantUML Policy — run /plantuml-init first"`. diff --git a/plantuml/tests/smoke/test-migrate.sh b/plantuml/tests/smoke/test-migrate.sh new file mode 100755 index 0000000..7e32c3c --- /dev/null +++ b/plantuml/tests/smoke/test-migrate.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +PLUGIN_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SKILL="$PLUGIN_ROOT/skills/plantuml-migrate/SKILL.md" +AGENT="$PLUGIN_ROOT/agents/puml-migrator/AGENT.md" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +[[ -f "$SKILL" ]] || fail "missing $SKILL" +[[ -f "$AGENT" ]] || fail "missing $AGENT" + +grep -qE '^name:\s*plantuml-migrate' "$SKILL" || fail "skill name wrong" +grep -qiE 'manual.edit.detect|hash|divergent' "$SKILL" || fail "skill missing manual-edit detection language" + +grep -qE '^name:\s*puml-migrator' "$AGENT" || fail "agent name wrong" +grep -qE '^model:\s*haiku' "$AGENT" || fail "agent not haiku" +grep -qE '^allowed-tools:.*Edit' "$AGENT" || fail "agent missing Edit" + +echo "PASS: plantuml-migrate static smoke" From 34306b62cc93ad7595b0927502c2abada0873c43 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:16:30 +0200 Subject: [PATCH 10/11] =?UTF-8?q?fix(plantuml):=20harden=20migrate=20skill?= =?UTF-8?q?=20=E2=80=94=20backup/recovery,=20idempotency,=20normalization,?= =?UTF-8?q?=20agent=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plantuml/agents/puml-migrator/AGENT.md | 48 +++++++-- plantuml/skills/plantuml-migrate/SKILL.md | 124 +++++++++++++++++----- 2 files changed, 138 insertions(+), 34 deletions(-) diff --git a/plantuml/agents/puml-migrator/AGENT.md b/plantuml/agents/puml-migrator/AGENT.md index e3f67df..bf9525a 100644 --- a/plantuml/agents/puml-migrator/AGENT.md +++ b/plantuml/agents/puml-migrator/AGENT.md @@ -1,6 +1,6 @@ --- name: puml-migrator -description: "Apply a declarative edit plan to a single `.puml` file. Returns a JSON status of applied/skipped operations. Dispatched by plantuml-migrate." +description: "Apply a declarative edit plan to a single `.puml` file. Returns a JSON status of applied/skipped/error operations. Dispatched by plantuml-migrate." model: haiku allowed-tools: Read, Edit --- @@ -26,18 +26,36 @@ edit operations. Apply them mechanically. Emit a JSON status. } ``` -Supported ops: -- `replace_include` — replace `!include <from>` with `!include <to>`. -- `rename_var` — replace all occurrences of `<from>` with `<to>` (case-sensitive). -- `update_target_directive` — replace `!$target = "..."` with `!$target = "<to>"`. +Supported ops (no others — emit `error` if you receive an unknown op): + +- `replace_include` — replace the unique line `!include <from>` with + `!include <to>`. Use `Edit` with the file's actual `!include <from>` + text as `old_string` (read it first to capture exact whitespace). +- `rename_var` — replace **all occurrences** of `<from>` with `<to>`, + case-sensitive. Use `Edit` with `replace_all: true`. Match is exact + string substitution; do not re-format. +- `update_target_directive` — find the line matching + `^\s*!\s*\$target\s*=\s*"[^"]*"\s*$`, read it via `Read` to capture + exact whitespace, then `Edit` it to `!$target = "<to>"` preserving + the file's leading indentation. The directive appears at most once. ## Behavior For each op in order: -1. Locate the relevant lines via Read. -2. Apply via Edit if matches found; else mark skipped (`reason: "no match"`). -3. After all ops applied, re-read the file to confirm syntactic plausibility - (closing tags `@enduml` still present, no doubled directives). + +1. **Locate** the relevant lines via `Read` to capture exact whitespace + and surrounding context (Edit requires byte-exact `old_string`). +2. **Apply** via `Edit`. If `Edit` reports no match, mark the op as + `skipped` with `reason: "no match"`. If `Edit` errors out for any + other reason, stop processing further ops and set the agent-level + `error` field. +3. **Confirm** after each op: re-Read the file. If `@startuml` / + `@enduml` are missing, doubled, or otherwise malformed, stop. The + file is now in an **inconsistent state** — record this in `error` + and surface in the response. Do NOT attempt to undo applied edits; + the orchestrator's backup mechanism handles recovery. + +After all ops, do one final confirmation pass. ## Output @@ -50,11 +68,21 @@ For each op in order: } ``` -`error` is null on success or a one-line message on failure. +`error` is `null` on success or a one-line message describing the +failure. When `error` is non-null, `applied` lists the ops that were +already written to disk before the failure — the file is in an +inconsistent state and the orchestrator must surface this prominently. + +If the input JSON cannot be parsed, emit: + +```json +{"file": "", "applied": [], "skipped": [], "error": "input parse error"} +``` ## Don'ts - Do NOT invent ops not listed. - Do NOT modify files outside `file`. - Do NOT touch `.plantuml/_*.puml` policy partials. +- Do NOT attempt rollback of partial edits — the orchestrator owns recovery. - Do NOT emit anything besides the JSON object. diff --git a/plantuml/skills/plantuml-migrate/SKILL.md b/plantuml/skills/plantuml-migrate/SKILL.md index 1190b8a..de9f639 100644 --- a/plantuml/skills/plantuml-migrate/SKILL.md +++ b/plantuml/skills/plantuml-migrate/SKILL.md @@ -1,61 +1,137 @@ --- name: plantuml-migrate -description: Apply a Policy change across all `.puml` files in the project (theme switch, target add/remove, brand colors update). Use after editing `## PlantUML Policy` in CLAUDE.md. Detects manual edits to `.plantuml/_*.puml` and halts to prompt before overwriting. +description: Apply a Policy change across all `.puml` files in the project (theme switch, target add/remove, brand colors update). Use after editing `## PlantUML Policy` in CLAUDE.md. Detects manual edits to `.plantuml/_*.puml` and halts to prompt before overwriting. Backs up `.plantuml/` before any destructive write. allowed-tools: Read, Edit, Glob, Bash, Task --- # PlantUML Migrate Propagate a Policy change across the project's `.plantuml/` and `.puml` -files. +files. The most invasive skill in the plugin — it modifies user files, +so safety mechanisms are explicit. ## Flow 1. **Read current Policy** from `CLAUDE.md` § "PlantUML Policy". Abort if - absent. + absent with `"migrate requires a PlantUML Policy — run /plantuml-init first"`. + 2. **Compute expected `.plantuml/`** files as if the Policy were freshly - materialized (run the same generation steps as `plantuml-bootstrap`, - but in-memory, producing strings rather than disk writes). + materialized. Run the same generation logic as `plantuml-bootstrap` + (see `${CLAUDE_PLUGIN_ROOT}/skills/plantuml-bootstrap/SKILL.md` § + "Generation"), but produce in-memory strings instead of disk writes. + For each of the four partials (`_brand.puml`, `_theme.puml`, + `_fonts.puml`, `_layout.puml`), hold the expected bytes. + 3. **Manual-edit detection (hash divergence)** — for each existing `.plantuml/_brand.puml`, `_theme.puml`, `_fonts.puml`, `_layout.puml`: - compare its on-disk SHA-256 to the SHA-256 of the in-memory expected - content. Use `shasum -a 256` (BSD-compatible). + - **Normalize** both sides before hashing to avoid false positives: + strip CR (`\r`), collapse trailing whitespace per line, ensure a + single trailing newline at EOF. Same normalization on both sides. + - Compute SHA-256 via `shasum -a 256` (BSD-compatible across macOS + and Linux). + - **Idempotency short-circuit**: if all four hashes match expected + AND no targets need adding/removing AND no Policy-driven `.puml` + edits are needed, print `"migration: nothing to do"` and exit 0. + 4. **If any divergence is detected**, list the divergent files and - present three options: - - **promote-to-policy** — re-engineer Policy fields from the on-disk - content (calls the `mode=reverse` flow of `plantuml-bootstrap` - conceptually). - - **overwrite** — proceed with regeneration, discarding manual edits. + present three options. Use `AskUserQuestion` if available; else + inline chat. + + **Safety contract:** Do NOT proceed without an explicit, unambiguous + user choice. If the user does not respond, or the response is + ambiguous, treat it as `abort`. NEVER default to `overwrite` or + `promote-to-policy`. Overwrite destroys user work. + + - **promote-to-policy** — reconcile the on-disk drift back into the + Policy section. Concretely: + 1. Parse each divergent partial: + - `_brand.puml` → extract `!$<name> = "#…"` lines as brand colors. + - `_theme.puml` → extract `!theme <name>` if present, else mark + theme as `custom`. + - `_fonts.puml` → extract `defaultFontName` and `defaultFontSize`. + - `_layout.puml` → extract `!pragma layout <name>` and detect + `left to right direction`. + 2. Map each parsed value to the corresponding Policy field. + 3. Show a per-field diff (Policy current → Policy proposed) and + ask the user to confirm field-by-field. Any rejected field is + reverted to the Policy value (and the on-disk partial will be + regenerated to match). + 4. After confirmation, `Edit` the `## PlantUML Policy` section in + CLAUDE.md to apply the accepted changes. Re-run from Step 2 + with the updated Policy. + - **overwrite** — proceed to Step 5 with the in-memory expected + content, discarding manual edits. - **abort** — exit with status 1. - Use `AskUserQuestion` if available; else inline chat. Do NOT proceed - without explicit user choice. -5. **Apply changes**: + +5. **Apply changes (with backup)**: + - **Backup first**: copy the existing `.plantuml/` to a timestamped + directory under the system temp dir, e.g. + `cp -R .plantuml "$(mktemp -d -t plantuml-migrate-backup.XXXXXX)/"`. + Capture the backup path; include it in the final summary so the + user can roll back. - Regenerate divergent or stale `.plantuml/_*.puml` from the expected - content. - - Add/remove `_targets/<t>.puml` per current Policy targets vs files - present. -6. **Edit-plan for `.puml`**: if the regeneration changed anything that + content. "Stale" means the on-disk hash differs from expected and + the user chose `overwrite` (or there were no manual edits to begin + with). + - Add/remove `.plantuml/_targets/<t>.puml` per current Policy targets + vs files present. + +6. **Edit-plan for `.puml`**: if the regeneration changed something that authored diagrams reference (rare — only include filename rename or - target directive change), build an `edit_plan` and dispatch - `puml-migrator` per file in parallel. -7. **Validate**: run `plantuml -checkonly` over every `.puml` file. Report - any failure. + target directive change), build an `edit_plan` per file. **Skip + dispatch when the per-file edit_plan is empty.** If all files have + empty plans, skip Step 6 entirely. Otherwise, dispatch + `puml-migrator` per file in parallel and aggregate the JSON results + (applied/skipped/error counts) for the summary. + +7. **Validate**: run `plantuml -checkonly` over every `.puml` file + (excluding `.plantuml/_*.puml`). Aggregate results. On any failure, + stop reporting "complete" and emit a recovery block (see Output). ## Output +Successful run: + ``` Migration summary +- backup: /tmp/plantuml-migrate-backup.XXXXXX/.plantuml - regenerated: .plantuml/_brand.puml, .plantuml/_theme.puml - targets added: docx -- diagrams edited: 0 +- targets removed: (none) +- diagrams edited: 0 applied, 0 skipped, 0 errors - checkonly: 12 ok, 0 errors Migration complete. ``` +Partial-failure run (any Step 7 error or any per-file `error` from +`puml-migrator`): + +``` +Migration summary (PARTIAL — REVIEW REQUIRED) +- backup: /tmp/plantuml-migrate-backup.XXXXXX/.plantuml +- regenerated: .plantuml/_brand.puml +- diagrams edited: 8 applied, 1 skipped, 1 error + - error: diagrams/Foo.puml — confirmation failed after replace_include +- checkonly: 11 ok, 1 error + - error: diagrams/Bar.puml — exited 1: include not found at line 2 + +To roll back: + rm -rf .plantuml && cp -R /tmp/plantuml-migrate-backup.XXXXXX/.plantuml . + (and revert any Step 6 edits via git checkout if your tree was clean) +``` + ## Notes - The skill is **stateful**: it modifies the user's project. Always produce a summary so the user can review. +- **Concurrency**: do NOT run `plantuml-migrate` while editing `.puml` + files in another tool. There is no locking; concurrent edits may be + lost. v1.0.0 limitation. +- **Atomicity**: Step 6 dispatches per-file edits in parallel. There is + no global rollback if Step 7 fails after Step 6 succeeded — the + backup covers `.plantuml/` only, not authored `.puml` edits. If your + tree was clean before invocation, `git checkout -- diagrams/` is the + fastest recovery. - For projects without a Policy: aborts with `"migrate requires a PlantUML Policy — run /plantuml-init first"`. From 81084c7b9be2ba84f060e3684fede6de30e117a6 Mon Sep 17 00:00:00 2001 From: Giovanni Costagliola <giovanni.costagliola@gmail.com> Date: Sat, 25 Apr 2026 15:18:21 +0200 Subject: [PATCH 11/11] docs(plantuml): finalize README with concrete examples and known limitations --- plantuml/README.md | 112 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 11 deletions(-) diff --git a/plantuml/README.md b/plantuml/README.md index 6e29d99..96db8d5 100644 --- a/plantuml/README.md +++ b/plantuml/README.md @@ -2,6 +2,12 @@ Authoring, rendering, and maintenance of PlantUML diagrams in Claude Code projects. Policy-driven theming, multi-target output (web/docx/pdf/pptx), and a maintenance layer for lint, validate, review, advisor, and migration. +## Status + +v1.0.0 — all 8 skills, 4 agents, and 1 command are present and validated by in-tree smoke tests. + +The smoke tests (`plantuml/tests/smoke/`) are static structural assertions: they verify that skills, agents, commands, and the plugin manifest are correctly wired. Behavioral correctness (LLM-driven authoring and maintenance flows) is validated in real use after install. + ## Requirements - `plantuml` CLI — `brew install plantuml` (macOS) or `apt install plantuml` (Debian/Ubuntu) @@ -10,26 +16,110 @@ Authoring, rendering, and maintenance of PlantUML diagrams in Claude Code projec ## Install -Add the marketplace and install the plugin via Claude Code's plugin manager. +Add this marketplace to Claude Code and install the `plantuml` plugin via the plugin manager. ## Quickstart -1. Run `/plantuml-init` in your project root to create a `## PlantUML Policy` in `CLAUDE.md` and materialize `.plantuml/`. -2. Author `.puml` files — the `plantuml-authoring` skill activates on intent. -3. Maintain: `plantuml-lint`, `plantuml-validate`, `plantuml-review`, `plantuml-advisor`, `plantuml-migrate`. +``` +# 1. Bootstrap your project +> /plantuml-init + + Creating ## PlantUML Policy in CLAUDE.md... + Materializing .plantuml/ (theme: aws, targets: png svg)... + Done. Edit CLAUDE.md > PlantUML Policy to customize. + +# 2. Author a diagram (the plantuml-authoring skill activates on intent) +> Create a sequence diagram for our login flow and save it as diagrams/login.puml + + [plantuml-authoring activates] + Writing diagrams/login.puml with skinparam includes from _base.puml... + Done. + +# 3. Maintain it +> Lint all .puml files + + [plantuml-lint activates, dispatches puml-linter agents in parallel] + diagrams/login.puml: OK + +> plantuml-validate mode=bless + + [plantuml-validate activates, renders all targets, writes baselines] + diagrams/login.puml [png]: blessed + diagrams/login.puml [svg]: blessed +``` ## Components -- **Skills:** `plantuml-authoring`, `plantuml-convert`, `plantuml-bootstrap`, `plantuml-lint`, `plantuml-validate`, `plantuml-review`, `plantuml-advisor`, `plantuml-migrate` -- **Agents:** `puml-linter`, `puml-renderer`, `puml-migrator`, `puml-visual-checker` -- **Commands:** `/plantuml-init` +### Authoring and Rendering + +| Component | Kind | Purpose | +|-----------|------|---------| +| `plantuml-authoring` | Skill | Create or restructure `.puml` files for any diagram type (UML, C4, ER, ArchiMate, MindMap, WBS, Gantt, Salt, JSON/YAML, nwdiag family) | +| `plantuml-convert` | Skill | Convert `.puml` files to PNG, SVG, or PDF; used by document skills as a render step | +| `puml-renderer` | Agent | Build-time worker: render or validate a single (file, target) pair and compare against a baseline | + +### Bootstrap + +| Component | Kind | Purpose | +|-----------|------|---------| +| `plantuml-bootstrap` | Skill | Create the `## PlantUML Policy` in `CLAUDE.md` and materialize `.plantuml/`; also runs in `mode=reverse` to recover policy from an existing directory | +| `/plantuml-init` | Command | User-facing shortcut that delegates to `plantuml-bootstrap` | + +### Maintenance + +| Component | Kind | Purpose | +|-----------|------|---------| +| `plantuml-lint` | Skill | Check `.puml` files for Policy drift, broken includes, and invariant violations (hardcoded colors, missing `_base.puml`, filename/title mismatch) | +| `plantuml-validate` | Skill | Render all declared targets and verify output against committed baselines; accepts `mode=check|bless` and `level=checkonly|svg-hash|png-perceptual` | +| `plantuml-review` | Skill | Qualitative review of a diagram for clarity, type-fit, layout, and readability | +| `plantuml-advisor` | Skill | Advise on diagram-type fit: confirm the current type or suggest a better one with a migration sketch | +| `plantuml-migrate` | Skill | Apply a Policy change (theme switch, target add/remove, brand colors) across all `.puml` files; backs up `.plantuml/` before any destructive write | + +### Build-time Workers + +| Component | Kind | Purpose | +|-----------|------|---------| +| `puml-linter` | Agent | Lint a batch of `.puml` files against Policy invariants; dispatched in parallel by `plantuml-lint` | +| `puml-migrator` | Agent | Apply a declarative edit plan to a single `.puml` file; dispatched by `plantuml-migrate` | +| `puml-visual-checker` | Agent | Smoke-check a rendered image for color, font, and layout correctness; build-time only, not user-facing in v1.0.0 | + +## Maintenance Flow + +Typical lifecycle after initial bootstrap: + +1. **Bootstrap** — `/plantuml-init` once per project. +2. **Author** — `plantuml-authoring` to create or restructure `.puml` files. +3. **Lint** — `plantuml-lint` to catch Policy drift and invariant violations. +4. **Validate** — `plantuml-validate mode=bless` to commit baselines; `mode=check` in CI. +5. **Review / Advisor** — `plantuml-review` for qualitative feedback; `plantuml-advisor` when the diagram type feels wrong. +6. **Policy change** — edit `## PlantUML Policy` in `CLAUDE.md`, then `plantuml-migrate` to propagate to all files. + +## Verifying Install + +From the marketplace root: + +```bash +bash tests/ci/run-structural-tests.sh +``` + +Then run the six plugin smoke tests: + +```bash +for t in bootstrap lint validate review advisor migrate; do + bash plantuml/tests/smoke/test-$t.sh +done +``` -See each skill's `SKILL.md` for usage details. +All must pass before considering the install complete. -## Verifying install +## Known Limitations (v1.0.0) -Run `bash tests/ci/run-structural-tests.sh` from the marketplace root. All suites should pass. +- `plantuml-validate level=png-perceptual` is not implemented and returns `unsupported`. Use `level=checkonly` (default) or `level=svg-hash` for CI. +- `plantuml-migrate` has no concurrent-edit locking. Do not run while another tool is writing `.puml` files. +- `puml-visual-checker` is a build-time agent only; it is not exposed as a user-facing skill in v1.0.0. +- Cross-machine `level=svg-hash` comparisons require pinned fonts. On heterogeneous CI, prefer `level=checkonly`. +- Agents (`agents/<name>/AGENT.md`) are auto-discovered by Claude Code. If discovery fails on first install, the orchestrating skills degrade to inline Sonnet invocations (correct but slower and costlier). Verify agent availability at first use. ## Dev -The plugin is the source of truth for all PlantUML evolution. Edits go directly under `plantuml/`. +The plugin is the source of truth for all PlantUML evolution. Edits go directly under `plantuml/`. Smoke tests live at `plantuml/tests/smoke/`. The minimal fixture project used by the smoke tests is at `plantuml/tests/fixtures/minimal-project/`.