@zenuml/core 3.47.9 → 3.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/dist/cloud-icons-eHuugVSv.js.map +1 -0
  2. package/dist/zenuml.esm.mjs +2153 -2156
  3. package/dist/zenuml.esm.mjs.map +1 -0
  4. package/dist/zenuml.js +82 -82
  5. package/dist/zenuml.js.map +1 -0
  6. package/package.json +11 -1
  7. package/src/cli/zenuml.ts +1164 -0
  8. package/.agents/skills/babysit-pr/SKILL.md +0 -223
  9. package/.agents/skills/babysit-pr/agents/openai.yaml +0 -7
  10. package/.agents/skills/dia-scoring/SKILL.md +0 -139
  11. package/.agents/skills/dia-scoring/agents/openai.yaml +0 -7
  12. package/.agents/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  13. package/.agents/skills/land-pr/SKILL.md +0 -120
  14. package/.agents/skills/propagate-core-release/SKILL.md +0 -205
  15. package/.agents/skills/propagate-core-release/agents/openai.yaml +0 -7
  16. package/.agents/skills/propagate-core-release/references/downstreams.md +0 -42
  17. package/.agents/skills/ship-branch/SKILL.md +0 -105
  18. package/.agents/skills/submit-branch/SKILL.md +0 -76
  19. package/.agents/skills/validate-branch/SKILL.md +0 -72
  20. package/.claude/commands/README.md +0 -162
  21. package/.claude/commands/analyze.md +0 -101
  22. package/.claude/commands/clarify.md +0 -158
  23. package/.claude/commands/code-review.md +0 -322
  24. package/.claude/commands/constitution.md +0 -73
  25. package/.claude/commands/create-docs.md +0 -309
  26. package/.claude/commands/full-context.md +0 -121
  27. package/.claude/commands/gemini-consult.md +0 -164
  28. package/.claude/commands/handoff.md +0 -146
  29. package/.claude/commands/implement.md +0 -56
  30. package/.claude/commands/plan.md +0 -43
  31. package/.claude/commands/refactor.md +0 -188
  32. package/.claude/commands/specify.md +0 -21
  33. package/.claude/commands/tasks.md +0 -62
  34. package/.claude/commands/update-docs.md +0 -314
  35. package/.claude/hooks/README.md +0 -270
  36. package/.claude/hooks/config/sensitive-patterns.json +0 -86
  37. package/.claude/hooks/gemini-context-injector.sh +0 -129
  38. package/.claude/hooks/mcp-security-scan.sh +0 -147
  39. package/.claude/hooks/notify.sh +0 -103
  40. package/.claude/hooks/setup/hook-setup.md +0 -96
  41. package/.claude/hooks/setup/settings.json.template +0 -63
  42. package/.claude/hooks/sounds/complete.wav +0 -0
  43. package/.claude/hooks/sounds/input-needed.wav +0 -0
  44. package/.claude/hooks/subagent-context-injector.sh +0 -65
  45. package/.claude/skills/babysit-pr/SKILL.md +0 -223
  46. package/.claude/skills/babysit-pr/agents/openai.yaml +0 -7
  47. package/.claude/skills/dia-scoring/SKILL.md +0 -139
  48. package/.claude/skills/dia-scoring/agents/openai.yaml +0 -7
  49. package/.claude/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  50. package/.claude/skills/emoji-eval/SKILL.md +0 -187
  51. package/.claude/skills/land-pr/SKILL.md +0 -120
  52. package/.claude/skills/propagate-core-release/SKILL.md +0 -205
  53. package/.claude/skills/propagate-core-release/agents/openai.yaml +0 -7
  54. package/.claude/skills/propagate-core-release/references/downstreams.md +0 -42
  55. package/.claude/skills/ship-branch/SKILL.md +0 -105
  56. package/.claude/skills/submit-branch/SKILL.md +0 -76
  57. package/.claude/skills/validate-branch/SKILL.md +0 -72
  58. package/.claude/skills/zenuml-ux-research/SKILL.md +0 -183
  59. package/.claude/skills/zenuml-ux-research/references/assertion-catalog.md +0 -261
  60. package/.claude/skills/zenuml-ux-research/references/best-practices-overview.md +0 -56
  61. package/.claude/skills/zenuml-ux-research/references/report-template.md +0 -89
  62. package/.claude/skills/zenuml-ux-research/references/scenarios/edit-message-label.md +0 -37
  63. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-message.md +0 -36
  64. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-participant.md +0 -31
  65. package/.claude/skills/zenuml-ux-research/references/scenarios/rename-participant.md +0 -33
  66. package/.claude/skills/zenuml-ux-research/references/scenarios/undo-insert.md +0 -35
  67. package/.devcontainer/devcontainer.json +0 -21
  68. package/.dockerignore +0 -19
  69. package/.eslintrc.js +0 -39
  70. package/.git-blame-ignore-revs +0 -6
  71. package/.kiro/hooks/README.md +0 -38
  72. package/.kiro/hooks/session-sound-notification.js +0 -44
  73. package/.kiro/hooks/session-sound-notification.json +0 -23
  74. package/.mcp.json.example +0 -17
  75. package/.nvmrc +0 -1
  76. package/.prettierignore +0 -4
  77. package/.prettierrc +0 -1
  78. package/.specify/memory/constitution.md +0 -33
  79. package/.specify/scripts/bash/check-prerequisites.sh +0 -166
  80. package/.specify/scripts/bash/common.sh +0 -113
  81. package/.specify/scripts/bash/create-new-feature.sh +0 -97
  82. package/.specify/scripts/bash/setup-plan.sh +0 -60
  83. package/.specify/scripts/bash/update-agent-context.sh +0 -728
  84. package/.specify/templates/agent-file-template.md +0 -23
  85. package/.specify/templates/plan-template.md +0 -219
  86. package/.specify/templates/spec-template.md +0 -116
  87. package/.specify/templates/tasks-template.md +0 -127
  88. package/.storybook/main.ts +0 -25
  89. package/.storybook/preview.ts +0 -29
  90. package/.watchmanconfig +0 -3
  91. package/AGENTS.md +0 -26
  92. package/CLAUDE.md +0 -124
  93. package/DEPLOYMENT.md +0 -62
  94. package/Dockerfile +0 -36
  95. package/IMPLEMENTATION_PLAN.md +0 -163
  96. package/Integration/vanilla-js/index.html +0 -42
  97. package/MCP-ASSISTANT-RULES.md +0 -85
  98. package/README_CN.md +0 -15
  99. package/TUTORIAL.md +0 -116
  100. package/antlr/antlr-4.11.1-complete.jar +0 -0
  101. package/bun.lock +0 -1544
  102. package/bunfig.toml +0 -52
  103. package/docs/UNICODE_SUPPORT.md +0 -179
  104. package/docs/ai-context/deployment-infrastructure.md +0 -21
  105. package/docs/ai-context/docs-overview.md +0 -89
  106. package/docs/ai-context/handoff.md +0 -174
  107. package/docs/ai-context/project-structure.md +0 -160
  108. package/docs/ai-context/system-integration.md +0 -21
  109. package/docs/asciidoc/contributor.adoc +0 -54
  110. package/docs/asciidoc/create-my-own-theme.adoc +0 -149
  111. package/docs/asciidoc/images/creation-component.png +0 -0
  112. package/docs/asciidoc/images/creation-rtl.png +0 -0
  113. package/docs/asciidoc/images/message-arrow-rtl.png +0 -0
  114. package/docs/asciidoc/images/occurrence.png +0 -0
  115. package/docs/asciidoc/images/return-message-conflict.png +0 -0
  116. package/docs/asciidoc/images/shift-up-half-the-height.png +0 -0
  117. package/docs/asciidoc/images/three-layer-info-arch.png +0 -0
  118. package/docs/asciidoc/images/vertical-alignment.svg +0 -1
  119. package/docs/asciidoc/images/vertically-aligning.png +0 -0
  120. package/docs/asciidoc/index.adoc +0 -277
  121. package/docs/asciidoc/theme-debug-web-app.png +0 -0
  122. package/docs/asciidoc/tutorial.adoc +0 -22
  123. package/docs/asciidoc/user-css.png +0 -0
  124. package/docs/async-vs-sync-parser-rules.md +0 -81
  125. package/docs/divider-parser-allow-spaces.md +0 -38
  126. package/docs/highlighting-messages.md +0 -52
  127. package/docs/images/editor-sample.png +0 -0
  128. package/docs/inherited-vs-provided-from.md +0 -64
  129. package/docs/parser/Assignment.md +0 -8
  130. package/docs/parser/PARSER_IMPROVEMENTS_CC.md +0 -425
  131. package/docs/parser/grammar_review_gemini.md +0 -116
  132. package/docs/participants-function.md +0 -25
  133. package/docs/responsive-participant-margin.md +0 -52
  134. package/docs/starter.md +0 -9
  135. package/docs/superpowers/plans/2026-03-27-e2e-test-reorg.md +0 -698
  136. package/docs/superpowers/plans/2026-03-30-emoji-support.md +0 -1220
  137. package/docs/superpowers/plans/2026-03-30-self-correcting-scoring.md +0 -206
  138. package/docs/superpowers/plans/2026-04-15-keyboard-editing-on-diagram.md +0 -1992
  139. package/docs/superpowers/plans/2026-04-15-zenuml-ux-research-skill.md +0 -1452
  140. package/docs/ux-research/.gitkeep +0 -0
  141. package/docs/ux-research/2026-04-15-rename-participant.md +0 -156
  142. package/docs/ux-research/2026-04-18-insert-participant.md +0 -151
  143. package/docs/width-translate-and-offsets.md +0 -62
  144. package/docs/xss.md +0 -59
  145. package/e2e/data/compare-cases.js +0 -1090
  146. package/e2e/data/diff-algorithm.js +0 -199
  147. package/e2e/fixtures/create-message.html +0 -26
  148. package/e2e/fixtures/editable-label.html +0 -35
  149. package/e2e/fixtures/editable-span.html +0 -122
  150. package/e2e/fixtures/empty-diagram.html +0 -23
  151. package/e2e/fixtures/fixture.html +0 -31
  152. package/e2e/fixtures/insert-participant.html +0 -23
  153. package/e2e/fixtures/reorder-cross-fragment.html +0 -31
  154. package/e2e/fixtures/reorder-fragment.html +0 -29
  155. package/e2e/fixtures/reorder-message.html +0 -27
  156. package/e2e/fixtures/svg-test.html +0 -21
  157. package/e2e/fixtures/type-switch.html +0 -29
  158. package/e2e/tools/canonical-history.html +0 -908
  159. package/e2e/tools/compare-case.html +0 -371
  160. package/e2e/tools/compare.html +0 -35
  161. package/e2e/tools/native-diff-ext/background.js +0 -60
  162. package/e2e/tools/native-diff-ext/bridge.js +0 -26
  163. package/e2e/tools/native-diff-ext/content.js +0 -194
  164. package/e2e/tools/svg-preview.html +0 -56
  165. package/embed.html +0 -193
  166. package/eslint.config.mjs +0 -35
  167. package/firebase-debug.log +0 -108
  168. package/iframe-container-demo/diagram.html +0 -124
  169. package/iframe-container-demo/host.html +0 -817
  170. package/index.html +0 -771
  171. package/mermaid-zenuml-async-spa-auth.png +0 -0
  172. package/mermaid-zenuml-async-spa-auth.snapshot.md +0 -96
  173. package/newsletter/unicode-support-announcement.md +0 -134
  174. package/playground/creation.html +0 -53
  175. package/playground/message.html +0 -63
  176. package/playwright.config.ts +0 -40
  177. package/renderer.html +0 -366
  178. package/scripts/analyze-compare-case/collect-data.mjs +0 -1134
  179. package/scripts/analyze-compare-case/config.mjs +0 -102
  180. package/scripts/analyze-compare-case/geometry.mjs +0 -101
  181. package/scripts/analyze-compare-case/native-diff.mjs +0 -224
  182. package/scripts/analyze-compare-case/output.mjs +0 -74
  183. package/scripts/analyze-compare-case/panel-diff.mjs +0 -114
  184. package/scripts/analyze-compare-case/report.mjs +0 -162
  185. package/scripts/analyze-compare-case/residual-scopes.mjs +0 -347
  186. package/scripts/analyze-compare-case/scoring.mjs +0 -829
  187. package/scripts/analyze-compare-case.mjs +0 -149
  188. package/scripts/bump-version.js +0 -117
  189. package/scripts/snapshot-dual.js +0 -173
  190. package/scripts/update-snapshots.js +0 -70
  191. package/skills/dia-scoring/SKILL.md +0 -129
  192. package/skills/dia-scoring/agents/openai.yaml +0 -7
  193. package/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  194. package/tailwind.config.js +0 -126
  195. package/test-compression.html +0 -274
  196. package/test-mermaid-zenuml.html +0 -57
  197. package/test-setup.ts +0 -124
  198. package/test-url-params.html +0 -192
  199. package/tsconfig.app.json +0 -31
  200. package/tsconfig.node.json +0 -24
  201. package/tsconfig.test.json +0 -9
  202. package/vite.config.lib.ts +0 -93
  203. package/vite.config.ts +0 -84
  204. package/wrangler.toml +0 -18
