# Contributing to Diagram Design Thanks for wanting to contribute — this project only gets better with more eyes on it. Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) first. All contributions are expected to keep the community welcoming. --- ## 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 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. --- ## Before you start - **Create an issue first** for anything non-trivial (new type, behavior change, import grammar work). Small fixes and docs can go straight to a PR. - **Work on a branch** — never commit directly to `main`. - **Keep the scope tight.** One PR = one concern. Mixing a new diagram type with a docs rewrite makes review slow. - **Python 3.10+ is required** for the development scripts (CI runs 3.11 and 3.12 across Linux, Windows, and macOS). --- ## Validation gates Every validation gate below must pass before a PR is ready. They also run automatically as GitHub Actions CI (`.github/workflows/ci.yml`). Every PR changes the distributed plugin package, including documentation- and CI-only PRs. Increment both native manifests together before opening or updating a PR: ```bash python3 scripts/bump-plugin-version.py # patch (default) python3 scripts/bump-plugin-version.py --minor # minor release python3 scripts/bump-plugin-version.py --major # major release ``` The helper refuses to run if the Claude, Codex, and Factory versions already differ. If another release lands on `main` first, rebase and bump again so your version remains greater than the new base. | What it checks | Command | |---|---| | Plugin bump helper and adversarial package cases | `python3 scripts/test-plugin-package.py` | | Maintainer policy matches native manifests and current CI gates | `python3 scripts/test-maintainer-policy.py` | | Synchronized, increasing versions; valid marketplace paths; packaged skill | `python3 scripts/verify-plugin-package.py origin/main` | | Claude marketplace and plugin schema, with warnings treated as errors | `claude plugin validate . --strict` | | Accessible SVG contract (unit tests for the a11y linter) | `python3 scripts/test-lint-a11y.py` | | 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` | | Semantic-motion verifier behaves (pass + adversarial cases) | `python3 scripts/test-verify-semantic-motion.py` | | Sequence-oauth verifier behaves (pass + adversarial cases) | `python3 scripts/test-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, 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` | | Label geometry checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-geometry.py` | | Treemap cells match the values they are labelled with, and labels fit | `python3 scripts/verify-treemap.py --all` | | Treemap checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-treemap.py` | | Dumbbell domain resolves finitely and its marks clear 3:1 | `python3 scripts/verify-dumbbell.py` | | 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` | | Ridgeline ridges share one amplitude and every printed range matches its bins | `python3 scripts/verify-ridgeline.py --all` | | Ridgeline checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-ridgeline.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` | | Bubble positions sit on shared axis scales and area encodes every declared size | `python3 scripts/verify-bubble.py --all` | | Bubble checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-bubble.py` | | Bump vertices sit exactly on one rank grid and every endpoint label is placed on both axes | `python3 scripts/verify-bump.py --all` | | Bump checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-bump.py` | | Beeswarm dots sit exactly at their values on one shared scale with no overprint | `python3 scripts/verify-beeswarm.py --all` | | Beeswarm checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-beeswarm.py` | | Every directional tone claim matches the ramp it describes, on every skin | `python3 scripts/verify-skin-polarity.py --all` | | Skin-polarity checker behaves (pass + adversarial cases) | `python3 scripts/test-verify-skin-polarity.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. Run them all at once before pushing: ```bash python3 scripts/test-plugin-package.py \ && python3 scripts/test-maintainer-policy.py \ && python3 scripts/verify-plugin-package.py origin/main \ && claude plugin validate . --strict \ && python3 scripts/test-lint-a11y.py \ && python3 scripts/verify-semantic-motion.py --markdown-only \ && 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/test-verify-semantic-motion.py \ && python3 scripts/test-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 \ && python3 scripts/test-self-check.py \ && python3 scripts/verify-geometry.py --all \ && python3 scripts/test-verify-geometry.py \ && python3 scripts/verify-treemap.py --all \ && python3 scripts/test-verify-treemap.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/verify-ridgeline.py --all \ && python3 scripts/test-verify-ridgeline.py \ && python3 scripts/verify-sankey.py --all \ && python3 scripts/test-verify-sankey.py \ && python3 scripts/verify-bubble.py --all \ && python3 scripts/test-verify-bubble.py \ && python3 scripts/verify-bump.py --all \ && python3 scripts/test-verify-bump.py \ && python3 scripts/verify-beeswarm.py --all \ && python3 scripts/test-verify-beeswarm.py \ && python3 scripts/verify-skin-polarity.py --all \ && python3 scripts/test-verify-skin-polarity.py ``` ### If a gate fails - **`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 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-ridgeline.py`:** a ridge is drawn on its own amplitude, a baseline sits off the stack's pitch or away from its drawn rule, a ridge is sampled on its own x positions, or a printed range is wider than the bins it claims. Fix the geometry or the declaration so they state one thing — never renormalise a single ridge to make it readable, and never move a baseline to buy one ridge headroom. - **`verify-bubble.py`:** a bubble is drawn off the shared axis scale its peers describe, its radius disagrees with the one area constant (`r = K·√size`), a second bubble wears the accent, an overlapping smaller bubble is painted under a larger one, or a bound label/tick disagrees with the mark it names. Fix the geometry, never the binding — and never nudge a bubble to open up space, because crowded bubbles mean the values really are close. - **`verify-bump.py`:** a vertex sits off the rank grid the figure itself declares, a snapshot's ranks are not a permutation of 1..N, a segment curves or arrives by a relative command, or an endpoint label is missing a coordinate, drawn inboard of the end it names, or off the gutter its peers share. Fix the geometry or the declaration, never the label — and never nudge a vertex off its row to dodge a label collision, because a rank between two ranks is not a rank. - **`verify-beeswarm.py`:** a dot is drawn off the shared value scale its peers describe, two dots declaring one value sit at two positions, a pair overprints instead of dodging, a second radius or a second non-focal fill appears, a second dot wears the accent, two dots share a `data-name` (or one is empty), anything positions a mark from CSS (`transform`, `translate`/`rotate`/`scale`, a geometry property, a motion path) by any carrier — attribute, inline `style`, or `