diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0877b4a..03ff9e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,16 @@ jobs: id: motion run: python scripts/test-verify-motion.py + - name: Verify docs and routing sync + if: always() + id: docs_sync + run: python scripts/verify-docs-sync.py + + - name: Verify packaged self-check + if: always() + id: self_check + run: python scripts/test-self-check.py + - name: Verify generated icon assets if: always() id: icons @@ -134,4 +144,6 @@ jobs: 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 "| Docs & Routing Sync | ${{ steps.docs_sync.outcome == 'success' && '✅ Passed' || (steps.docs_sync.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 echo "| Primitive Icons & Documentation | ${{ steps.icons.outcome == 'success' && '✅ Passed' || (steps.icons.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4c12fa..ad9f0f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,8 @@ Every validation gate below must pass before a PR is ready. They also run automa | 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` | | Every shipped motion template/example | `python3 scripts/verify-motion.py --shipped` | +| Docs/routing sync (description hooks, gallery reachability, README tree) | `python3 scripts/verify-docs-sync.py` | +| Packaged output self-check behaves (pass + adversarial cases) | `python3 scripts/test-self-check.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. @@ -54,7 +56,9 @@ python3 scripts/test-lint-a11y.py \ && python3 scripts/verify-sequence-oauth.py \ && python3 scripts/verify-drawio-import.py \ && python3 scripts/verify-mermaid-import.py \ - && python3 scripts/test-verify-motion.py + && python3 scripts/test-verify-motion.py \ + && python3 scripts/verify-docs-sync.py \ + && python3 scripts/test-self-check.py ``` ### If a gate fails @@ -98,11 +102,15 @@ New examples should be added to the gallery (`assets/index.html`) so they stay b Motion is opt-in. Start from `skills/diagram-design/assets/template-motion.html`, follow `references/animation.md`, and run `python3 scripts/verify-motion.py ` plus `python3 scripts/test-verify-motion.py`. A motion file must preserve complete no-JavaScript, reduced-motion, print, screenshot, and export states. Keep the controller byte-for-byte identical to the template; changes require updating the canonical template, example, documentation, and adversarial tests together. +## Design decisions (ADRs) + +Settled policies live as short records in `docs/adr/` — one pinned motion controller, semantic patterns never expanding the 27-type taxonomy, the reveal-only autoplay rule, and the SKILL.md byte cap with its trigger-rich description requirement. Read the relevant ADR before proposing a change that touches one; when a PR settles a new policy, add an ADR in the same PR. + ## Adding a new diagram type 1. Write `skills/diagram-design/references/type-.md` — layout conventions, anti-patterns, and a worked pattern for that type. Mirror an existing reference's structure. -2. Add the row to the selection table in `skills/diagram-design/SKILL.md` §3. -3. Add the three example variants (see above) and register them in the gallery. +2. Add the row to the selection table in `skills/diagram-design/SKILL.md` §3 **and** the type's name to the frontmatter `description` — `verify-docs-sync.py` fails if the description loses or lacks a type's lexical hook. +3. Add the three example variants (see above) and register them in the gallery (`assets/index.html`) — `verify-docs-sync.py` fails on any shipped example the gallery can't reach. 4. Run the full gate suite — new examples are linted automatically by `--all`. ## Changing the icon set diff --git a/README.md b/README.md index 57550fa..0bd4ab3 100644 --- a/README.md +++ b/README.md @@ -344,7 +344,8 @@ diagram-design/ │ │ └── primitive-terminal.md │ ├── scripts/ │ │ ├── drawio_extract.py — draw.io → structured IR -│ │ └── mermaid_extract.py — Mermaid → structured IR +│ │ ├── mermaid_extract.py — Mermaid → structured IR +│ │ └── self_check.py — packaged output self-check (runs installed) │ └── assets/ │ ├── index.html — live gallery, tabbed │ ├── template*.html — scaffolds for new diagrams @@ -359,6 +360,7 @@ diagram-design/ │ ├── sample-flowchart.mmd │ ├── sample-readme-with-mermaid.md │ └── sample-adversarial.mmd +├── docs/adr/ — short records of settled design decisions └── docs/screenshots/ — images used in this README ``` @@ -378,6 +380,8 @@ If you touch the Mermaid import path, `python3 scripts/verify-mermaid-import.py` it covers all supported grammars, multi-block Markdown, adversarial labels, trust-boundary behavior, resource caps, named failures, and reference/command wiring. +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, or the README tree names a file that doesn't exist. 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) 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`). ### What loads when @@ -402,6 +406,17 @@ No matter how many types exist, the agent only reads the one you need. Add a new --- +## It's working if… + +- A routine request ("make me a flowchart") loads `SKILL.md` plus exactly one type reference — nothing else. +- Before drawing, the agent states the chosen type, pattern, size, and planned cuts, then renders. +- The output is one `.html` file that opens double-clicked, offline, with no network requests beyond Google Fonts. +- Screen readers announce the diagram's title and description; `prefers-reduced-motion` shows the complete static frame. +- `python3 skills/diagram-design/scripts/self_check.py ` prints `OK` on the generated file. +- After brand onboarding, new diagrams use your site's paper, ink, accent, and fonts — with a fidelity receipt naming each. + +If any of these fail, that's a bug worth filing. + ## The design system (in one paragraph) One accent color, 1–2 focal elements per diagram. Three font families: Instrument Serif (title + italic callouts), Geist sans (node names), Geist Mono (technical sublabels). 1px hairline borders, no shadows, max border-radius 10px. Every coord, width, and gap divisible by 4 — non-negotiable, it's what keeps the diagrams from feeling AI-generated. Mono is for technical content (ports, URLs, field types), not a blanket "dev" aesthetic. Coral-tinted focal nodes draw the eye to the 1–2 things that matter. Full spec in [`SKILL.md`](skills/diagram-design/SKILL.md#5-design-system). diff --git a/docs/adr/0001-static-by-default-single-pinned-controller.md b/docs/adr/0001-static-by-default-single-pinned-controller.md new file mode 100644 index 0000000..d81f5e5 --- /dev/null +++ b/docs/adr/0001-static-by-default-single-pinned-controller.md @@ -0,0 +1,17 @@ +# ADR 0001 — Static by default; one pinned controller for motion + +**Status:** accepted (v2.3) + +## Context + +Diagrams are shared as single HTML files and embedded in posts, decks, and docs. Arbitrary inline JavaScript in a shareable artifact is both a security surface and a review burden: every generated file would need its script audited. Motion, however, genuinely clarifies ordered change (queues filling, policy traces diverging). + +## Decision + +Output is static and script-free by default (`data-motion-mode="none"`). When motion is requested, the file may carry exactly one `", 1), + "canonical data-diagram-controls attribute", + ) + check_fail( + "missing svg role", + static.replace('role="img"', 'role="presentation"', 1), + "role=img", + ) + check_fail( + "modified controller", + animated.replace("'motion-ready'", "'motion-ready' /* patched */", 1), + "exactly match the controller", + ) + check_fail( + "second script", + animated.replace("", "", 1), + "at most one script", + ) + check_fail( + "hidden motion item", + animated.replace( + '", "
", 1).replace("", "
", 1), + "