@@ -1,206 +0,0 @@
1
- # Self-Correcting Dia-Scoring Implementation Plan
2
-
3
- > **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.
4
-
5
- **Goal:** Add a gap-detection and self-correction validation loop to the dia-scoring skill so it automatically detects and fixes analyzer blind spots during normal scoring runs.
6
-
7
- **Architecture:** The entire change is a new section in `SKILL.md` — behavioral instructions that tell the agent to cross-reference the diff panel's pixel clusters against the analyzer's coverage map, triage unaccounted clusters, and fix `collect-data.mjs` when the gap is a selector/extraction miss. No new scripts or tooling.
8
-
9
- **Tech Stack:** Markdown (SKILL.md), Playwright browser_evaluate (runtime), collect-data.mjs (runtime fixes)
10
-
11
- ---
12
-
13
- ### Task 1: Add "Gap Detection and Self-Correction" section to SKILL.md
14
-
15
- **Files:**
16
- - Modify: `/Users/pengxiao/.claude/skills/dia-scoring/SKILL.md` (append new section after the existing "Known Analyzer Internals" section, before "Commands")
17
-
18
- - [ ] **Step 1: Add the new section**
19
-
20
- Open `/Users/pengxiao/.claude/skills/dia-scoring/SKILL.md` and insert the following section between the "Known Analyzer Internals" section (line 167) and the "Commands" section (line 175):
21
-
22
- ```markdown
23
- ## Gap Detection and Self-Correction
24
-
25
- After running the analyzer and producing the JSON report, **automatically validate** that the report covers all visible diff clusters. Do not wait for the user to request calibration.
26
-
27
- ### Step 1: Build a Coverage Map
28
-
29
- Collect every fine-grained bounding box from the analyzer JSON:
30
- - `html_box` / `svg_box` from labels, numbers, arrows
31
- - `html_icon_box` / `svg_icon_box` from participant icons
32
- - `label_box` from participant labels
33
- - `stereotype_box`, `comment_box`, `divider_box`, occurrence boxes, etc.
34
-
35
- Use the most specific boxes available (e.g., `icon_box` rather than the coarse `participant_box`) to avoid masking sub-element gaps.
36
-
37
- ### Step 2: Find Unaccounted Diff Clusters
38
-
39
- Scan the `#diff-panel canvas` for connected clusters of red (HTML-only) or blue (SVG-only) pixels. Filter noise (clusters < 20 pixels). A cluster is **covered** when:
40
-
41
- - The cluster's centroid falls inside a reported element's bounding box, OR
42
- - The overlap between the cluster and a reported element's box is >= 30% of the cluster's area
43
-
44
- Clusters meeting neither condition are **unaccounted** and trigger investigation.
45
-
46
- `colorDiff` (purple) pixels within a covered region are expected and do not trigger investigation.
47
-
48
- ### Step 3: Verify Coordinate Mapping
49
-
50
- Before inspecting the DOM at gap coordinates:
51
-
52
- 1. Derive an initial canvas-to-page mapping from frame/canvas geometry (canvas natural size / frame CSS size).
53
- 2. Probe a known anchor — pick a reported element with known page coordinates and verify the mapping lands on it via `document.elementFromPoint`.
54
- 3. If the probe hits the wrong panel, empty space, or an unrelated element, recalibrate once using the probe result.
55
- 4. If the mapping still fails after one recalibration, mark the cluster as `uncertain` and move on.
56
-
57
- ### Step 4: Inspect DOM at Gap Coordinates
58
-
59
- For each unaccounted cluster with a verified mapping:
60
-
61
- 1. Use `document.elementFromPoint(x, y)` on both the HTML and SVG panels.
62
- 2. Walk up to the semantic parent (participant, message, fragment) to understand the element's role.
63
- 3. Classify: emoji icon, stereotype, arrow, label, or novel element.
64
-
65
- ### Step 5: Triage
66
-
67
- Classify each gap before acting:
68
-
69
- - **`likely_analyzer_gap`** — Element exists on both sides, belongs to an existing scoring category (icons, labels, stereotypes, etc.), but the collection logic missed it. Proceed to fix.
70
- - **`likely_renderer_residual`** — Element exists on only one side, or the difference is a genuine rendering discrepancy. Report in scoring output but do not modify the analyzer.
71
- - **`uncertain`** — Cannot determine cause confidently. Report with DOM context and coordinates for manual review.
72
-
73
- Only `likely_analyzer_gap` triggers a self-fix.
74
-
75
- ### Step 6: Fix the Collection Logic
76
-
77
- For `likely_analyzer_gap` clusters:
78
-
79
- 1. Read the relevant collection function in `collect-data.mjs` (e.g., `collectHtmlParticipants` for participant sub-elements).
80
- 2. Compare the function's selectors and extraction logic against the actual DOM element's tag, classes, and attributes.
81
- 3. Identify why it wasn't matched.
82
- 4. Fix the collection logic. This may include: adding selector patterns, adding fallback extraction paths, adjusting pairing logic, or modifying measurement paths. Keep changes targeted — no broad refactors.
83
-
84
- This fixes the measurement tool, not the renderers.
85
-
86
- ### Step 7: Re-run and Verify
87
-
88
- 1. Re-run the analyzer on the target case. Confirm the previously-unaccounted cluster is now covered and semantically correct.
89
- 2. Run 1-2 sibling cases with the same element family and confirm: populated data, no regression in previously-working sections.
90
-
91
- ### Safety Limits
92
-
93
- - Maximum **2 fix-and-rerun iterations** per scoring session.
94
- - Only auto-fix `likely_analyzer_gap` that maps to an existing scoring category and collection function.
95
- - Novel element types (no existing category): report as unresolved with element identity and coordinates.
96
-
97
- ### Limitations
98
-
99
- - **Invisible diffs**: If an element renders identically in both HTML and SVG (no diff pixels), this loop cannot detect that the analyzer doesn't cover it. The loop is reactive to visible differences only.
100
- - **Novel categories**: The loop can detect and report novel element types but does not create new scoring categories autonomously.
101
- ```
102
-
103
- - [ ] **Step 2: Update the participant icons scope line**
104
-
105
- In the same file, find line 74:
106
-
107
- ```markdown
108
- - participant icons (actor, database, ec2, lambda, azurefunction, sqs, sns, iam, boundary, control, entity)
109
- ```
110
-
111
- Replace with:
112
-
113
- ```markdown
114
- - participant icons (actor, database, ec2, lambda, azurefunction, sqs, sns, iam, boundary, control, entity, and emoji-based icons like 🌐, 🔒, 🗄️)
115
- ```
116
-
117
- This documents that emoji icons are in scope, which the self-correction loop discovered.
118
-
119
- - [ ] **Step 3: Verify the edit is well-formed**
120
-
121
- Read the modified SKILL.md and confirm:
122
- - The new section appears between "Known Analyzer Internals" and "Commands"
123
- - No existing sections were accidentally modified
124
- - The markdown renders correctly (no broken formatting)
125
-
126
- Run: `cat /Users/pengxiao/.claude/skills/dia-scoring/SKILL.md | head -250`
127
- Expected: The new "Gap Detection and Self-Correction" section is visible, followed by the existing "Commands" section.
128
-
129
- - [ ] **Step 4: Commit**
130
-
131
- ```bash
132
- cd /Users/pengxiao/workspaces/zenuml/mmd-zenuml-core
133
- git add /Users/pengxiao/.claude/skills/dia-scoring/SKILL.md
134
- git commit -m "feat(dia-scoring): add gap detection and self-correction validation loop"
135
- ```
136
-
137
- ---
138
-
139
- ### Task 2: Validate the self-correction loop on the emoji-async-return case
140
-
141
- This task verifies the new skill instructions work end-to-end by running a scoring session on the case that originally exposed the gap.
142
-
143
- **Files:**
144
- - No files created or modified — this is a validation task
145
-
146
- - [ ] **Step 1: Run the analyzer on emoji-async-return**
147
-
148
- ```bash
149
- cd /Users/pengxiao/workspaces/zenuml/mmd-zenuml-core
150
- node scripts/analyze-compare-case.mjs --case emoji-async-return --json 2>&1 | python3 -c "
151
- import json, sys
152
- data = json.load(sys.stdin)
153
- icons = data.get('participant_icons', [])
154
- print(f'participant_icons count: {len(icons)}')
155
- for icon in icons:
156
- print(f' {icon.get(\"name\", \"?\")} — presence html:{icon.get(\"presence\",{}).get(\"html\")} svg:{icon.get(\"presence\",{}).get(\"svg\")} status:{icon.get(\"status\")}')
157
- "
158
- ```
159
-
160
- Expected: `participant_icons count: 0` (the analyzer doesn't detect emoji icons yet — this confirms the gap exists before the self-correction loop runs).
161
-
162
- - [ ] **Step 2: Navigate to the compare-case page and read the diff panel**
163
-
164
- Using Playwright:
165
- 1. Navigate to `http://localhost:8080/e2e/tools/compare-case.html?case=emoji-async-return`
166
- 2. Wait for `[native-diff-ext] Done!` in console
167
- 3. Read the `#diff-panel canvas` pixel data via `browser_evaluate`
168
- 4. Identify unaccounted clusters in the participant icon region
169
-
170
- Expected: The diff panel shows HTML-only and SVG-only pixel clusters around the emoji icons (🌐, 🔒, 🗄️) that are not covered by any reported element in the analyzer output.
171
-
172
- - [ ] **Step 3: Follow the self-correction loop**
173
-
174
- Execute steps 1-7 of the new "Gap Detection and Self-Correction" section:
175
- 1. Build coverage map from analyzer JSON
176
- 2. Find unaccounted clusters (the icon regions)
177
- 3. Verify coordinate mapping with a known anchor probe
178
- 4. Inspect DOM at gap coordinates — find `span.mr-1.flex-shrink-0` (HTML) and emoji `tspan` (SVG)
179
- 5. Triage as `likely_analyzer_gap` (element exists on both sides, belongs to participant icons category)
180
- 6. Fix `collect-data.mjs` — add emoji icon detection to both `collectHtmlParticipants` and `collectSvgParticipants`
181
- 7. Re-run analyzer, verify icons are now detected. Run 1-2 sibling emoji cases.
182
-
183
- Expected: After the fix, `participant_icons count: 3` with all three emoji icons detected and scored.
184
-
185
- - [ ] **Step 4: Commit the collect-data.mjs fix**
186
-
187
- ```bash
188
- cd /Users/pengxiao/workspaces/zenuml/mmd-zenuml-core
189
- git add scripts/analyze-compare-case/collect-data.mjs
190
- git commit -m "fix(analyzer): detect emoji-based participant icons in collect-data"
191
- ```
192
-
193
- ---
194
-
195
- ### Task 3: Commit the design spec
196
-
197
- **Files:**
198
- - Stage: `docs/superpowers/specs/2026-03-30-self-correcting-scoring-design.md`
199
-
200
- - [ ] **Step 1: Commit the spec**
201
-
202
- ```bash
203
- cd /Users/pengxiao/workspaces/zenuml/mmd-zenuml-core
204
- git add docs/superpowers/specs/2026-03-30-self-correcting-scoring-design.md
205
- git commit -m "docs: add self-correcting dia-scoring design spec"
206
- ```