@qijenchen/design-system 0.1.0-beta.10 → 0.1.0-beta.13

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 (307) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +7 -15
  3. package/cli-init.mjs +90 -0
  4. package/ds-canonical/commands/README.md +26 -0
  5. package/ds-canonical/commands/gov-status.md +79 -0
  6. package/ds-canonical/hooks/README.md +145 -0
  7. package/ds-canonical/hooks/_log-fire.sh +44 -0
  8. package/ds-canonical/hooks/block_prototype_imports.py +111 -0
  9. package/ds-canonical/hooks/check_app_shell_primary_header_consistency.sh +68 -0
  10. package/ds-canonical/hooks/check_audit_post_report_validator.sh +88 -0
  11. package/ds-canonical/hooks/check_audit_sample_escape.sh +73 -0
  12. package/ds-canonical/hooks/check_benchmark_citation.sh +106 -0
  13. package/ds-canonical/hooks/check_canonical_propagation.sh +189 -0
  14. package/ds-canonical/hooks/check_chrome_header_handcraft.sh +70 -0
  15. package/ds-canonical/hooks/check_codex_brief_invariants.sh +83 -0
  16. package/ds-canonical/hooks/check_codex_collab_5step.sh +108 -0
  17. package/ds-canonical/hooks/check_datatable_invariants.sh +117 -0
  18. package/ds-canonical/hooks/check_dim_count_drift.sh +72 -0
  19. package/ds-canonical/hooks/check_field_controls_contracts.sh +110 -0
  20. package/ds-canonical/hooks/check_field_family_invariants.sh +205 -0
  21. package/ds-canonical/hooks/check_file_size_budget.sh +60 -0
  22. package/ds-canonical/hooks/check_header_with_tabs_border.sh +87 -0
  23. package/ds-canonical/hooks/check_main_branch_workbench.sh +93 -0
  24. package/ds-canonical/hooks/check_naming_and_abstraction.sh +165 -0
  25. package/ds-canonical/hooks/check_opacity_token_usage.sh +149 -0
  26. package/ds-canonical/hooks/check_pattern_invariants.sh +194 -0
  27. package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +56 -0
  28. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +53 -0
  29. package/ds-canonical/hooks/check_propose_plain_chinese.sh +74 -0
  30. package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +70 -0
  31. package/ds-canonical/hooks/check_select_all_canonical.sh +58 -0
  32. package/ds-canonical/hooks/check_solo_workflow.sh +258 -0
  33. package/ds-canonical/hooks/check_spec_class_drift.sh +88 -0
  34. package/ds-canonical/hooks/check_story_invariants.sh +612 -0
  35. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +105 -0
  36. package/ds-canonical/hooks/check_tab_lg_chrome_header_equal.sh +66 -0
  37. package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +104 -0
  38. package/ds-canonical/hooks/enforce_home_charter.sh +44 -0
  39. package/ds-canonical/hooks/inject_pending_self_audit.sh +204 -0
  40. package/ds-canonical/hooks/lib/_approval_re.sh +33 -0
  41. package/ds-canonical/hooks/lib/_code_quality.sh +73 -0
  42. package/ds-canonical/hooks/lib/_cva_default_sync.sh +69 -0
  43. package/ds-canonical/hooks/lib/_governance_coverage_check.sh +49 -0
  44. package/ds-canonical/hooks/lib/_hardcoded_strings.sh +163 -0
  45. package/ds-canonical/hooks/lib/_layout_space_canonical.sh +56 -0
  46. package/ds-canonical/hooks/lib/_overlay_handcraft.sh +141 -0
  47. package/ds-canonical/hooks/lib/_person_data_richness.sh +42 -0
  48. package/ds-canonical/hooks/lib/_story_compile_drift.sh +48 -0
  49. package/ds-canonical/hooks/lib/_token_hygiene.sh +95 -0
  50. package/ds-canonical/hooks/log_governance_fires.sh +50 -0
  51. package/ds-canonical/hooks/log_skill_invokes.sh +41 -0
  52. package/ds-canonical/hooks/post_edit_dispatcher.sh +62 -0
  53. package/ds-canonical/hooks/retired/check_anatomy_section_numbering.sh +106 -0
  54. package/ds-canonical/hooks/retired/check_avatar_hovercard.sh +90 -0
  55. package/ds-canonical/hooks/retired/check_button_icon_literal.sh.retired-2026-04-28 +38 -0
  56. package/ds-canonical/hooks/retired/check_container_breathing.sh +142 -0
  57. package/ds-canonical/hooks/retired/check_governance_compliance.sh +61 -0
  58. package/ds-canonical/hooks/retired/check_icon_only_padding_formula.sh +104 -0
  59. package/ds-canonical/hooks/retired/check_item_content_primitive.sh +150 -0
  60. package/ds-canonical/hooks/retired/check_item_list_gap.sh +153 -0
  61. package/ds-canonical/hooks/retired/check_sideoffset_canonical.sh +65 -0
  62. package/ds-canonical/hooks/retired/check_spec_iteration_tag.sh +87 -0
  63. package/ds-canonical/hooks/retired/check_ssot_consultation.sh +88 -0
  64. package/ds-canonical/hooks/retired/check_sync_update.sh +20 -0
  65. package/ds-canonical/hooks/retired/check_third_party_dom_verified.sh +95 -0
  66. package/ds-canonical/hooks/retired/enforce_home_charter.sh +125 -0
  67. package/ds-canonical/hooks/retired/post_edit_canonical_interrogate.sh +109 -0
  68. package/ds-canonical/hooks/retired/pre_edit_spec_check.sh +68 -0
  69. package/ds-canonical/hooks/retired/pre_new_component_spec.sh +39 -0
  70. package/ds-canonical/hooks/retired/pre_write_subsumption_check.sh +112 -0
  71. package/ds-canonical/hooks/retired/stop_meta_self_audit.sh.retired-2026-05-13 +76 -0
  72. package/ds-canonical/hooks/retired/tests/test_check_anatomy_section_numbering.sh +14 -0
  73. package/ds-canonical/hooks/retired/tests/test_check_avatar_hovercard.sh +15 -0
  74. package/ds-canonical/hooks/retired/tests/test_check_container_breathing.sh +15 -0
  75. package/ds-canonical/hooks/retired/tests/test_check_governance_compliance.sh +15 -0
  76. package/ds-canonical/hooks/retired/tests/test_check_icon_only_padding_formula.sh +79 -0
  77. package/ds-canonical/hooks/retired/tests/test_check_item_content_primitive.sh +15 -0
  78. package/ds-canonical/hooks/retired/tests/test_check_item_list_gap.sh +163 -0
  79. package/ds-canonical/hooks/retired/tests/test_check_sideoffset_canonical.sh +15 -0
  80. package/ds-canonical/hooks/retired/tests/test_check_spec_iteration_tag.sh +15 -0
  81. package/ds-canonical/hooks/retired/tests/test_check_ssot_consultation.sh +15 -0
  82. package/ds-canonical/hooks/retired/tests/test_check_sync_update.sh +14 -0
  83. package/ds-canonical/hooks/retired/tests/test_check_third_party_dom_verified.sh +15 -0
  84. package/ds-canonical/hooks/retired/tests/test_enforce_home_charter.sh +15 -0
  85. package/ds-canonical/hooks/retired/tests/test_pre_edit_spec_check.sh +15 -0
  86. package/ds-canonical/hooks/retired/tests/test_pre_new_component_spec.sh +15 -0
  87. package/ds-canonical/hooks/retired/tests/test_pre_write_subsumption_check.sh +63 -0
  88. package/ds-canonical/hooks/session_start_governance_check.sh +263 -0
  89. package/ds-canonical/hooks/stop_passive_logging.sh +322 -0
  90. package/ds-canonical/hooks/stop_self_audit.sh +450 -0
  91. package/ds-canonical/hooks/tests/KNOWN-BROKEN.md +15 -0
  92. package/ds-canonical/hooks/tests/run-all.sh +76 -0
  93. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +143 -0
  94. package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +140 -0
  95. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +115 -0
  96. package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +93 -0
  97. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +115 -0
  98. package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +133 -0
  99. package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +123 -0
  100. package/ds-canonical/hooks/tests/test_check_code_quality.sh +15 -0
  101. package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +96 -0
  102. package/ds-canonical/hooks/tests/test_check_cva_default_sync.sh +15 -0
  103. package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +122 -0
  104. package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +98 -0
  105. package/ds-canonical/hooks/tests/test_check_field_controls_contracts.sh +126 -0
  106. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +194 -0
  107. package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +32 -0
  108. package/ds-canonical/hooks/tests/test_check_hardcoded_strings.sh +14 -0
  109. package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +110 -0
  110. package/ds-canonical/hooks/tests/test_check_layout_space_canonical.sh +73 -0
  111. package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +147 -0
  112. package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +136 -0
  113. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +110 -0
  114. package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +126 -0
  115. package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +148 -0
  116. package/ds-canonical/hooks/tests/test_check_peoplepicker_ssot_drift.sh +108 -0
  117. package/ds-canonical/hooks/tests/test_check_person_data_richness.sh +58 -0
  118. package/ds-canonical/hooks/tests/test_check_pixel_quantified_audit.sh +142 -0
  119. package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +126 -0
  120. package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +117 -0
  121. package/ds-canonical/hooks/tests/test_check_select_all_canonical.sh +125 -0
  122. package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +201 -0
  123. package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +135 -0
  124. package/ds-canonical/hooks/tests/test_check_story_anatomy.sh.broken +197 -0
  125. package/ds-canonical/hooks/tests/test_check_story_category.sh.broken +187 -0
  126. package/ds-canonical/hooks/tests/test_check_story_compile_drift.sh +15 -0
  127. package/ds-canonical/hooks/tests/test_check_story_invariants.sh +209 -0
  128. package/ds-canonical/hooks/tests/test_check_story_name_jargon.sh.broken +53 -0
  129. package/ds-canonical/hooks/tests/test_check_story_slot_split.sh +156 -0
  130. package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +176 -0
  131. package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +138 -0
  132. package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +21 -0
  133. package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +169 -0
  134. package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +77 -0
  135. package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +125 -0
  136. package/ds-canonical/hooks/tests/test_log_governance_fires.sh +10 -0
  137. package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +7 -0
  138. package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +108 -0
  139. package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +143 -0
  140. package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +95 -0
  141. package/ds-canonical/hooks/tests/test_stop_governance_drift_check.sh.broken +125 -0
  142. package/ds-canonical/hooks/tests/test_stop_harvest_corrections.sh +10 -0
  143. package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +100 -0
  144. package/ds-canonical/hooks/tests/test_stop_self_audit.sh +76 -0
  145. package/ds-canonical/hooks/tests/test_stop_tsc_sanity.sh +10 -0
  146. package/ds-canonical/references/README.md +43 -0
  147. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +74 -0
  148. package/ds-canonical/references/build-ui-canonicals.md +69 -0
  149. package/ds-canonical/references/cva-patterns.md +41 -0
  150. package/ds-canonical/references/drag-canonical.md +331 -0
  151. package/ds-canonical/references/item-anatomy-recipe.md +225 -0
  152. package/ds-canonical/references/naming-conventions.md +56 -0
  153. package/ds-canonical/references/principle-dim-map.json +515 -0
  154. package/ds-canonical/references/props-naming.md +45 -0
  155. package/ds-canonical/references/spec-rules.md +58 -0
  156. package/ds-canonical/references/ssot-consultation.md +63 -0
  157. package/ds-canonical/references/ssot-index.md +40 -0
  158. package/ds-canonical/references/story-baseline-registry.json +79 -0
  159. package/ds-canonical/references/structural-token-retention.md +42 -0
  160. package/ds-canonical/references/tailwind-gotchas.md +87 -0
  161. package/ds-canonical/references/ui-dev-rules.md +60 -0
  162. package/ds-canonical/rules/README.md +34 -0
  163. package/ds-canonical/rules/meta-patterns.md +87 -0
  164. package/ds-canonical/rules/self-verify.md +53 -0
  165. package/ds-canonical/rules/spec-rules.md +25 -0
  166. package/ds-canonical/rules/story-rules.md +56 -0
  167. package/ds-canonical/rules/ui-development.md +87 -0
  168. package/ds-canonical/skills/README.md +88 -0
  169. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +181 -0
  170. package/ds-canonical/skills/code-quality-audit/SKILL.md +63 -0
  171. package/ds-canonical/skills/codex-collab/SKILL.md +249 -0
  172. package/ds-canonical/skills/codex-collab/references/brief-template.md +48 -0
  173. package/ds-canonical/skills/codex-collab/references/transport.md +58 -0
  174. package/ds-canonical/skills/codify-corrections/SKILL.md +184 -0
  175. package/ds-canonical/skills/codify-principle/SKILL.md +151 -0
  176. package/ds-canonical/skills/component-quality-gate/SKILL.md +102 -0
  177. package/ds-canonical/skills/component-quality-gate/references/checklist.md +79 -0
  178. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +247 -0
  179. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +123 -0
  180. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +165 -0
  181. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +91 -0
  182. package/ds-canonical/skills/delivery-handoff/SKILL.md +229 -0
  183. package/ds-canonical/skills/delivery-handoff/references/flow-diagram.md +180 -0
  184. package/ds-canonical/skills/delivery-handoff/references/handoff-template.md +177 -0
  185. package/ds-canonical/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  186. package/ds-canonical/skills/design-system-audit/SKILL.md +343 -0
  187. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1260 -0
  188. package/ds-canonical/skills/design-system-audit/references/checkpoints.md +240 -0
  189. package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +240 -0
  190. package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +364 -0
  191. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +175 -0
  192. package/ds-canonical/skills/design-system-audit/references/spec-template.md +66 -0
  193. package/ds-canonical/skills/ensure-canonical/SKILL.md +196 -0
  194. package/ds-canonical/skills/governance-health/SKILL.md +146 -0
  195. package/ds-canonical/skills/knowledge-prune/SKILL.md +303 -0
  196. package/ds-canonical/skills/new-component/SKILL.md +170 -0
  197. package/ds-canonical/skills/new-component/references/new-component-checklist.md +85 -0
  198. package/ds-canonical/skills/performance-audit/SKILL.md +107 -0
  199. package/ds-canonical/skills/product-ui-audit/SKILL.md +230 -0
  200. package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +246 -0
  201. package/ds-canonical/skills/product-ui-audit/references/common-misuses.md +329 -0
  202. package/ds-canonical/skills/product-ui-audit/references/report-template.md +159 -0
  203. package/ds-canonical/skills/propose-options/SKILL.md +177 -0
  204. package/ds-canonical/skills/prototype/SKILL.md +244 -0
  205. package/ds-canonical/skills/prototype/references/audit-checks.md +37 -0
  206. package/ds-canonical/skills/prototype/references/benchmark-sources.md +94 -0
  207. package/ds-canonical/skills/prototype/references/checkpoints.md +191 -0
  208. package/ds-canonical/skills/prototype/references/evaluation-matrix.md +141 -0
  209. package/ds-canonical/skills/prototype/references/ooux-template.md +198 -0
  210. package/ds-canonical/skills/prototype/references/proposal-template.md +229 -0
  211. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +198 -0
  212. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +159 -0
  213. package/ds-canonical/skills/story-writing/SKILL.md +122 -0
  214. package/ds-canonical/skills/story-writing/references/anatomy-standard.md +217 -0
  215. package/ds-canonical/skills/story-writing/references/category-templates.md +174 -0
  216. package/ds-canonical/skills/story-writing/references/example-selection.md +70 -0
  217. package/ds-canonical/skills/story-writing/references/self-check.md +20 -0
  218. package/ds-canonical/skills/ux-audit/SKILL.md +130 -0
  219. package/ds-canonical/skills/visual-audit/SKILL.md +245 -0
  220. package/ds-canonical/skills/visual-audit/output/.gitkeep +0 -0
  221. package/ds-canonical/skills/visual-audit/references/audit-architecture.md +100 -0
  222. package/ds-canonical/skills/visual-audit/references/visual-checklist.md +297 -0
  223. package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  224. package/package.json +9 -5
  225. package/src/components/Accordion/accordion.spec.md +114 -0
  226. package/src/components/Alert/alert.spec.md +197 -0
  227. package/src/components/AppShell/app-shell.spec.md +331 -0
  228. package/src/components/AspectRatio/aspect-ratio.spec.md +134 -0
  229. package/src/components/Avatar/avatar.spec.md +329 -0
  230. package/src/components/Badge/badge.spec.md +380 -0
  231. package/src/components/Breadcrumb/breadcrumb.spec.md +257 -0
  232. package/src/components/BulkActionBar/bulk-action-bar.spec.md +210 -0
  233. package/src/components/Button/button.spec.md +460 -0
  234. package/src/components/Calendar/calendar.spec.md +242 -0
  235. package/src/components/Carousel/carousel.spec.md +253 -0
  236. package/src/components/Chart/chart.spec.md +155 -0
  237. package/src/components/Checkbox/checkbox.spec.md +344 -0
  238. package/src/components/Chip/chip.spec.md +237 -0
  239. package/src/components/CircularProgress/circular-progress.spec.md +268 -0
  240. package/src/components/Coachmark/coachmark.spec.md +230 -0
  241. package/src/components/Combobox/combobox.spec.md +180 -0
  242. package/src/components/Command/command.spec.md +171 -0
  243. package/src/components/DataTable/data-table.spec.md +525 -0
  244. package/src/components/DateGrid/date-grid.spec.md +215 -0
  245. package/src/components/DatePicker/date-picker.spec.md +334 -0
  246. package/src/components/DescriptionList/description-list.spec.md +214 -0
  247. package/src/components/Dialog/dialog.spec.md +202 -0
  248. package/src/components/DropdownMenu/dropdown-menu.spec.md +250 -0
  249. package/src/components/Empty/empty.spec.md +214 -0
  250. package/src/components/Field/field-controls.spec.md +338 -0
  251. package/src/components/Field/field.spec.md +438 -0
  252. package/src/components/Field/form-validation.spec.md +152 -0
  253. package/src/components/FieldControlGroup/field-control-group.spec.md +176 -0
  254. package/src/components/FileItem/file-item.spec.md +467 -0
  255. package/src/components/FileUpload/file-upload.spec.md +123 -0
  256. package/src/components/FileViewer/file-viewer.spec.md +373 -0
  257. package/src/components/HoverCard/hover-card.spec.md +157 -0
  258. package/src/components/Input/input.spec.md +193 -0
  259. package/src/components/LinkInput/link-input.spec.md +130 -0
  260. package/src/components/Menu/menu-item.spec.md +290 -0
  261. package/src/components/NameCard/name-card.spec.md +171 -0
  262. package/src/components/Notice/notice.spec.md +149 -0
  263. package/src/components/NumberInput/number-input.spec.md +126 -0
  264. package/src/components/OverflowIndicator/overflow-indicator.spec.md +120 -0
  265. package/src/components/PeoplePicker/people-picker.spec.md +263 -0
  266. package/src/components/Popover/popover.spec.md +198 -0
  267. package/src/components/ProgressBar/progress-bar.spec.md +232 -0
  268. package/src/components/RadioGroup/radio-group.spec.md +141 -0
  269. package/src/components/Rating/rating.spec.md +208 -0
  270. package/src/components/ScrollArea/scroll-area.spec.md +145 -0
  271. package/src/components/SegmentedControl/segmented-control.spec.md +295 -0
  272. package/src/components/Select/select.spec.md +299 -0
  273. package/src/components/SelectMenu/select-menu.spec.md +220 -0
  274. package/src/components/SelectionControl/selection-item.spec.md +128 -0
  275. package/src/components/Separator/separator.spec.md +109 -0
  276. package/src/components/Sheet/sheet.spec.md +148 -0
  277. package/src/components/Sidebar/sidebar.spec.md +713 -0
  278. package/src/components/Skeleton/skeleton.spec.md +104 -0
  279. package/src/components/Slider/slider.spec.md +353 -0
  280. package/src/components/Steps/steps.spec.md +465 -0
  281. package/src/components/Switch/switch.spec.md +215 -0
  282. package/src/components/Tabs/tabs.spec.md +314 -0
  283. package/src/components/Tag/tag.spec.md +282 -0
  284. package/src/components/Textarea/textarea.spec.md +151 -0
  285. package/src/components/TimePicker/time-picker.spec.md +279 -0
  286. package/src/components/Toast/toast.spec.md +177 -0
  287. package/src/components/Tooltip/tooltip.spec.md +139 -0
  288. package/src/components/TreeView/tree-view.spec.md +374 -0
  289. package/src/patterns/action-bar/action-bar.spec.md +458 -0
  290. package/src/patterns/element-anatomy/element-anatomy.spec.md +215 -0
  291. package/src/patterns/element-anatomy/inline-action.spec.md +315 -0
  292. package/src/patterns/element-anatomy/item-anatomy.spec.md +1042 -0
  293. package/src/patterns/header-canonical/header-canonical.spec.md +285 -0
  294. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +191 -0
  295. package/src/patterns/overlay-surface/overlay-surface.spec.md +428 -0
  296. package/src/patterns/resize-handle/resize-handle.spec.md +109 -0
  297. package/src/tokens/color/color.spec.md +804 -0
  298. package/src/tokens/density/density.spec.md +127 -0
  299. package/src/tokens/elevation/elevation.spec.md +81 -0
  300. package/src/tokens/layoutSpace/layoutSpace.spec.md +314 -0
  301. package/src/tokens/motion/motion.spec.md +97 -0
  302. package/src/tokens/opacity/opacity.spec.md +78 -0
  303. package/src/tokens/orphan-tokens.spec.md +117 -0
  304. package/src/tokens/radius/radius.spec.md +123 -0
  305. package/src/tokens/typography/typography.spec.md +202 -0
  306. package/src/tokens/uiSize/uiSize.spec.md +438 -0
  307. package/src/styles/preset.css +0 -31
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import re
5
+ import sys
6
+ import time
7
+
8
+ # Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
9
+ # Resolve project root from this script's location(stable; cwd may be anywhere
10
+ # depending on how Claude Code invokes the hook)to avoid stray .claude/ trees.
11
+ try:
12
+ _project_root = os.environ.get('CLAUDE_PROJECT_DIR') or os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
+ _log_dir = os.path.join(_project_root, '.claude', 'logs')
14
+ os.makedirs(_log_dir, exist_ok=True)
15
+ with open(os.path.join(_log_dir, 'hook-fires-per-hook.jsonl'), 'a') as _f:
16
+ _f.write(json.dumps({'ts': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'hook': os.path.basename(__file__)}) + '\n')
17
+ except Exception:
18
+ pass
19
+
20
+ EXPLORATION_PREFIX = "src/explorations/"
21
+ SRC_PREFIX = "src/"
22
+
23
+ def load_event():
24
+ try:
25
+ return json.load(sys.stdin)
26
+ except Exception:
27
+ return None
28
+
29
+ def collect_file_paths(tool_input):
30
+ paths = []
31
+
32
+ if not isinstance(tool_input, dict):
33
+ return paths
34
+
35
+ file_path = tool_input.get("file_path")
36
+ if isinstance(file_path, str):
37
+ paths.append(file_path)
38
+
39
+ edits = tool_input.get("edits")
40
+ if isinstance(edits, list):
41
+ for item in edits:
42
+ if isinstance(item, dict):
43
+ p = item.get("file_path")
44
+ if isinstance(p, str):
45
+ paths.append(p)
46
+
47
+ files = tool_input.get("files")
48
+ if isinstance(files, list):
49
+ for item in files:
50
+ if isinstance(item, dict):
51
+ p = item.get("file_path")
52
+ if isinstance(p, str):
53
+ paths.append(p)
54
+
55
+ return list(dict.fromkeys(paths))
56
+
57
+ def should_check(path):
58
+ if not path.startswith(SRC_PREFIX):
59
+ return False
60
+ if path.startswith(EXPLORATION_PREFIX):
61
+ return False
62
+ if not (path.endswith(".ts") or path.endswith(".tsx")):
63
+ return False
64
+ return True
65
+
66
+ def contains_exploration_import(content):
67
+ patterns = [
68
+ r'from\s+[\'"].*src/explorations/.*[\'"]',
69
+ r'from\s+[\'"].*explorations/.*[\'"]',
70
+ r'import\s+[\'"].*src/explorations/.*[\'"]',
71
+ r'import\s+[\'"].*explorations/.*[\'"]'
72
+ ]
73
+ return any(re.search(p, content) for p in patterns)
74
+
75
+ def main():
76
+ event = load_event()
77
+ if not event:
78
+ sys.exit(0)
79
+
80
+ tool_input = event.get("tool_input", {})
81
+ touched_paths = collect_file_paths(tool_input)
82
+
83
+ offenders = []
84
+
85
+ for path in touched_paths:
86
+ if not should_check(path):
87
+ continue
88
+ if not os.path.exists(path):
89
+ continue
90
+
91
+ try:
92
+ with open(path, "r", encoding="utf-8") as f:
93
+ content = f.read()
94
+ except Exception:
95
+ continue
96
+
97
+ if contains_exploration_import(content):
98
+ offenders.append(path)
99
+
100
+ if offenders:
101
+ lines = [
102
+ "Blocked: 正式程式碼不得 import src/explorations/ 內的檔案。"
103
+ ]
104
+ lines.extend(f"- {p}" for p in offenders)
105
+ print("\n".join(lines))
106
+ sys.exit(2)
107
+
108
+ sys.exit(0)
109
+
110
+ if __name__ == "__main__":
111
+ main()
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env bash
2
+ # check_app_shell_primary_header_consistency.sh — PreToolUse Edit/Write
3
+ #
4
+ # 2026-05-21 ship per user directive「該程式化的就程式化」+「確認當有 global header 時,
5
+ # sidebar 內的 header 應該要拿掉」+ world-class GitHub/Gmail/Figma 共識。
6
+ #
7
+ # Detects 2 violations in AppShell consumer code:
8
+ # V1) `layout="primary-header"` without `globalHeader=...` prop
9
+ # → 缺 globalHeader 而 layout=primary-header 是邏輯矛盾(per app-shell.spec.md
10
+ # 「primary-header = primary-sidebar + 一條 global header」)
11
+ #
12
+ # V2) `layout="primary-header"` + 任何 `<SidebarHeader>...</SidebarHeader>` 在同 file
13
+ # → WorkspaceBrand 已該在 globalHeader,sidebar 內不該再有 SidebarHeader
14
+ # (per app-shell.spec.md「WorkspaceBrand 放置 SSOT」+ world-class GitHub/Gmail/Figma 一致)
15
+ #
16
+ # 對齊 .claude/rules/self-verify.md「Pre-edit」階段 + check_chrome_header_handcraft.sh /
17
+ # check_overlay_handcraft.sh 等既有 SSOT-enforcement hook idiom。
18
+ # Exception escape:`// @app-shell-primary-header-allow: <reason>` 檔頭。
19
+
20
+ set -uo pipefail
21
+ source "$(dirname "$0")/_log-fire.sh"
22
+
23
+ # 只看 Edit / Write tool
24
+ TOOL_NAME=$(echo "${CLAUDE_TOOL_INPUT:-}" | python3 -c "import sys,json; d=json.load(sys.stdin) if sys.stdin.isatty()==False else {}; print(d.get('tool_name',''))" 2>/dev/null || echo "")
25
+ if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then exit 0; fi
26
+
27
+ TARGET=$(echo "${CLAUDE_TOOL_INPUT:-}" | python3 -c "import sys,json; d=json.load(sys.stdin) if sys.stdin.isatty()==False else {}; print(d.get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")
28
+ # 只查 .tsx / .stories.tsx consumer file
29
+ if [[ ! "$TARGET" =~ \.(tsx)$ ]]; then exit 0; fi
30
+ if [[ ! -f "$TARGET" ]]; then exit 0; fi
31
+
32
+ # 排除 spec / test / SSOT 檔
33
+ case "$TARGET" in
34
+ *.spec.md|*test*|*/AppShell/app-shell.tsx) exit 0 ;;
35
+ esac
36
+
37
+ # Escape allowlist
38
+ if grep -q "@app-shell-primary-header-allow:" "$TARGET"; then exit 0; fi
39
+
40
+ # 偵測 layout="primary-header"
41
+ if ! grep -q 'layout="primary-header"\|layout={["\047]primary-header["\047]}' "$TARGET"; then exit 0; fi
42
+
43
+ VIOLATIONS=()
44
+
45
+ # V1:layout="primary-header" 但無 globalHeader=
46
+ if ! grep -q 'globalHeader\s*=' "$TARGET"; then
47
+ VIOLATIONS+=("V1 缺 globalHeader prop:layout=\"primary-header\" 必傳 globalHeader 否則邏輯矛盾(per app-shell.spec.md「primary-header = primary-sidebar + 一條 global header」)")
48
+ fi
49
+
50
+ # V2:layout="primary-header" + <SidebarHeader> 同 file → WorkspaceBrand 該在 globalHeader 不重複
51
+ if grep -q '<SidebarHeader' "$TARGET"; then
52
+ VIOLATIONS+=("V2 Sidebar 內含 SidebarHeader:primary-header mode WorkspaceBrand 該在 globalHeader,sidebar 內不該重複(per spec.md「WorkspaceBrand 放置 SSOT」+ world-class GitHub/Gmail/Figma 共識)。若 sidebar header 是其他內容(非 brand),加 escape allowlist `// @app-shell-primary-header-allow:` 並說明 reason")
53
+ fi
54
+
55
+ if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
56
+ echo "🚨 AppShell primary-header consistency violation" >&2
57
+ echo "Target: $TARGET" >&2
58
+ for v in "${VIOLATIONS[@]}"; do echo " • $v" >&2; done
59
+ echo "" >&2
60
+ echo "修法:" >&2
61
+ echo " (a) 傳 globalHeader prop / 撤掉 SidebarHeader" >&2
62
+ echo " (b) 改 layout=\"primary-sidebar\"(若不需要 global header)" >&2
63
+ echo " (c) Escape 允許:檔首加 \`// @app-shell-primary-header-allow: <rationale>\`" >&2
64
+ log_fire "check_app_shell_primary_header_consistency"
65
+ exit 2
66
+ fi
67
+
68
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/bin/bash
2
+ # PostToolUse hook: validate `/design-system-audit` final report quality.
3
+ # 2026-05-17 ship — codex Q4 verdict「post-audit stop hook / final report validator」最合理 trigger 位置。
4
+ #
5
+ # Triggers: 任何 Write/Edit 到 `.claude/logs/audit-report-*.json` OR `.claude/memory/project_audit_progress.md`
6
+ #
7
+ # 驗證:
8
+ # (a) NO-SAMPLE invariant — report 不含「sample top N / subset / pick top X」keyword
9
+ # (b) 46-dim full dispatch — report 應列 ≥ 46 dim coverage 紀錄(或明示 N/A 跳過理由)
10
+ # (c) audit-prompts.md coverage — 若 missing dim prompt → flag prune-chain-trigger
11
+ # (d) `@benchmark-unverified-blanket` count drift — vs last audit baseline
12
+ # (e) prune-chain-trigger signal → emit additionalContext 進下一 turn,inject_pending_self_audit 吸
13
+ #
14
+ # 對應 SKILL.md `/design-system-audit` Phase 4.5 機械化 trigger(2026-05-17 加)。
15
+
16
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
17
+
18
+ set -uo pipefail
19
+
20
+ INPUT=$(cat 2>/dev/null || echo "{}")
21
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
22
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null)
23
+
24
+ # Only fire on audit report writes
25
+ case "$FILE_PATH" in
26
+ */audit-report-*.json) ;;
27
+ */project_audit_progress.md) ;;
28
+ *) exit 0 ;;
29
+ esac
30
+
31
+ case "$TOOL" in Write|Edit|MultiEdit) ;; *) exit 0 ;; esac
32
+
33
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
34
+ [ -f "$FILE_PATH" ] || exit 0
35
+
36
+ WARNINGS=""
37
+ TRIGGER_PRUNE=0
38
+
39
+ # ─ Validator A: NO-SAMPLE ─────────────────────────────────────────────────
40
+ if grep -qE 'sample top [0-9]+|sampled top|subset|pick top [0-9]+|top hot|sampled components' "$FILE_PATH" 2>/dev/null; then
41
+ WARNINGS="${WARNINGS}\n ❌ [A] NO-SAMPLE violation:report 含 sample subset keyword,違反 audit-full-sweep canonical(memory/feedback_audit_full_sweep_not_sample.md)"
42
+ fi
43
+
44
+ # ─ Validator B: 46-dim coverage ───────────────────────────────────────────
45
+ DIM_COUNT=$(grep -cE '(Dim|dim) ([1-9]|[1-4][0-9]|5[01])(\b|:)' "$FILE_PATH" 2>/dev/null || echo 0)
46
+ DIM_COUNT=${DIM_COUNT:-0}
47
+ if [ "$DIM_COUNT" -lt 46 ]; then
48
+ WARNINGS="${WARNINGS}\n ⚠️ [B] Dim coverage:report 提到 ${DIM_COUNT} dim,< 46 期望。確認跑了 46-dim 全 dispatch 還是漏 group"
49
+ fi
50
+
51
+ # ─ Validator C: audit-prompts.md coverage ─────────────────────────────────
52
+ AUDIT_PROMPTS="$PROJECT_DIR/.claude/skills/design-system-audit/references/audit-prompts.md"
53
+ if [ -f "$AUDIT_PROMPTS" ]; then
54
+ PROMPT_DIM_COUNT=$(grep -cE '^###[[:space:]]*Dim[[:space:]]+[1-9][0-9]?(\b|:|[[:space:]])' "$AUDIT_PROMPTS" 2>/dev/null || echo 0)
55
+ PROMPT_DIM_COUNT=${PROMPT_DIM_COUNT:-0}
56
+ if [ "$PROMPT_DIM_COUNT" -lt 46 ]; then
57
+ WARNINGS="${WARNINGS}\n 🔴 [C] audit-prompts.md prompt coverage:${PROMPT_DIM_COUNT}/46 — 紙上 dim 無 prompt,sub-agent 跑不動。下輪 prune Phase 1 D5 candidate(canonical drift:SKILL.md 46-dim vs audit-prompts.md prompt 數差距)"
58
+ TRIGGER_PRUNE=1
59
+ fi
60
+ fi
61
+
62
+ # ─ Validator D: @benchmark-unverified-blanket count drift ─────────────────
63
+ BENCH_DEBT=$(grep -rc '@benchmark-unverified-blanket' "$PROJECT_DIR/packages/design-system/src/" 2>/dev/null | awk -F: '{s+=$2} END{print s+0}')
64
+ BENCH_DEBT=${BENCH_DEBT:-0}
65
+ if [ "$BENCH_DEBT" -gt 0 ]; then
66
+ WARNINGS="${WARNINGS}\n ⚠️ [D] Benchmark cite debt:${BENCH_DEBT} 處 `@benchmark-unverified-blanket` marker — 對應 prune D9(M22 cite debt)"
67
+ TRIGGER_PRUNE=1
68
+ fi
69
+
70
+ # ─ Validator E: prune-chain-trigger emit ──────────────────────────────────
71
+ if [ "$TRIGGER_PRUNE" -eq 1 ] || [ -n "$WARNINGS" ]; then
72
+ mkdir -p "$PROJECT_DIR/.claude/logs" 2>/dev/null
73
+ printf '{"ts":"%s","file":"%s","trigger_prune":%d,"warnings":%s}\n' \
74
+ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
75
+ "$FILE_PATH" \
76
+ "$TRIGGER_PRUNE" \
77
+ "$(printf '%b' "$WARNINGS" | jq -Rs .)" \
78
+ >> "$PROJECT_DIR/.claude/logs/audit-post-report-validator.jsonl" 2>/dev/null || true
79
+
80
+ if [ "$TRIGGER_PRUNE" -eq 1 ]; then
81
+ CTX=$(printf '🚨 audit post-report validator: prune-chain-trigger fire。下輪 invoke /knowledge-prune scope=full(triggers: audit-prompts coverage < 100%% / @benchmark-unverified-blanket count > 0)。\n%b' "$WARNINGS")
82
+ jq -n --arg ctx "$CTX" '{
83
+ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: $ctx }
84
+ }'
85
+ fi
86
+ fi
87
+
88
+ exit 0
@@ -0,0 +1,73 @@
1
+ #!/bin/bash
2
+ # Audit NO-SAMPLE strict enforcement(2026-05-17 P0 user-mandated):
3
+ # /design-system-audit --deep dispatch sub-agent prompt 禁含 sample escape clause。
4
+ # 違 = BLOCKER 不允 dispatch。
5
+ #
6
+ # 背景:audit Dim 24/25/40-43/45/46/48 sub-agent 多次 sampled 不全掃 → user 抓「你他媽是不是又再搞抽樣」。
7
+ # 加 mechanical hook 攔截 dispatch prompt 含 sample escape keyword。
8
+ #
9
+ # PreToolUse(Agent)hook:讀 tool_input.prompt,grep escape keyword 命中 → exit 2 BLOCKER。
10
+ #
11
+ # Allow escape:dispatch prompt 加 `// @audit-sample-allow: <rationale>` 整 prompt 豁免(極罕見場景)。
12
+
13
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
14
+
15
+ set -uo pipefail
16
+
17
+ INPUT=$(cat)
18
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
+ EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // ""')
20
+
21
+ case "${TOOL:-}" in
22
+ Agent) ;;
23
+ *) exit 0 ;;
24
+ esac
25
+
26
+ # 2026-05-23 P0 升級 per user verbatim「幹你娘就叫你他媽所有稽核都要完整執行不要再抽樣,到底要講幾次?...把全部要稽核的東西都給我避免抽樣」:
27
+ # - PreToolUse:scan tool_input.prompt(dispatch prompt)阻 escape clause
28
+ # - PostToolUse:scan tool_response.content(sub-agent OUTPUT)阻 sub-agent 自報「I sampled / spot-check / representative」admission
29
+ case "${EVENT:-}" in
30
+ PostToolUse)
31
+ # Sub-agent OUTPUT scan — catch post-fact admission
32
+ OUTPUT=$(echo "$INPUT" | jq -r '.tool_response.content // .tool_response // ""' 2>/dev/null)
33
+ # Only fire on audit-related agent runs(by output keyword)
34
+ if ! echo "$OUTPUT" | grep -qiE 'audit|Dim [0-9]+|D[0-9]+ CLEAN|DS-wide|sub-agent'; then exit 0; fi
35
+ PROMPT="$OUTPUT" # reuse PROMPT var for downstream grep
36
+ ;;
37
+ *)
38
+ # PreToolUse(default existing logic):scan dispatch prompt
39
+ PROMPT=$(echo "$INPUT" | jq -r '.tool_input.prompt // ""')
40
+ ;;
41
+ esac
42
+
43
+ # Allow escape(極罕見)
44
+ if echo "$PROMPT" | grep -qE '@audit-sample-allow:'; then
45
+ exit 0
46
+ fi
47
+
48
+ # Only enforce on audit-related dispatch(detect by prompt keyword)
49
+ if ! echo "$PROMPT" | grep -qE 'audit|Dim [0-9]+|DS-wide|sub-agent|sweep'; then
50
+ exit 0
51
+ fi
52
+
53
+ # Detect sample escape clause keyword
54
+ # 2026-05-23 M34 fix per user verbatim「抽樣幾百次了到底要怎樣才能百分百避免」+「我要你他媽所有稽核都不能抽樣」:
55
+ # 原 regex 太窄(narrow) 漏抓 my own dispatch keyword「Sample-based scan OK」+「cover 20+ random samples」+「too many components for exhaustive」。
56
+ # 升 grep -i case-insensitive + 廣 pattern coverage(spec wording「NO-SAMPLE」是 broad category)。
57
+ ESCAPE_HITS=$(echo "$PROMPT" | grep -oiE 'sample[ -]?based|sample-N|sample-only|sample evidence|sample top [0-9]+|sample[s]?[[:space:]]+(scan|OK|allowed|fine|enough|sufficient|recommended|sub|covering)|covering[[:space:]]+[0-9]+\+?[[:space:]]*sample|covers[[:space:]]+[0-9]+\+?[[:space:]]*sample|cover[[:space:]]+[0-9]+\+?[[:space:]]*(random[[:space:]]+)?sample|pick top [0-9]+|top hot|sampled[[:space:]]*(components|elements|files|Button|button|story|stories)|sample-?based|sampling[[:space:]]+(scan|approach|method)|heavy agent needed|full sweep deferred|defer.*heavy|cite.*heavy agent|exhaustive[[:space:]]+too[[:space:]]+(many|much)|too[[:space:]]+many[[:space:]]+for[[:space:]]+exhaustive|random[[:space:]]+sample|skip[[:space:]]+rest|skip[[:space:]]+remaining|spot[[:space:]]*[-]?[[:space:]]*check(ed|ing)?|spot[[:space:]]*sample|representative[[:space:]]+(sample|subset)|i[[:space:]]+sampled|only[[:space:]]+sampled|sampled[[:space:]]+only|sample[[:space:]]+sized?|sub-agent[[:space:]]+self-judg|self-judgment[[:space:]]+per[[:space:]]+Dim|AI[[:space:]]+judgment[[:space:]]+per[[:space:]]+Dim|sample-based[[:space:]]+AI[[:space:]]+judgment|sampled[[:space:]]+(Button|button|first|few|several)' | sort -u)
58
+
59
+ if [ -n "$ESCAPE_HITS" ]; then
60
+ printf '🚨 AUDIT NO-SAMPLE ESCAPE CLAUSE BLOCKER(audit canonical 2026-05-17 user-mandated):\n' >&2
61
+ printf ' Dispatch prompt 含 sample escape clause:\n' >&2
62
+ echo "$ESCAPE_HITS" | while IFS= read -r hit; do
63
+ printf ' • "%s"\n' "$hit" >&2
64
+ done
65
+ printf '\n Per CLAUDE.md `# 稽核 canonical` + design-system-audit/SKILL.md Phase 1 NO-SAMPLE invariant:\n' >&2
66
+ printf ' 禁含「sample / top N / heavy agent needed / full sweep deferred」escape clause。\n' >&2
67
+ printf ' 每 dim 必 DS-wide ALL components 全掃,context 不夠拆 stage 分批,**不 sample**。\n' >&2
68
+ printf '\n 修方向:從 prompt 移除 escape clause,改寫「拆 N-stage(每 stage 10-15 元件)所有 stages 必跑完」。\n' >&2
69
+ printf ' Escape(極罕見): prompt 加 // @audit-sample-allow: <rationale>\n' >&2
70
+ exit 2
71
+ fi
72
+
73
+ exit 0
@@ -0,0 +1,106 @@
1
+ #!/bin/bash
2
+ set -uo pipefail
3
+ # PreToolUse hook:阻止「沒 source citation 的 world-class benchmark claim」(對齊 M22)。
4
+ #
5
+ # Bug 史(2026-05-02):
6
+ # AI 在 spec.md / tsx comment 寫「對齊 Ant Design」「Material 共識」「Polaris 派」
7
+ # 但沒附 source URL / screenshot / specific impl reference → 寫 code 時憑印象解讀,
8
+ # 常常**錯誤**(e.g., 我寫 Ant showTime range 是 2 calendar,實證是 1 calendar)。
9
+ #
10
+ # 機械化規則:
11
+ # 寫 spec / tsx 含 benchmark 關鍵詞 → 必同段含**至少一條** citation:
12
+ # - Inline URL(`https://...` 包含 ant-design / material / polaris / atlassian / carbon / shadcn / radix-ui domain)
13
+ # - GitHub issue / PR / source path(`#L42` line ref)
14
+ # - Screenshot reference(`snapshots/...`)
15
+ # - Marked `@benchmark-unverified`(顯式撤回)
16
+ #
17
+ # 允許 escape:
18
+ # 檔頭加 `// @benchmark-citation-allow: <reason>` 整檔豁免(legacy,M22 hook 上線過渡期)
19
+ # 檔頭加 `// @benchmark-unverified-blanket: <reason>` 整檔 M22 (d) 撤回(file-level unverified)
20
+
21
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
22
+
23
+ set -euo pipefail
24
+
25
+ INPUT=$(cat)
26
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
27
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
28
+
29
+ case "$TOOL" in
30
+ Edit|Write|MultiEdit) ;;
31
+ *) exit 0 ;;
32
+ esac
33
+
34
+ # Only spec.md / .tsx in design-system
35
+ case "$FILE_PATH" in
36
+ */packages/design-system/src/**/*.spec.md|*/packages/design-system/src/**/*.tsx) ;;
37
+ *) exit 0 ;;
38
+ esac
39
+
40
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
41
+ (.tool_input.content // "") + "\n" +
42
+ (.tool_input.new_string // "") + "\n" +
43
+ ([.tool_input.edits[]? | .new_string] | join("\n"))
44
+ ' 2>/dev/null || echo "")
45
+
46
+ [ -z "${NEW_CONTENT//[[:space:]]/}" ] && exit 0
47
+
48
+ # File-level allowlist
49
+ FIRST_LINES=$(printf '%s\n' "$NEW_CONTENT" | sed -n '1,5p')
50
+ if echo "$FIRST_LINES" | grep -qE '@benchmark-citation-allow:|@benchmark-unverified-blanket:'; then
51
+ exit 0
52
+ fi
53
+ if [ -f "$FILE_PATH" ]; then
54
+ ON_DISK_FIRST=$(sed -n '1,5p' "$FILE_PATH" 2>/dev/null || true)
55
+ if echo "$ON_DISK_FIRST" | grep -qE '@benchmark-citation-allow:|@benchmark-unverified-blanket:'; then
56
+ exit 0
57
+ fi
58
+ fi
59
+
60
+ # Detect benchmark claim keywords + check inline citation in same paragraph
61
+ # 用 perl -0777 slurp 比 awk simpler;每個含 benchmark keyword 的「段」(連續非空行)
62
+ # 必含 citation evidence。
63
+ VIOLATIONS=$(printf '%s' "$NEW_CONTENT" | perl -0777 -ne '
64
+ my $bench = qr/(Ant Design|Material(\s+X|\s+Design|\s+UI|\s+3)?|Polaris|Atlassian|Carbon|shadcn|Radix UI?|Apple HIG|Notion|Airtable|ClickUp|Figma|Linear)/;
65
+ my $cite = qr/(https?:\/\/(www\.)?(ant-design|material|polaris\.shopify|atlassian\.design|carbon|shadcn|ui\.shadcn|radix-ui|github|developer\.apple)|#L\d|snapshots\/|@benchmark-unverified)/;
66
+ # Iterate paragraphs(separated by blank lines)
67
+ for my $para (split /\n\s*\n/) {
68
+ next unless $para =~ $bench;
69
+ # Skip paragraphs that ARE source citations(URL line itself)
70
+ next if $para =~ /^\s*[\*-]?\s*https?:\/\//;
71
+ # Has citation? OK
72
+ next if $para =~ $cite;
73
+ # Violation:claim 但沒 cite
74
+ my $first_line = (split /\n/, $para)[0];
75
+ print " - " . substr($first_line, 0, 80) . "...\n";
76
+ }
77
+ '
78
+ )
79
+
80
+ if [ -n "$VIOLATIONS" ]; then
81
+ cat >&2 <<EOF
82
+
83
+ ┄┄┄┄ check_benchmark_citation — world-class benchmark claim 缺 source ┄┄┄┄
84
+
85
+ [P1 WARN] ${FILE_PATH}
86
+ 偵測到 benchmark claim 段缺 inline citation:
87
+ ${VIOLATIONS}
88
+ M22 canonical(2026-05-02):**寫 spec / code 含「Ant / Material / Polaris / ...」claim 必附**:
89
+ (a) Inline URL(domain 含 ant-design / material / polaris / atlassian / radix-ui / github 等)
90
+ (b) GitHub source path + line ref(\`#L42\`)
91
+ (c) Screenshot reference(\`snapshots/...\`)
92
+ (d) 顯式撤回 \`@benchmark-unverified\`
93
+
94
+ 歷史教訓(2026-05-02):
95
+ - 我 claim「Ant showTime range = 2 calendars」憑印象 → 實證是 1 calendar,白忙一場
96
+ - User: 「下次到底該如何完全避免你自己不斷說鬼話?」→ M22 + 本 hook
97
+
98
+ 整檔豁免:
99
+ - \`// @benchmark-citation-allow: <reason>\`(legacy 過渡期暫掛)
100
+ - \`// @benchmark-unverified-blanket: <reason>\`(M22 (d) file-level 撤回)
101
+ EOF
102
+ # Soft warning(P1)— print to stderr, don't block(exit 1 vs exit 2)
103
+ exit 1
104
+ fi
105
+
106
+ exit 0
@@ -0,0 +1,189 @@
1
+ #!/bin/bash
2
+ # Canonical propagation unified hook(2026-05-08 cluster E consolidation)
3
+ #
4
+ # Merges 3 PreToolUse hooks(原各檔已 retire,合併入此):
5
+ # E.1 principles canonical(原 check_principles_canonical,P0 BLOCK new file / P1 warn existing)
6
+ # E.2 L3 primitive import(原 check_l3_primitive_import,P0 BLOCK)
7
+ # E.3 spec-impl default alignment(原 check_spec_impl_default_alignment,P1 stderr)
8
+ #
9
+ # Why merge:皆 canonical SSOT propagation invariant — principles structure / L3 layer 邊界 /
10
+ # spec→impl 一致性,共用 INPUT parsing 模式。散裝是 M17 + Anthropic ≤ 15 hook 偏離。
11
+ #
12
+ # File scope per rule:
13
+ # E.1: *.principles.stories.tsx
14
+ # E.2: *.ts / *.tsx outside packages/design-system/src/{components,patterns,tokens,hooks}
15
+ # E.3: *.spec.md
16
+ #
17
+ # Allowlist tags:
18
+ # E.1: `// @principles-rationale: <reason>`
19
+ # E.2: 檔頭 5 行 `// @l3-import-allow: <reason>`
20
+ # E.3: 檔頭 5 行 `// @spec-impl-allow: <reason>`
21
+
22
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
23
+
24
+ set -uo pipefail
25
+
26
+ INPUT=$(cat)
27
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
28
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
29
+
30
+ case "$TOOL" in
31
+ Edit|Write|MultiEdit) ;;
32
+ *) exit 0 ;;
33
+ esac
34
+
35
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
36
+ (.tool_input.content // "") + "\n" +
37
+ (.tool_input.new_string // "") + "\n" +
38
+ ([.tool_input.edits[]? | .new_string] | join("\n"))
39
+ ' 2>/dev/null || echo "")
40
+
41
+ [ -z "${NEW_CONTENT//[[:space:]]/}" ] && exit 0
42
+
43
+ WORST=0
44
+ record_worst() { local lvl=$1; [ "$lvl" -gt "$WORST" ] && WORST=$lvl; }
45
+
46
+ # ── E.1 principles canonical(*.principles.stories.tsx)──────────────────────
47
+ case "$FILE_PATH" in
48
+ *.principles.stories.tsx)
49
+ if ! echo "$NEW_CONTENT" | grep -q "@principles-rationale:"; then
50
+ EXISTING_CONTENT=""
51
+ [ -f "$FILE_PATH" ] && EXISTING_CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "")
52
+ FULL_CONTENT="${EXISTING_CONTENT}
53
+ ${NEW_CONTENT}"
54
+ STORY_EXPORTS=$(echo "$FULL_CONTENT" | grep -oE "^export const [A-Z][a-zA-Z]+" | awk '{print $3}' | sort -u)
55
+
56
+ VIOLATIONS_E1=""
57
+ WARNINGS_E1=""
58
+
59
+ DEPRECATED_NAMES=("Forbidden" "Donts" "Pitfalls" "Prohibitions" "NonGoals" "VisualDonts")
60
+ for deprecated in "${DEPRECATED_NAMES[@]}"; do
61
+ if echo "$STORY_EXPORTS" | grep -qE "^${deprecated}"; then
62
+ if [ -z "$EXISTING_CONTENT" ]; then
63
+ VIOLATIONS_E1="${VIOLATIONS_E1}\n • [P0] Deprecated naming '${deprecated}*' — rename to 'WhenNotToUse'"
64
+ else
65
+ WARNINGS_E1="${WARNINGS_E1}\n • [P1] Deprecated '${deprecated}*' — migrate to 'WhenNotToUse'"
66
+ fi
67
+ fi
68
+ done
69
+
70
+ HAS_WHEN_TO_USE=0; HAS_WHEN_NOT_TO_USE=0; HAS_VS_RULE=0; HAS_CONTENT_GUIDELINES=0; HAS_USAGE_GUIDANCE=0
71
+ echo "$STORY_EXPORTS" | grep -qE "^WhenToUse$" && HAS_WHEN_TO_USE=1
72
+ echo "$STORY_EXPORTS" | grep -qE "^WhenNotToUse$" && HAS_WHEN_NOT_TO_USE=1
73
+ echo "$STORY_EXPORTS" | grep -qE "^Vs[A-Z].*Rule$|^.+Vs.+Rule$" && HAS_VS_RULE=1
74
+ echo "$STORY_EXPORTS" | grep -qE "^ContentGuidelines$" && HAS_CONTENT_GUIDELINES=1
75
+ echo "$STORY_EXPORTS" | grep -qE "^(UsageScenarioRule|WhatItIs)$" && HAS_WHEN_TO_USE=1
76
+ for deprecated in "${DEPRECATED_NAMES[@]}"; do
77
+ echo "$STORY_EXPORTS" | grep -qE "^${deprecated}" && HAS_WHEN_NOT_TO_USE=1
78
+ done
79
+ echo "$STORY_EXPORTS" | grep -qE "^UsageGuidance$" && HAS_USAGE_GUIDANCE=1
80
+
81
+ CORE_COUNT=$((HAS_WHEN_TO_USE + HAS_WHEN_NOT_TO_USE + HAS_VS_RULE + HAS_CONTENT_GUIDELINES))
82
+ [ "$HAS_USAGE_GUIDANCE" -eq 1 ] && CORE_COUNT=2
83
+
84
+ if [ "$CORE_COUNT" -lt 2 ]; then
85
+ if [ -z "$EXISTING_CONTENT" ]; then
86
+ VIOLATIONS_E1="${VIOLATIONS_E1}\n • [P0] Universal core ≥ 2 required: have ${CORE_COUNT} of {WhenToUse, WhenNotToUse, Vs*Rule, ContentGuidelines}"
87
+ else
88
+ WARNINGS_E1="${WARNINGS_E1}\n • [P1] Universal core ${CORE_COUNT}/2 — add WhenToUse / WhenNotToUse / Vs*Rule / ContentGuidelines"
89
+ fi
90
+ fi
91
+
92
+ if [ -n "$VIOLATIONS_E1" ]; then
93
+ cat >&2 <<EOF
94
+
95
+ ┄┄┄ E.1 check_canonical_propagation — principles canonical BLOCKER ┄┄┄
96
+
97
+ [P0] ${FILE_PATH}
98
+ Violations:$(echo -e "$VIOLATIONS_E1")
99
+
100
+ Per category-templates.md「Principles canonical」:
101
+ ‣ Universal core ≥ 2 of {WhenToUse, WhenNotToUse, Vs*Rule, ContentGuidelines}
102
+ ‣ Anti-pattern naming → WhenNotToUse(Forbidden/Donts/Pitfalls/Prohibitions/NonGoals/VisualDonts deprecated)
103
+ ‣ 例外:加 \`// @principles-rationale: <reason>\`
104
+
105
+ EOF
106
+ record_worst 2
107
+ fi
108
+ [ -n "$WARNINGS_E1" ] && echo "⚠️ E.1 principles canonical warning:$(echo -e "$WARNINGS_E1")" >&2
109
+ fi
110
+ ;;
111
+ esac
112
+
113
+ # ── E.2 L3 primitive import(*.ts/.tsx outside DS internal)───────────────────
114
+ case "$FILE_PATH" in
115
+ *.ts|*.tsx)
116
+ case "$FILE_PATH" in
117
+ */packages/design-system/src/components/*|*/packages/design-system/src/patterns/*|*/packages/design-system/src/tokens/*|*/packages/design-system/src/hooks/*) ;; # DS internal skip
118
+ *)
119
+ # File-level allowlist:檔頭 5 行
120
+ ALLOW_E2=0
121
+ FIRST_LINES_E2=$(printf '%s\n' "$NEW_CONTENT" | sed -n '1,5p')
122
+ echo "$FIRST_LINES_E2" | grep -qE '//[[:space:]]*@l3-import-allow:' && ALLOW_E2=1
123
+ if [ -f "$FILE_PATH" ] && [ "$ALLOW_E2" = "0" ]; then
124
+ ON_DISK_FIRST=$(sed -n '1,5p' "$FILE_PATH" 2>/dev/null || true)
125
+ echo "$ON_DISK_FIRST" | grep -qE '//[[:space:]]*@l3-import-allow:' && ALLOW_E2=1
126
+ fi
127
+ if [ "$ALLOW_E2" = "0" ]; then
128
+ L3_IMPORT_PATTERN='from\s+["'"'"'][^"'"'"']*patterns/element-anatomy/item-anatomy["'"'"']'
129
+ if printf '%s\n' "$NEW_CONTENT" | grep -qE "$L3_IMPORT_PATTERN"; then
130
+ MATCHED_LINE=$(printf '%s\n' "$NEW_CONTENT" | grep -nE "$L3_IMPORT_PATTERN" | head -1)
131
+ cat >&2 <<EOF
132
+
133
+ ┄┄┄ E.2 check_canonical_propagation — L3 primitive import BLOCKER ┄┄┄
134
+
135
+ [P0] ${FILE_PATH}
136
+ > ${MATCHED_LINE}
137
+
138
+ L3 internal primitives(\`ItemInlineAction\` / \`ItemContent\` / \`RowSizeProvider\` 等)
139
+ 是給 DS 作者建 host 元件用的 building block,不是 app code 用的。
140
+
141
+ 修法(擇一):
142
+ (a) host config API(90%):<Input endAction={...} /> / <TreeItem inlineActions={...} />
143
+ (b) host slot escape(10%):<Input endSlot={<Custom/>} />
144
+ (c) 獨立 button(非 inline):<Button iconOnly variant="text" />
145
+
146
+ 刻意 import L3:檔首 5 行加 \`// @l3-import-allow: <reason + spec.md anchor>\`
147
+
148
+ EOF
149
+ record_worst 2
150
+ fi
151
+ fi
152
+ ;;
153
+ esac
154
+ ;;
155
+ esac
156
+
157
+ # ── E.3 spec-impl default alignment(*.spec.md)──────────────────────────────
158
+ case "$FILE_PATH" in
159
+ *.spec.md)
160
+ FIRST_LINES_E3=$(printf '%s\n' "$NEW_CONTENT" | sed -n '1,5p')
161
+ if ! echo "$FIRST_LINES_E3" | grep -q '@spec-impl-allow'; then
162
+ DEFAULT_LINES=$(printf '%s' "$NEW_CONTENT" | grep -nE '預設|預値|default[ \t]*[=:]|`default`|預設值' | head -3)
163
+ if [ -n "$DEFAULT_LINES" ]; then
164
+ cat >&2 <<EOF
165
+
166
+ ┄┄┄ E.3 check_canonical_propagation — spec-impl default alignment WARN ┄┄┄
167
+
168
+ [P1] ${FILE_PATH}
169
+ 偵測「default」/「預設」keyword:
170
+ ${DEFAULT_LINES}
171
+
172
+ ⚠️ spec 寫預設值 → 必對應 impl 驗證一致(M14 + 真實案例:SelectMenu spec 寫
173
+ 「width 預設 = trigger-width」但 PopoverContent w-72 hardcode override,canonical 失效)
174
+
175
+ Self-check:
176
+ - 找對應 implementation file
177
+ - grep 該 default value 是否 hardcode / 是否被外層 override
178
+ - 不一致 → 修 impl OR 修 spec 對齊
179
+ - 一致 → impl 加 inline comment 「Default per spec L<N>」回扣
180
+
181
+ 整檔豁免:檔頭 5 行 \`// @spec-impl-allow: <reason>\`(soft warn 不擋 commit)
182
+
183
+ EOF
184
+ fi
185
+ fi
186
+ ;;
187
+ esac
188
+
189
+ exit $WORST