diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index a87f2bb..26f8c2a 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -1,7 +1,7 @@
{
"name": "diagram-design",
"metadata": {
- "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling."
+ "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling."
},
"owner": {
"name": "Cathryn Lavery"
diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index 781becd..3f41ba9 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "diagram-design",
- "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
- "version": "2.5.20",
+ "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
+ "version": "2.6.0",
"author": {
"name": "Cathryn Lavery",
"url": "https://github.com/cathrynlavery"
diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json
index 715c78a..8ef7cbf 100644
--- a/.codex-plugin/plugin.json
+++ b/.codex-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "diagram-design",
- "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
- "version": "2.5.20",
+ "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
+ "version": "2.6.0",
"author": {
"name": "Cathryn Lavery",
"url": "https://github.com/cathrynlavery"
@@ -26,7 +26,7 @@
"interface": {
"displayName": "Diagram Design",
"shortDescription": "Create editorial technical and product diagrams as standalone HTML.",
- "longDescription": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, Sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
+ "longDescription": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, Sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
"developerName": "Cathryn Lavery",
"category": "Productivity",
"capabilities": [
diff --git a/.factory-plugin/plugin.json b/.factory-plugin/plugin.json
index 781becd..3f41ba9 100644
--- a/.factory-plugin/plugin.json
+++ b/.factory-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "diagram-design",
- "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
- "version": "2.5.20",
+ "description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, DP security matrix, sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, or database schema diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
+ "version": "2.6.0",
"author": {
"name": "Cathryn Lavery",
"url": "https://github.com/cathrynlavery"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 329918f..3a411f9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -142,6 +142,14 @@ jobs:
id: skin
run: python scripts/lint-skin.py --all --baseline
+ - name: Verify quantitative polar chart
+ if: always()
+ id: polar
+ shell: bash
+ run: |
+ python scripts/test-verify-polar.py
+ python scripts/verify-polar.py
+
- name: Verify sequence OAuth specs
if: always()
id: oauth
@@ -164,6 +172,16 @@ jobs:
id: motion
run: python scripts/test-verify-motion.py
+ - name: Verify doctor diagnostics contract
+ if: always()
+ id: doctor
+ run: python scripts/verify-doctor.py
+
+ - name: Verify doctor diagnostics tests
+ if: always()
+ id: doctor_tests
+ run: python scripts/test-verify-doctor.py
+
- name: Verify docs and routing sync
if: always()
id: docs_sync
@@ -221,6 +239,46 @@ jobs:
id: slopegraph_tests
run: python scripts/test-verify-slopegraph.py
+ - name: Verify sankey flow conservation
+ if: always()
+ id: sankey
+ run: python scripts/verify-sankey.py --all
+
+ - name: Verify sankey checker
+ if: always()
+ id: sankey_tests
+ run: python scripts/test-verify-sankey.py
+
+ # lint-render.py's oracle is pixels, so the browser is part of the contract:
+ # pin Playwright exactly, and let that pin choose the Chromium build. Bump
+ # both together, deliberately, by editing PLAYWRIGHT_VERSION.
+ #
+ # One platform only. Antialiasing and font fallback differ per OS, so running
+ # a pixel comparison across the whole matrix would compare six oracles rather
+ # than one, and the thresholds are calibrated on Linux Chromium. The other
+ # matrix legs still run every source-level gate.
+ - name: Install Playwright Chromium
+ if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
+ env:
+ PLAYWRIGHT_VERSION: "1.62.0"
+ shell: bash
+ run: |
+ pip install "playwright==${PLAYWRIGHT_VERSION}"
+ playwright install --with-deps chromium
+ python -c "import importlib.metadata as m; print('playwright', m.version('playwright'))"
+
+ # The checks before the sweep: a broken linter must not be able to report
+ # a clean repository.
+ - name: Verify render linter checks
+ if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
+ id: render_selftest
+ run: python scripts/lint-render.py --self-test
+
+ - name: Run render linter
+ if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
+ id: render
+ run: python scripts/lint-render.py --all
+
- name: Verify generated icon assets
if: always()
id: icons
@@ -247,10 +305,13 @@ jobs:
echo "| Devicon Normalization | ${{ steps.devicon.outcome == 'success' && '✅ Passed' || (steps.devicon.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Mojibake Repair Utility | ${{ steps.mojibake.outcome == 'success' && '✅ Passed' || (steps.mojibake.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Color Palette & Skin Linter | ${{ steps.skin.outcome == 'success' && '✅ Passed' || (steps.skin.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Quantitative Polar Chart | ${{ steps.polar.outcome == 'success' && '✅ Passed' || (steps.polar.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Sequence Diagram Grammar | ${{ steps.oauth.outcome == 'success' && '✅ Passed' || (steps.oauth.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Draw.io Import Extractor | ${{ steps.drawio.outcome == 'success' && '✅ Passed' || (steps.drawio.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Mermaid Import Extractor | ${{ steps.mermaid.outcome == 'success' && '✅ Passed' || (steps.mermaid.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Optional Motion Contract | ${{ steps.motion.outcome == 'success' && '✅ Passed' || (steps.motion.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Doctor Diagnostics Contract | ${{ steps.doctor.outcome == 'success' && '✅ Passed' || (steps.doctor.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Doctor Diagnostics Tests | ${{ steps.doctor_tests.outcome == 'success' && '✅ Passed' || (steps.doctor_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Docs & Routing Sync | ${{ steps.docs_sync.outcome == 'success' && '✅ Passed' || (steps.docs_sync.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Canonical Screenshot Freshness | ${{ steps.screenshots.outcome == 'success' && '✅ Passed' || (steps.screenshots.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Packaged Self-Check | ${{ steps.self_check.outcome == 'success' && '✅ Passed' || (steps.self_check.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
@@ -262,4 +323,8 @@ jobs:
echo "| Dumbbell Checker | ${{ steps.dumbbell_tests.outcome == 'success' && '✅ Passed' || (steps.dumbbell_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Slopegraph Shared Scale | ${{ steps.slopegraph.outcome == 'success' && '✅ Passed' || (steps.slopegraph.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Slopegraph Checker | ${{ steps.slopegraph_tests.outcome == 'success' && '✅ Passed' || (steps.slopegraph_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Sankey Flow Conservation | ${{ steps.sankey.outcome == 'success' && '✅ Passed' || (steps.sankey.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Sankey Checker | ${{ steps.sankey_tests.outcome == 'success' && '✅ Passed' || (steps.sankey_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Primitive Icons & Documentation | ${{ steps.icons.outcome == 'success' && '✅ Passed' || (steps.icons.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Render Linter Self-Test | ${{ steps.render_selftest.outcome == 'success' && '✅ Passed' || (steps.render_selftest.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Rendered Layout (paint oracle) | ${{ steps.render.outcome == 'success' && '✅ Passed' || (steps.render.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0714edb..7d3b0b9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) first. All contributions ar
## What this project is
-Diagram Design is an agent skill (Claude Code, Codex, Pi) that produces editorial-quality diagrams as self-contained HTML files. The repo is documentation-first: `skills/diagram-design/SKILL.md` is the index, each of the 38 visual types has its own reference file, and the extractor scripts in `skills/diagram-design/scripts/` turn draw.io and Mermaid sources into a structured IR.
+Diagram Design is an agent skill (Claude Code, Codex, Pi) that produces editorial-quality diagrams as self-contained HTML files. The repo is documentation-first: `skills/diagram-design/SKILL.md` is the index, each of the 39 visual types has its own reference file, and the extractor scripts in `skills/diagram-design/scripts/` turn draw.io and Mermaid sources into a structured IR.
See [README.md](README.md) for the full picture, including the design system and the import/export flows.
@@ -46,13 +46,17 @@ The helper refuses to run if the Claude and Codex versions already differ. If an
| Semantic-pattern routing | `python3 scripts/verify-semantic-motion.py --markdown-only` |
| Animated-example structure and accessibility | `python3 scripts/verify-semantic-motion.py --example-only` |
| Skin conformance of every example and template (colors, fonts, a11y, assets, scripts) | `python3 scripts/lint-skin.py --all --baseline` |
+| Rendered-layout checker and shipped examples/templates | `python3 scripts/lint-render.py --self-test && python3 scripts/lint-render.py --all` |
+| Quantitative polar encoding and variant parity | `python3 scripts/test-verify-polar.py && python3 scripts/verify-polar.py` |
| A single file, e.g. a new example | `python3 scripts/lint-skin.py skills/diagram-design/assets/example-my-type.html` |
| Sequence-doc consistency (ATL fragments, budgets) | `python3 scripts/verify-sequence-oauth.py` |
| draw.io import path (real extractor vs fixtures + docs sync) | `python3 scripts/verify-drawio-import.py && python3 scripts/test-verify-drawio-import.py` |
| Mermaid import path (grammars, adversarial input, caps, docs sync) | `python3 scripts/verify-mermaid-import.py` |
| Optional motion contract (fallbacks, controls, budgets, determinism) | `python3 scripts/test-verify-motion.py` |
+| Doctor diagnostics contract (env checks, script presence, routing wiring) | `python3 scripts/verify-doctor.py` |
+| Doctor diagnostics adversarial tests | `python3 scripts/test-verify-doctor.py` |
| Every shipped motion template/example | `python3 scripts/verify-motion.py --shipped` |
-| Docs/routing sync (description hooks, gallery, README tree, reference links, strict-bundler support paths, profile surfaces) | `python3 scripts/verify-docs-sync.py && python3 scripts/test-verify-docs-sync.py` |
+| Docs/routing sync (description hooks, gallery, README tree, reference links, strict-bundler support paths, command/prompt surfaces) | `python3 scripts/verify-docs-sync.py && python3 scripts/test-verify-docs-sync.py` |
| Canonical README screenshots match their example HTML sources and recorded PNG digests | `python3 scripts/verify-screenshot-freshness.py` |
| Packaged output self-check behaves (pass + adversarial cases) | `python3 scripts/test-self-check.py` |
| Label masks are never clipped by a node painted after them | `python3 scripts/verify-geometry.py --all` |
@@ -63,6 +67,8 @@ The helper refuses to run if the Claude and Codex versions already differ. If an
| Dumbbell checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-dumbbell.py` |
| Slopegraph axes share one scale and every endpoint matches its printed value | `python3 scripts/verify-slopegraph.py --all` |
| Slopegraph checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-slopegraph.py` |
+| Sankey flow conservation and ribbon geometry | `python3 scripts/verify-sankey.py --all` |
+| Sankey checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-sankey.py` |
| Generated icon assets are up to date (`icons.html`, `primitive-icons.md`) | `python3 scripts/build-icons.py` then `git diff --exit-code` on the two generated files |
The semantic-pattern gate also caps `skills/diagram-design/SKILL.md` at 40,000 bytes so the installed skill remains practical to load. If that gate fails, reduce duplication or move detail into a routed reference; do not remove routing vocabulary from frontmatter.
@@ -78,11 +84,17 @@ python3 scripts/test-plugin-package.py \
&& python3 scripts/verify-semantic-motion.py --example-only \
&& python3 scripts/verify-motion.py --shipped \
&& python3 scripts/lint-skin.py --all --baseline \
+ && python3 scripts/lint-render.py --self-test \
+ && python3 scripts/lint-render.py --all \
+ && python3 scripts/test-verify-polar.py \
+ && python3 scripts/verify-polar.py \
&& python3 scripts/verify-sequence-oauth.py \
&& python3 scripts/verify-drawio-import.py \
&& python3 scripts/test-verify-drawio-import.py \
&& python3 scripts/verify-mermaid-import.py \
&& python3 scripts/test-verify-motion.py \
+ && python3 scripts/verify-doctor.py \
+ && python3 scripts/test-verify-doctor.py \
&& python3 scripts/verify-docs-sync.py \
&& python3 scripts/test-verify-docs-sync.py \
&& python3 scripts/verify-screenshot-freshness.py \
@@ -94,7 +106,9 @@ python3 scripts/test-plugin-package.py \
&& python3 scripts/verify-dumbbell.py \
&& python3 scripts/test-verify-dumbbell.py \
&& python3 scripts/verify-slopegraph.py --all \
- && python3 scripts/test-verify-slopegraph.py
+ && python3 scripts/test-verify-slopegraph.py \
+ && python3 scripts/verify-sankey.py --all \
+ && python3 scripts/test-verify-sankey.py
```
### If a gate fails
@@ -102,7 +116,7 @@ python3 scripts/test-plugin-package.py \
- **`verify-plugin-package.py`:** run the bump helper if the versions did not increase. If packaging validation fails, keep both marketplaces pointed at the repository root and keep the shared skill at `skills/diagram-design/SKILL.md`.
- **`lint-skin.py`:** the failure message names the file, line, and category (`color`, `font-family`, `a11y`, `external-asset`, `pure-black`, `script`). Colors must come from the palette in `skills/diagram-design/references/style-guide.md`; fonts from the allowed list; diagrams must satisfy the accessible SVG contract (see below). The linter also requires the SHA-pinned controller from `template-motion.html` verbatim and rejects remote resources, CSS `@import`, non-fragment CSS `url()`, event handlers, `srcdoc`, executable URLs, and extra scripts.
- **`verify-*.py`:** the extractor's real behavior no longer matches its fixture or the documentation, or the reference/command/prompt wiring drifted. Fix the source of truth — do not widen a test to avoid a failure.
-- **`verify-screenshot-freshness.py`:** a canonical minimal-light example or its committed PNG changed without a synchronized catalog refresh. Before the first regeneration, install the renderer with `python3 -m pip install playwright && python3 -m playwright install chromium`. Then run `python3 scripts/render-canonical-screenshots.py`, inspect all 38 renders, and commit the updated PNGs plus `docs/screenshots/manifest.json`.
+- **`verify-screenshot-freshness.py`:** a canonical minimal-light example or its committed PNG changed without a synchronized catalog refresh. Before the first regeneration, install the renderer with `python3 -m pip install playwright && python3 -m playwright install chromium`. Then run `python3 scripts/render-canonical-screenshots.py`, inspect all 39 renders, and commit the updated PNGs plus `docs/screenshots/manifest.json`.
- **`verify-slopegraph.py`:** the two axes disagree about scale or origin, or an endpoint is drawn somewhere other than where its own declared value belongs. Fix the coordinate, never the label — and never move a point to stop two endpoint labels colliding, because crowded labels mean the values really are close.
- **`verify-geometry.py`:** a label mask overlaps a node declared later in the document, so the node fill clips the label at render time. Move the label to a free segment of its connector — keep the 6–10px gap from the stroke required by SKILL.md §6, and do not shrink the mask to sneak under the check.
- **Icon assets:** you changed `scripts/vendor/icons/` or `scripts/build-icons.py` and the generated files went stale. Rerun `python3 scripts/build-icons.py` and commit the regenerated files.
diff --git a/README.md b/README.md
index 3e0f5f9..7245293 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
*New in 2.5.10: ten more layout grammars — Sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, and database schema.*
-38 editorial diagram types for Claude Code, Codex, Factory Droid, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop. Semantic patterns describe behavior separately from layout, so a queue, policy trace, or trust boundary can use the nearest existing type without expanding the type count. Static HTML remains the default; optional motion is available for ordered explanations. The skill also redraws draw.io or Mermaid sources at a chosen format, size, and detail level.
+39 editorial diagram types for Claude Code, Codex, Factory Droid, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop. Semantic patterns describe behavior separately from layout, so a queue, policy trace, or trust boundary can use the nearest existing type without expanding the type count. Static HTML remains the default; optional motion is available for ordered explanations. The skill also redraws draw.io or Mermaid sources at a chosen format, size, and detail level.
No Figma. No generic rounded boxes. No 30-minute color-picking sessions.
@@ -24,7 +24,7 @@ No Figma. No generic rounded boxes. No 30-minute color-picking sessions.
I write at [littlemight.com](https://littlemight.com?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=intro) (and run [BestSelf.co](https://bestself.co?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=intro) on the side). Every time I needed a diagram — an architecture sketch, a flowchart, a pyramid of what matters most — I'd ask Claude and get back a generic rounded-box thing that looked nothing like the rest of the site. I'd either fight with Figma for 30 minutes or just skip the diagram.
-So I built a Claude Code skill for it. Thirty-eight visual types, editorial quality, matches your brand in 60 seconds by reading your website.
+So I built a Claude Code skill for it. Thirty-nine visual types, editorial quality, matches your brand in 60 seconds by reading your website.
> *The highest-quality move is usually deletion.* Every node earns its place. The accent color is reserved for the 1–2 things the reader should look at first. Target density: 4/10.
@@ -32,7 +32,7 @@ So I built a Claude Code skill for it. Thirty-eight visual types, editorial qual
## What it makes
-All 38 visual types ship in three static variants: minimal light, minimal dark, and full-editorial. Open any of them directly in a browser. There is no build step, JavaScript, or external image dependency.
+All 39 visual types ship in three static variants: minimal light, minimal dark, and full-editorial. Open any of them directly in a browser. There is no build step, JavaScript, or external image dependency.
@@ -98,13 +98,13 @@ All 38 visual types ship in three static variants: minimal light, minimal dark,
Story map Backbone × release slices
Database schema Physical tables + column FKs
-
+
Polar chart Cyclic magnitude · linear radius
The v2.5.10 release added the final ten types above. Compare their light, dark, and full-editorial variants in the [30-variant contact sheet](.github/pr-previews/editorial-diagrams-2.5.10.jpg).
-**Browse the live gallery:** [cathrynlavery.github.io/diagram-design](https://cathrynlavery.github.io/diagram-design/) — or open [`skills/diagram-design/assets/index.html`](skills/diagram-design/assets/index.html) locally to flip through all 38 diagrams with light / dark / full-editorial tabs.
+**Browse the live gallery:** [cathrynlavery.github.io/diagram-design](https://cathrynlavery.github.io/diagram-design/) — or open [`skills/diagram-design/assets/index.html`](skills/diagram-design/assets/index.html) locally to flip through all 39 diagrams with light / dark / full-editorial tabs.
---
@@ -145,7 +145,7 @@ Droid tracks Git plugins by commit rather than the manifest's display version. T
pi install https://github.com/cathrynlavery/diagram-design
```
-Run `/reload` in an open Pi session. Pi makes the skill available for matching diagram requests; use `/skill:diagram-design` to invoke it explicitly. Pi also loads the `/export-diagram`, `/import-mermaid`, and `/profile` prompt templates. The unpinned Git install is intentional: Pi has no automatic package refresh, so run `pi update --extensions` to pull merged updates.
+Run `/reload` in an open Pi session. Pi makes the skill available for matching diagram requests; use `/skill:diagram-design` to invoke it explicitly. Pi also loads the `/export-diagram`, `/import-mermaid`, `/profile`, and `/doctor` prompt templates. The unpinned Git install is intentional: Pi has no automatic package refresh, so run `pi update --extensions` to pull merged updates.
> **One-time migration:** an existing standalone `npx skills add` copy will not start following the Codex marketplace automatically. Remove that standalone copy, then use the Codex marketplace commands above. Likewise, uninstall a personal Cowork copy and reinstall Diagram Design from your organization's marketplace. Future marketplace version bumps then flow through each client's native update path.
@@ -213,7 +213,7 @@ Every diagram template gives the inline SVG an accessible name and description:
### Manual override
-Prefer to set tokens by hand? Open [`skills/diagram-design/references/style-guide.md`](skills/diagram-design/references/style-guide.md) and edit the table. Everything downstream reads from there — all 38 diagrams, the annotation primitive, and the gallery all inherit semantic role names (`accent`, not `#eb6c36`).
+Prefer to set tokens by hand? Open [`skills/diagram-design/references/style-guide.md`](skills/diagram-design/references/style-guide.md) and edit the table. Everything downstream reads from there — all 39 diagrams, the annotation primitive, and the gallery all inherit semantic role names (`accent`, not `#eb6c36`).
### First-run gate
@@ -234,7 +234,7 @@ The profile library is shared across Claude Code, Codex, Factory Droid, and Pi.
## Quickstart
```bash
-# From a cloned checkout, open the gallery to see all 38 diagrams
+# From a cloned checkout, open the gallery to see all 39 diagrams
open skills/diagram-design/assets/index.html # macOS
xdg-open skills/diagram-design/assets/index.html # Linux
@@ -357,11 +357,13 @@ diagram-design/
│ ├── export-diagram.md — plugin export command
│ ├── import-drawio.md — plugin draw.io import command
│ ├── import-mermaid.md — plugin Mermaid import command
-│ └── profile.md — plugin client-profile command
+│ ├── profile.md — plugin client-profile command
+│ └── doctor.md — plugin environment diagnostics command
├── prompts/
│ ├── export-diagram.md — Pi `/export-diagram` prompt template
│ ├── import-mermaid.md — Pi Mermaid import prompt template
-│ └── profile.md — Pi `/profile` prompt template
+│ ├── profile.md — Pi `/profile` prompt template
+│ └── doctor.md — Pi `/doctor` diagnostics prompt template
├── skills/
│ └── diagram-design/
│ ├── SKILL.md — philosophy, selection guide, checklist
@@ -409,7 +411,7 @@ diagram-design/
│ └── assets/
│ ├── index.html — live gallery, tabbed
│ ├── template*.html — scaffolds for new diagrams
-│ ├── example-.html — 3 variants × 38 types
+│ ├── example-.html — 3 variants × 39 types
│ ├── example-loop-terminal.html
│ ├── example-quadrant-consultant.html
│ ├── example-import-drawio.html
@@ -418,11 +420,18 @@ diagram-design/
│ └── example-sequence-oauth*.html
├── scripts/
│ ├── bump-plugin-version.py — synchronized Claude/Codex/Factory version bump
-│ ├── render-canonical-screenshots.py — deterministic 38-type PNG catalog renderer
+│ ├── render-canonical-screenshots.py — deterministic 39-type PNG catalog renderer
│ ├── verify-screenshot-freshness.py — source + screenshot digest gate
│ ├── verify-plugin-package.py — version + marketplace package gate
│ ├── test-plugin-package.py — adversarial package-gate tests
-│ ├── test-verify-docs-sync.py — docs/profile-surface gate tests
+│ ├── lint-render.py — Chromium rendered-layout checker
+│ ├── verify-doctor.py — doctor diagnostics contract gate
+│ ├── test-verify-doctor.py — doctor diagnostics adversarial tests
+│ ├── verify-polar.py — quantitative polar encoding gate
+│ ├── test-verify-polar.py — polar gate adversarial tests
+│ ├── verify-sankey.py — Sankey conservation + geometry gate
+│ ├── test-verify-sankey.py — Sankey gate adversarial tests
+│ ├── test-verify-docs-sync.py — docs/routing-surface gate tests
│ └── fixtures/
│ ├── sample-flowchart.mmd
│ ├── sample-readme-with-mermaid.md
@@ -449,10 +458,47 @@ behavior, resource caps, named failures, and reference/command wiring.
Label placement is gated geometrically: `python3 scripts/verify-geometry.py --all` fails CI when a label mask overlaps a node declared later in the document, because the node fill would clip the text at render time. `python3 scripts/test-verify-geometry.py` keeps that checker honest in both directions.
Treemaps get a second geometric gate, because their whole claim is that area *is* the encoding: `python3 scripts/verify-treemap.py --all` fails CI when a cell's share of the drawn area doesn't match the value printed inside it, or when a label overruns the cell it names. It measures area error as a *relative* figure — an absolute one passes exactly the small cells most likely to be wrong. `python3 scripts/test-verify-treemap.py` keeps it honest in both directions.
-Docs and routing surfaces are themselves gated: `python3 scripts/verify-docs-sync.py` fails CI if the SKILL.md description loses a type's lexical hook, the gallery can't reach a shipped example, the README tree names a file that doesn't exist, a relative reference link is broken, a scanner-visible support path is not shipped inside the skill package, or the Claude/Pi profile surfaces drift from `profiles.md`. `python3 scripts/test-verify-docs-sync.py` exercises those newer checks adversarially, including the strict-bundler behavior used by Hermes Agent. The skill also ships `skills/diagram-design/scripts/self_check.py` — a distilled output checker installed agents can run on their own generated diagrams; `python3 scripts/test-self-check.py` keeps it honest. Settled design decisions (why one pinned controller, why patterns never add types, the autoplay policy, the SKILL.md byte cap, why label placement is verified geometrically, and why client profiles use marker-first resolution) live as short ADRs in `docs/adr/` — read them before relitigating one, add one when you settle a new policy.
+Docs and routing surfaces are themselves gated: `python3 scripts/verify-docs-sync.py` fails CI if the SKILL.md description loses a type's lexical hook, the gallery can't reach a shipped example, the README tree names a file that doesn't exist, a relative reference link is broken, a scanner-visible support path is not shipped inside the skill package, or any command/prompt surface drifts from its routed reference. `python3 scripts/test-verify-docs-sync.py` exercises those newer checks adversarially, including the strict-bundler behavior used by Hermes Agent. The skill also ships `skills/diagram-design/scripts/self_check.py` — a distilled output checker installed agents can run on their own generated diagrams; `python3 scripts/test-self-check.py` keeps it honest. Settled design decisions (why one pinned controller, why patterns never add types, the autoplay policy, the SKILL.md byte cap, why label placement is verified geometrically, and why client profiles use marker-first resolution) live as short ADRs in `docs/adr/` — read them before relitigating one, add one when you settle a new policy.
All pull requests and pushes are automatically validated across Linux, Windows, and macOS runners via GitHub Actions CI (`.github/workflows/ci.yml`).
+`lint-skin.py` reads the source. `lint-render.py` renders it — headless Chromium
+reports what actually got painted, which catches content cut off by the SVG
+viewport, collapsed SVGs, horizontal page overflow, missing local assets and JS
+errors. Both run in CI on every pull request.
+
+```bash
+pip install playwright && playwright install chromium # same dep as PNG export
+python3 scripts/lint-render.py --self-test # checks the checks
+python3 scripts/lint-render.py --all # examples and templates
+python3 scripts/lint-render.py
+python3 scripts/lint-render.py --fonts --all # measure with the real webfonts
+```
+
+Clipping is measured by paint, not geometry: `getBoundingClientRect()` on an SVG
+child ignores stroke width, markers and filter bleed, and knows nothing about
+`clip-path` or `overflow: visible`, so it both misses real clipping and invents
+clipping that isn't there. Instead each SVG is screenshot as authored and again
+with its `overflow` released, and the two are diffed — ink that appears outside
+was being cut off. Releases are staged — the SVG alone, then each clipping
+ancestor — so a wrapper release can't mask spill at the SVG's own edge, and an SVG
+authored `overflow: visible` inside a clipping wrapper is still checked.
+`--self-test` asserts all of that on 23 cases, over half of them cases that must
+*not* be flagged, and it also asserts the DOM is byte-identical after measuring.
+
+No golden images, so there is nothing to re-record and no PNGs in the repo.
+Network is cut at the browser's resolver, which covers WebSockets and anything
+else that bypasses request routing, with request routing as a second layer;
+`--fonts` excludes exactly the two Google Fonts hostnames and allows them only
+over HTTPS. Since the oracle is pixels, CI pins Playwright and its Chromium build
+rather than installing whatever is newest.
+
+**Font metrics differ between the default run and `--fonts`.** With network
+blocked — the default, and what CI runs — text is laid out in the fallback faces,
+not Instrument Serif and Geist. That is deterministic and machine-independent,
+which is what a linter needs, but it is not what your reader sees. Run
+`--fonts --all` locally when you care whether real text fits its box.
+
### What loads when
At startup, the agent sees only the skill name and description. When a request matches, it loads `SKILL.md`; semantic, type, and animation references are pulled in only when relevant.
@@ -470,7 +516,7 @@ At startup, the agent sees only the skill name and description. When a request m
| "Give me a terminal / CLI-window version" | `SKILL.md` + `references/primitive-terminal.md` |
| "Redraw this .drawio file for my deck" | `SKILL.md` + `references/import-drawio.md` + `references/output-spec.md` + the chosen type's reference |
| "Redraw this Mermaid block for my deck" | `SKILL.md` + `references/import-mermaid.md` + `references/output-spec.md` + the chosen type's reference |
-| Routine static diagram-making (any of the 38 visual types) | Only `SKILL.md` + that one type's reference |
+| Routine static diagram-making (any of the 39 visual types) | Only `SKILL.md` + that one type's reference |
No matter how many types exist, the agent only reads the one you need. Add a new type tomorrow and nothing else changes.
diff --git a/commands/doctor.md b/commands/doctor.md
new file mode 100644
index 0000000..bfd09a4
--- /dev/null
+++ b/commands/doctor.md
@@ -0,0 +1,22 @@
+---
+description: Run one-shot environment diagnostics for Diagram Design readiness
+argument-hint: [--strict] [--json]
+allowed-tools:
+ - Read
+ - Bash
+ - Glob
+---
+
+Run environment diagnostics for Diagram Design by following [`skills/diagram-design/references/doctor.md`](../skills/diagram-design/references/doctor.md). Treat that reference as the source of truth and do not reimplement its logic here.
+
+Full argument string: `$ARGUMENTS`
+
+## Required behavior
+
+1. Locate and apply the exact checks and output contract from `references/doctor.md`.
+2. Keep this command read-only: do not install dependencies, do not edit files, and do not run destructive git operations.
+3. If a check command fails, capture stderr, mark the check `warn` or `fail` per the reference, and continue remaining checks.
+4. Print the compact summary line plus per-check statuses, and include `Next actions` only when needed.
+5. If `--json` is passed, append the structured JSON block defined by the reference.
+
+Report only verified results from this run.
diff --git a/commands/import-drawio.md b/commands/import-drawio.md
index 2416a6b..403727a 100644
--- a/commands/import-drawio.md
+++ b/commands/import-drawio.md
@@ -22,7 +22,8 @@ Accepts `.drawio`, `.drawio.xml`, `.xml`, `.drawio.png`, and `.drawio.svg`.
- `--detail=balanced` · `--audience=mixed`.
- `--variant=light` — the minimal light template.
- A single-page file selects its only page; a multi-page file lists pages and asks which to use.
-- Type is chosen from the extracted structure; `--type` forces one of the 38.
+- Type is chosen from the extracted structure; `--type` forces one of the visual types in
+ [`SKILL.md` §3](../skills/diagram-design/SKILL.md).
## Flags
diff --git a/commands/import-mermaid.md b/commands/import-mermaid.md
index 7a7891e..5579da2 100644
--- a/commands/import-mermaid.md
+++ b/commands/import-mermaid.md
@@ -22,7 +22,8 @@ Accepts `.mmd`, `.mermaid`, and Markdown files containing fenced `mermaid` block
- `--detail=balanced` · `--audience=mixed`.
- `--variant=light` — the minimal light template.
- A single diagram selects diagram 0; a multi-block Markdown file lists blocks and asks which to use.
-- Type is chosen from the extracted grammar and structure; `--type` forces one of the 38.
+- Type is chosen from the extracted grammar and structure; `--type` forces one of the visual
+ types in [`SKILL.md` §3](../skills/diagram-design/SKILL.md).
## Flags
diff --git a/commands/profile.md b/commands/profile.md
index 727df6f..57caa8a 100644
--- a/commands/profile.md
+++ b/commands/profile.md
@@ -1,6 +1,6 @@
---
description: Save, load, inspect, update, reset, or delete diagram-design client profiles
-argument-hint: [list|save|load|show|update|reset|delete] [name]
+argument-hint: "[list|save|load|show|update|reset|delete] [name]"
allowed-tools:
- Read
- Write
diff --git a/docs/adr/0002-semantic-patterns-do-not-expand-the-taxonomy.md b/docs/adr/0002-semantic-patterns-do-not-expand-the-taxonomy.md
index 79e8a26..9b28327 100644
--- a/docs/adr/0002-semantic-patterns-do-not-expand-the-taxonomy.md
+++ b/docs/adr/0002-semantic-patterns-do-not-expand-the-taxonomy.md
@@ -1,6 +1,6 @@
# ADR 0002 — Semantic patterns never expand the visual-type taxonomy
-**Status:** accepted (v2.3)
+**Status:** accepted (v2.3; amended v2.6)
## Context
@@ -8,7 +8,7 @@ Auditing behavior-rich figures (queues, policy traces, trust boundaries) showed
## Decision
-Behavior is a separate axis. The seven semantic patterns in `references/semantic-patterns.md` each route to the **nearest existing visual type** for layout; a pattern owns semantic primitives and a tighter budget, never a second layout grammar. The visual-type count only moves when a genuinely new *layout* grammar appears (it did in v2.5.10 — see [ADR 0007](0007-new-layout-grammars.md), 28 → 38).
+Behavior is a separate axis. The seven semantic patterns in `references/semantic-patterns.md` each route to the **nearest existing visual type** for layout; a pattern owns semantic primitives and a tighter budget, never a second layout grammar. The visual-type count only moves when a genuinely new *layout* grammar appears (it did in v2.5.10 — see [ADR 0007](0007-new-layout-grammars.md), 28 → 38 — and again for Polar, 38 → 39).
## Consequences
@@ -20,6 +20,8 @@ Behavior is a separate axis. The seven semantic patterns in `references/semantic
**2026-08-18 — the count is 28.** Treemap was admitted under the escape clause above: recursive area subdivision is a layout grammar no existing type provides (bar encodes with length, nested with containment and no quantity, pyramid with rank). It shipped the full §10 set, and the counters named above moved 27 → 28 together with the prose.
-The decision itself is unchanged — semantic patterns still never add a type, and the count still moves only for a new *layout* grammar. What this amendment records is the procedure: the two counters are this ADR's enforcement, so a PR that edits them without amending this file has quietly made itself the authority. Amend here in the same PR, or the number in the test is just whatever the last contributor typed.
-
**2026-08-19 — the count is 38.** Ten additional visual grammars were admitted under the same escape clause: Sankey, fishbone, Wardley map, kanban, user journey, deployment, dependency graph, UML class, story map, and database schema. The per-type argument is in [ADR 0007](0007-new-layout-grammars.md); each ships the full §10 set, and the two counters move 28 → 38 together with the prose.
+
+**2026-08-20 — the count is 39.** Polar was admitted under the same escape clause: angle encodes ordered cyclic categories and linear radius encodes one quantitative series, a layout grammar no existing type provides. It shipped the full §10 set, and the counters named above moved 38 → 39 together with the prose.
+
+The decision itself is unchanged — semantic patterns still never add a type, and the count still moves only for a new *layout* grammar. What this amendment records is the procedure: the two counters are this ADR's enforcement, so a PR that edits them without amending this file has quietly made itself the authority. Amend here in the same PR, or the number in the test is just whatever the last contributor typed.
diff --git a/docs/adr/0007-new-layout-grammars.md b/docs/adr/0007-new-layout-grammars.md
index 85766cd..0f3b937 100644
--- a/docs/adr/0007-new-layout-grammars.md
+++ b/docs/adr/0007-new-layout-grammars.md
@@ -61,4 +61,8 @@ Kept out on purpose, so the next audit does not relitigate them.
- **ER's scope was narrowed, not duplicated.** `type-er.md` now states that it is entity-level and points at `type-db-schema.md` for the physical schema. Without that edit the two types would have overlapping "Best for" claims and the router would be ambiguous.
- The ADR 0004 byte cap became binding. Adding these types required trimming SKILL.md body prose: the §11 import consequences were compressed, the terminal-variant and typography paragraphs were tightened, and §4's six connector anti-pattern rows collapsed into one row pointing at §6 — that table was the third statement of rules already given in full in §6 and checked one by one in §9. `SKILL.md` sits at ~39.2 KB against the 40 KB cap. The next type must be paid for the same way, never by trimming the frontmatter description.
- Per-type budget rows stayed in SKILL.md §7 rather than moving into the type references, because several older references (venn, pyramid, layers, ER, swimlane, timeline) do not state their own limits. Moving the rows out would have lost those numbers.
-- `docs/screenshots/` has no PNGs for the new types yet; the README lists them in a text table instead of the image grid until the screenshots are generated.
+- At acceptance time, `docs/screenshots/` had no PNGs for the new types; README listed them in a text table pending generation.
+
+## Amendment — canonical screenshots shipped
+
+The ten types now have canonical PNGs, image-grid entries in README, and source/screenshot digests in `docs/screenshots/manifest.json`. Polar was admitted separately afterward and moved the repository-wide count from 38 to 39; this ADR still records the ten-type 28 → 38 decision.
diff --git a/docs/screenshots/manifest.json b/docs/screenshots/manifest.json
index d98fa57..866796b 100644
--- a/docs/screenshots/manifest.json
+++ b/docs/screenshots/manifest.json
@@ -101,6 +101,15 @@
"width": 2400,
"height": 1202
},
+ {
+ "slug": "polar",
+ "source": "skills/diagram-design/assets/example-polar.html",
+ "screenshot": "docs/screenshots/polar.png",
+ "source_sha256": "596e2042f18044ef1fa2c2eb849f39fa5c7767b2a17e272617ebd27e10bc86b1",
+ "screenshot_sha256": "a1dd1d4cb38db2217759c5d256cc56d832acc4f3077f090cc8691d6e4f82ef31",
+ "width": 2400,
+ "height": 1202
+ },
{
"slug": "loop",
"source": "skills/diagram-design/assets/example-loop.html",
diff --git a/docs/screenshots/polar.png b/docs/screenshots/polar.png
new file mode 100644
index 0000000..22e4d72
Binary files /dev/null and b/docs/screenshots/polar.png differ
diff --git a/docs/superpowers/plans/2026-08-18-polar-chart.md b/docs/superpowers/plans/2026-08-18-polar-chart.md
new file mode 100644
index 0000000..bfbcdf2
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-18-polar-chart.md
@@ -0,0 +1,592 @@
+# Quantitative Polar Chart Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Add a verified 28th visual type that renders one cyclic quantitative series as radial lollipops with angle=category and linear radius=magnitude.
+
+**Architecture:** The public contract lives in `references/type-polar.md`; three static HTML examples carry machine-readable `data-polar-*` metadata. A dependency-free Python verifier recomputes all endpoints and rejects hubs, nonlinear radius, visible zero rays, invalid values, and variant drift. Existing routing, gallery, README, CI, and package manifests are updated as one minor release.
+
+**Tech Stack:** Python standard library 3.10+, static HTML/CSS, inline SVG, existing repository validation scripts, headless Google Chrome for the README screenshot.
+
+**Spec:** `docs/superpowers/specs/2026-08-18-polar-chart-design.md`
+
+## Global Constraints
+
+- Angle encodes category; radius encodes magnitude with `radius = R * value / max`.
+- The shared scale starts at exactly zero; `max` is finite and greater than zero.
+- Zero renders with no value ray and no endpoint marker; its numeric label remains visible.
+- One series, 4–8 unique ordered categories, and at most one focal category.
+- No filled quantitative wedges, donut hub, gradients, negative values, logarithmic scale, interaction, animation, or external assets.
+- All three examples use the same values, `viewBox="0 0 1000 520"`, center `(500,230)`, radius `160`, max `100`, start angle `-90`, and clockwise order.
+- Static examples must satisfy the accessible SVG contract and existing skin rules.
+- No new runtime or test dependency.
+- Add a minor plugin release: `2.4.4 → 2.5.0` unless `origin/main` advances first; after a rebase, rerun `python3 scripts/bump-plugin-version.py --minor` from the new base.
+
+---
+
+### Task 1: Build the quantitative polar verifier with adversarial tests
+
+**Files:**
+- Create: `scripts/test-verify-polar.py`
+- Create: `scripts/verify-polar.py`
+
+**Interfaces:**
+- Produces: `check(path: Path) -> list[str]`, `check_variants(paths: Sequence[Path]) -> list[str]`, and CLI `python3 scripts/verify-polar.py [file ...]`.
+- Consumes later: Task 2 uses the verifier as the RED/GREEN gate for the three examples; Task 4 adds both commands to CI.
+
+- [ ] **Step 1: Write the failing verifier tests**
+
+Create `scripts/test-verify-polar.py` with a fixture builder for a four-category chart. The valid fixture has center `(100,100)`, `R=100`, max `100`, angles `-90/0/90/180`, and values `0/25/50/100`:
+
+```python
+#!/usr/bin/env python3
+from __future__ import annotations
+
+import importlib.util
+import sys
+import tempfile
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent.parent
+VERIFIER = ROOT / "scripts/verify-polar.py"
+
+
+def load_verifier():
+ spec = importlib.util.spec_from_file_location("verify_polar", VERIFIER)
+ assert spec is not None and spec.loader is not None
+ module = importlib.util.module_from_spec(spec)
+ sys.modules[spec.name] = module
+ spec.loader.exec_module(module)
+ return module
+
+
+def category(index: int, value: int, endpoint: tuple[float, float] | None) -> str:
+ body = '{}'.format(value)
+ if endpoint is not None:
+ x, y = endpoint
+ body = (
+ f''
+ f'' + body
+ )
+ return (
+ f'{body}'
+ )
+
+
+def document(categories: str, extra_svg: str = "") -> str:
+ return (
+ ''
+ ''
+ )
+
+
+VALID = document(
+ category(0, 0, None)
+ + category(1, 25, (125, 100))
+ + category(2, 50, (100, 150))
+ + category(3, 100, (0, 100))
+)
+```
+
+In `main()`, write each fixture to a temporary file and assert these results:
+
+```python
+cases = [
+ ("25 percent hub", VALID.replace('data-polar-inner-radius="0"', 'data-polar-inner-radius="25"'), "inner radius"),
+ ("sqrt radius", VALID.replace('x2="125"', 'x2="150"').replace('cx="125"', 'cx="150"'), "linear endpoint"),
+ ("visible zero ray", VALID.replace('0', '0'), "zero category"),
+ ("out of range", VALID.replace('data-polar-value="100"', 'data-polar-value="101"'), "outside 0..100"),
+ ("duplicate index", VALID.replace('data-polar-index="3"', 'data-polar-index="2"'), "indices"),
+ ("quantitative wedge", VALID.replace('', ''), "wedge"),
+]
+```
+
+Assert `check(valid_path) == []`. For every invalid case, assert at least one finding exists and one finding contains the named substring; do not assert the total count because a malformed value may correctly violate more than one invariant.
+
+Also create three valid files and change the dark fixture's `data-polar-value="25"` to `26`; assert `check_variants()` reports `variant data drift`.
+
+- [ ] **Step 2: Run the test to verify RED**
+
+Run:
+
+```bash
+python3 scripts/test-verify-polar.py
+```
+
+Expected: fail while importing missing `scripts/verify-polar.py`.
+
+- [ ] **Step 3: Implement the minimal verifier**
+
+Create `scripts/verify-polar.py` using only `dataclasses`, `html.parser`, `math`, `pathlib`, `sys`, and `typing`.
+
+Define these data structures and parser output:
+
+```python
+@dataclass
+class Category:
+ name: str
+ index: int
+ value: float
+ focal: bool
+ rays: list[dict[str, str]]
+ markers: list[dict[str, str]]
+ value_labels: int
+
+
+@dataclass
+class Chart:
+ attrs: dict[str, str]
+ categories: list[Category]
+ wedge_count: int
+```
+
+`PolarParser(HTMLParser)` must capture exactly one element carrying `data-polar-chart`; capture `g[data-polar-category]`, its `line[data-polar-ray]`, `circle[data-polar-marker]`, and `text[data-polar-value-label]`; count any `path[data-polar-wedge]`.
+
+Implement `check()` with these exact invariants:
+
+```python
+TOLERANCE = 0.75
+required = (
+ "data-polar-cx", "data-polar-cy", "data-polar-radius",
+ "data-polar-min", "data-polar-max", "data-polar-start-angle",
+ "data-polar-clockwise", "data-polar-radius-encoding",
+ "data-polar-inner-radius",
+)
+expected_angle = radians(start_angle) + (1 if clockwise else -1) * 2 * pi * index / count
+expected_radius = radius * value / maximum
+expected_x = cx + expected_radius * cos(expected_angle)
+expected_y = cy + expected_radius * sin(expected_angle)
+```
+
+Return findings for: missing/duplicate chart; missing or non-numeric metadata; min not zero; max/radius not positive; non-linear encoding; non-zero inner radius; category count outside 4–8; non-contiguous indices; duplicate trimmed names; more than one focal; value outside range; value-label count not one; zero carrying a ray or marker; positive value not carrying exactly one ray and marker; ray start not at center; ray/marker endpoint outside tolerance; any wedge.
+
+Implement `check_variants()` by comparing this signature for all paths:
+
+```python
+signature = (
+ cx, cy, radius, minimum, maximum, start_angle, clockwise,
+ tuple((c.index, c.name, c.value, c.focal) for c in sorted(categories, key=lambda c: c.index)),
+)
+```
+
+With no CLI arguments, validate the exact shipped paths `example-polar.html`, `example-polar-dark.html`, and `example-polar-full.html`; with arguments, validate those files. Print each finding as `FAIL : ` and return 1 if any finding exists; otherwise print `OK ` and return 0.
+
+- [ ] **Step 4: Run GREEN and confirm the shipped-file gate is still RED**
+
+Run:
+
+```bash
+python3 scripts/test-verify-polar.py
+python3 scripts/verify-polar.py
+```
+
+Expected: adversarial tests pass; shipped verifier fails because the three example files do not exist yet.
+
+- [ ] **Step 5: Commit the verifier slice**
+
+```bash
+git add scripts/test-verify-polar.py scripts/verify-polar.py
+git commit -m "test(polar): verify linear radius encoding"
+```
+
+---
+
+### Task 2: Add the type reference and three verified static examples
+
+**Files:**
+- Create: `skills/diagram-design/references/type-polar.md`
+- Create: `skills/diagram-design/assets/example-polar.html`
+- Create: `skills/diagram-design/assets/example-polar-dark.html`
+- Create: `skills/diagram-design/assets/example-polar-full.html`
+
+**Interfaces:**
+- Consumes: `scripts/verify-polar.py` and the approved spec.
+- Produces: three identical `data-polar-*` datasets consumed by Task 3 gallery/docs wiring.
+
+- [ ] **Step 1: Preserve the missing-example RED result**
+
+Run:
+
+```bash
+python3 scripts/verify-polar.py
+```
+
+Expected: fail naming all three missing example files.
+
+- [ ] **Step 2: Write `type-polar.md` from the approved contract**
+
+Create these sections in order:
+
+```markdown
+# Polar Chart
+
+**Best for:** one quantitative series across 4–8 categories whose clockwise order is meaningful.
+
+## Input contract
+## Quantitative encoding
+### Zero
+## Layout conventions
+## Geometry
+## Visual treatment
+## Complexity budget
+## When not to use
+## Anti-patterns
+## Examples
+```
+
+Copy the formulas, zero behavior, exclusions, scale validation, label-anchor rules, and `1000×520` reference geometry verbatim from the spec. The anti-pattern list must explicitly reject donut hubs, filled wedges, tangent labels, multiple series, truncated scales, sorted-by-value categories, and treating missing as zero. Route multiple series to Radar, non-cyclic categories to Bar, and more than eight time buckets to Line.
+
+- [ ] **Step 3: Build the minimal-light example with exact geometry**
+
+Start from `skills/diagram-design/assets/template.html`. Use this dataset and endpoint table; retain three decimals where shown so the verifier tolerance is meaningful:
+
+| i | label | value | spoke end | value end | label point (`R+28`) |
+|---|---|---:|---|---|---|
+| 0 | 00–03 | 32 | `500,70` | `500,178.8` | `500,42` |
+| 1 | 03–06 | 18 | `613.137,116.863` | `520.365,209.635` | `632.936,97.064` |
+| 2 | 06–09 | 24 | `660,230` | `538.4,230` | `688,230` |
+| 3 | 09–12 | 58 | `613.137,343.137` | `565.62,295.62` | `632.936,362.936` |
+| 4 | 12–15 | 100 focal | `500,390` | `500,390` | `500,418` |
+| 5 | 15–18 | 82 | `386.863,343.137` | `407.227,322.773` | `367.064,362.936` |
+| 6 | 18–21 | 76 | `340,230` | `378.4,230` | `312,230` |
+| 7 | 21–24 | 45 | `386.863,116.863` | `449.088,179.088` | `367.064,97.064` |
+
+The primary SVG opening must be:
+
+```html
+