@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,205 @@
1
+ #!/bin/bash
2
+ # Field family unified invariant hook(2026-05-08 cluster A consolidation)
3
+ #
4
+ # Merges 4 PreToolUse hooks(原各檔已 retire,合併入此)— 共 4 條 sub-rules:
5
+ # A.1 naked row-mode propagation(原 check_naked_row_mode_propagation,P0 BLOCKER)
6
+ # A.2 FieldControlGroup wrapper direct child(原 check_field_control_group_direct_child,P1 WARN)
7
+ # A.3 Field state ring SSOT(原 check_field_state_token_consume 3 sub-rules,P0 BLOCKER)
8
+ # A.4 disabled placeholder color(原 check_disabled_placeholder_color,P1 stderr only)
9
+ #
10
+ # Why merge:皆 Field 家族 invariant,共用 INPUT parsing + Edit/Write filter pattern,
11
+ # 分散在 4 個 hook 是「散裝 SSOT」(M17 + Anthropic ≤ 15 hook best practice 違反)。
12
+ #
13
+ # Exit code precedence:BLOCK(2)> WARN(1)> INFO(0)。每 rule 可獨立觸發,worst 勝。
14
+ #
15
+ # Per-rule allowlist(各自獨立):
16
+ # A.1: `// @naked-row-mode-allow: <reason>`
17
+ # A.2: `// @fcg-wrapper-allow: <reason>` 或檔頭
18
+ # A.3: `// @field-state-ring-allow: <reason>`
19
+ # A.4: `// @disabled-color-allow: <reason>`
20
+
21
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
22
+
23
+ set -uo 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
+ # Tool filter — 只 Edit/Write/MultiEdit 跑
30
+ case "$TOOL" in
31
+ Edit|Write|MultiEdit) ;;
32
+ *) exit 0 ;;
33
+ esac
34
+
35
+ # 不是 .tsx / .ts 直接過(各 rule 內部還會再 narrow)
36
+ case "$FILE_PATH" in
37
+ *.tsx|*.ts) ;;
38
+ *) exit 0 ;;
39
+ esac
40
+
41
+ # 讀 merged content(舊檔 + 新 edit 拼起)— A.1 / A.3 需要整檔判 naked variant 存在性
42
+ FILE_CONTENT=""
43
+ if [ -f "$FILE_PATH" ]; then
44
+ FILE_CONTENT=$(cat "$FILE_PATH")
45
+ fi
46
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
47
+ (.tool_input.content // "") + "\n" +
48
+ (.tool_input.new_string // "") + "\n" +
49
+ ([.tool_input.edits[]? | .new_string] | join("\n"))
50
+ ' 2>/dev/null || echo "")
51
+
52
+ # A.2 / A.4 只看 NEW_CONTENT(diff-level signal),A.1 / A.3 看 MERGED
53
+ MERGED_CONTENT="${FILE_CONTENT}
54
+ ${NEW_CONTENT}"
55
+
56
+ [ -z "${MERGED_CONTENT//[[:space:]]/}" ] && exit 0
57
+
58
+ WORST=0
59
+ record_worst() { local lvl=$1; [ "$lvl" -gt "$WORST" ] && WORST=$lvl; }
60
+
61
+ # ── A.1 naked row-mode propagation(P0 BLOCKER)─────────────────────────────────
62
+ case "$FILE_PATH" in
63
+ *components/*.tsx)
64
+ case "$FILE_PATH" in
65
+ */field-wrapper.tsx|*/textarea.tsx) ;; # SSOT host skip
66
+ *)
67
+ if ! echo "$MERGED_CONTENT" | grep -q '@naked-row-mode-allow' \
68
+ && echo "$MERGED_CONTENT" | grep -E "variant:\s*['\"]naked['\"]|variant=\{?['\"]naked['\"]" >/dev/null \
69
+ && echo "$MERGED_CONTENT" | grep -E "(inline-flex|flex)[^\"'\`]*items-center" >/dev/null \
70
+ && ! echo "$MERGED_CONTENT" | grep -q "nakedCellRowModeAlign"; then
71
+ cat >&2 <<EOF
72
+
73
+ ┄┄┄ A.1 check_field_family_invariants — naked row-mode propagation BLOCKER ┄┄┄
74
+
75
+ [P0] ${FILE_PATH}
76
+ 偵測到此檔消費 \`variant="naked"\` + 內部 wrapper hardcode \`items-center\`,
77
+ 但**未** import / apply \`nakedCellRowModeAlign\` SSOT。
78
+
79
+ ⚠️ M19 canonical:naked variant 元件所有內部 wrapper 必 propagate host cell
80
+ \`data-row-mode\`(autoRow→items-start / fixed→items-center)。
81
+
82
+ 修法:
83
+ 1. import { nakedCellRowModeAlign } from '@/design-system/components/Field/field-wrapper'
84
+ 2. wrapper className 加上 SSOT(eg \`cn('flex-1 min-w-0 inline-flex items-center', nakedCellRowModeAlign)\`)
85
+ 3. 例外:行尾 \`// @naked-row-mode-allow: <reason>\`
86
+
87
+ EOF
88
+ record_worst 2
89
+ fi
90
+ ;;
91
+ esac
92
+ ;;
93
+ esac
94
+
95
+ # ── A.2 FieldControlGroup wrapper direct child(P1 WARN)────────────────────────
96
+ case "$FILE_PATH" in
97
+ *.tsx)
98
+ if echo "$NEW_CONTENT" | grep -q '<FieldControlGroup' \
99
+ && ! echo "$NEW_CONTENT" | grep -q '@fcg-wrapper-allow'; then
100
+ SUSPECT=$(printf '%s' "$NEW_CONTENT" | awk '
101
+ /<FieldControlGroup/ { inFCG=1; next }
102
+ /<\/FieldControlGroup>/ { inFCG=0; next }
103
+ inFCG && /^[[:space:]]*<(div|span)[[:space:]>]/ {
104
+ if ($0 !~ /display:[[:space:]]*contents/ && $0 !~ /@fcg-wrapper-allow/) print NR ":" $0
105
+ }
106
+ ')
107
+ if [ -n "$SUSPECT" ]; then
108
+ cat >&2 <<EOF
109
+
110
+ ┄┄┄ A.2 check_field_family_invariants — FieldControlGroup wrapper WARN ┄┄┄
111
+
112
+ [P1] ${FILE_PATH}
113
+ 偵測到 FieldControlGroup 內有 \`<div>\` / \`<span>\` wrapper(可能破壞 CSS \`[&>*]\` variants):
114
+ ${SUSPECT}
115
+
116
+ 修法 3 擇 1:
117
+ 1. 移除 wrapper,Field control 直接是 FieldControlGroup direct child
118
+ 2. 透過 prop forward className(eg \`<FilterValuePicker className="flex-1 min-w-0">\`)
119
+ 3. wrapper 用 \`display:contents\` / 加 \`// @fcg-wrapper-allow: <reason>\`
120
+
121
+ EOF
122
+ record_worst 1
123
+ fi
124
+ fi
125
+ ;;
126
+ esac
127
+
128
+ # ── A.3 Field state ring SSOT(P0 BLOCKER,3 sub-rules)─────────────────────────
129
+ case "$FILE_PATH" in
130
+ *components/*.tsx)
131
+ case "$FILE_PATH" in
132
+ */field-wrapper.tsx|*/textarea.tsx|*.stories.tsx|*.test.*|*.spec.tsx) ;; # SSOT/test skip
133
+ *)
134
+ if ! echo "$NEW_CONTENT" | grep -q '@field-state-ring-allow'; then
135
+ # A.3.1 舊 box-shadow inset
136
+ if echo "$NEW_CONTENT" | grep -E "(hover|focus-within|data-\[state=open\]):shadow-\[inset" >/dev/null; then
137
+ cat >&2 <<'EOF'
138
+
139
+ ┄┄┄ A.3.1 check_field_family_invariants — Field state ring shadow inset BLOCKER ┄┄┄
140
+
141
+ [P0] naked variant state ring 用 `box-shadow inset` — v9 retire pattern。
142
+ 修法:讓 Field default state machine 自動繼承(border-based);cell hover 用 `nakedCellEditableDisplayHover` const。
143
+
144
+ EOF
145
+ record_worst 2
146
+ fi
147
+ # A.3.2 自寫 outline state ring
148
+ if echo "$NEW_CONTENT" | grep -E "(hover|focus-within|focus-visible|data-\[state=open\]):outline-(border|primary)" >/dev/null; then
149
+ cat >&2 <<'EOF'
150
+
151
+ ┄┄┄ A.3.2 check_field_family_invariants — Field state ring outline BLOCKER ┄┄┄
152
+
153
+ [P0] 自寫 `hover:outline-border` / `focus-within:outline-primary` — v13 canonical 禁。
154
+ 修法:Field default 自動繼承 / cell 用 `nakedCellEditableDisplayHover` const。
155
+
156
+ EOF
157
+ record_worst 2
158
+ fi
159
+ # A.3.3 per-control open=blue override(v13.5)
160
+ if echo "$NEW_CONTENT" | grep -E "(open|isOpen) +&& +.{0,40}('border-primary'|\"border-primary\")" >/dev/null \
161
+ || echo "$NEW_CONTENT" | grep -E "data-\[state=open\]:border-primary" >/dev/null; then
162
+ cat >&2 <<'EOF'
163
+
164
+ ┄┄┄ A.3.3 check_field_family_invariants — per-control open=blue BLOCKER ┄┄┄
165
+
166
+ [P0] per-control `open && 'border-primary'` / `data-[state=open]:border-primary` — v13.3 canonical「focus dominates everything」禁。
167
+ 修法:刪 override。Radix Popover open 時 trigger 通常 focused → focus-within fires → 藍(自然 Ant 風)。改 Field default SSOT 須 spec 補 rationale。
168
+
169
+ EOF
170
+ record_worst 2
171
+ fi
172
+ fi
173
+ ;;
174
+ esac
175
+ ;;
176
+ esac
177
+
178
+ # ── A.4 disabled placeholder color(P1 stderr,exit 0 不 block)──────────────────
179
+ if ! echo "$NEW_CONTENT" | grep -q '@disabled-color-allow'; then
180
+ SUSPECT_DP=""
181
+ if echo "$NEW_CONTENT" | grep -E "placeholder:text-fg-muted" >/dev/null \
182
+ && ! echo "$NEW_CONTENT" | grep -E "(disabled:placeholder:text-fg-disabled|group-data-\[field-mode=disabled\].*placeholder:text-fg-disabled|resolvedMode\s*===\s*'disabled'.*text-fg-disabled)" >/dev/null; then
183
+ SUSPECT_DP="$SUSPECT_DP [placeholder:text-fg-muted 無 disabled override]"
184
+ fi
185
+ if echo "$NEW_CONTENT" | grep -E '<span[^>]*"text-fg-muted"[^>]*>\s*\{[^}]*placeholder' >/dev/null 2>&1 \
186
+ && ! echo "$NEW_CONTENT" | grep -E "resolvedMode\s*===\s*'disabled'" >/dev/null; then
187
+ SUSPECT_DP="$SUSPECT_DP [<span text-fg-muted>{placeholder} 不分 mode]"
188
+ fi
189
+ if [ -n "$SUSPECT_DP" ]; then
190
+ cat >&2 <<EOF
191
+
192
+ ┄┄┄ A.4 check_field_family_invariants — disabled placeholder color WARN ┄┄┄
193
+
194
+ [P1] ${FILE_PATH}
195
+ ${SUSPECT_DP}
196
+ 修法:disabled:placeholder:text-fg-disabled / group-data-[field-mode=disabled]/field:placeholder:text-fg-disabled / JSX 條件
197
+ 詳:tokens/color/color.spec.md「Disabled state precedence canonical」/ M24
198
+ 例外:行尾 \`// @disabled-color-allow: <reason>\`
199
+
200
+ EOF
201
+ # A.4 原 hook exit 0(stderr only),保持向後兼容不升 WORST
202
+ fi
203
+ fi
204
+
205
+ exit $WORST
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ set -uo pipefail
3
+ # PreToolUse Edit/Write: enforce per-file line budgets for governance files.
4
+ #
5
+ # Budgets — SSOT 是 CLAUDE.md `# 治理 canonical` 「行數預算」段。
6
+ # 本檔不再硬寫(避免 漂移 — 2026-05-15 Fix 4 per sub-agent a9e6d53c finding 6:
7
+ # 之前硬寫 400 vs CLAUDE.md「target 200 / transition 400 / hard cap 800」三 home 三 baseline)。
8
+ # 改 dynamic 從 CLAUDE.md grep,fallback 寫死值。SSOT 改一處全處跟動。
9
+ #
10
+ # Non-blocking: injects warning via hookSpecificOutput additionalContext; Claude
11
+ # decides whether to split or ack. Hard block would paralyse edits to legitimately
12
+ # large canonical specs (e.g. item-anatomy ~900 lines) — require explicit override.
13
+
14
+ # Per-hook fire logging(enables /knowledge-prune D2 dead-hook detection)
15
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
16
+
17
+ set -euo pipefail
18
+
19
+ INPUT=$(cat)
20
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
21
+
22
+ [ -z "$FILE_PATH" ] && exit 0
23
+ [ -f "$FILE_PATH" ] || exit 0
24
+
25
+ # Only check governance files
26
+ # CLAUDE.md L34 SSOT: target ≤ 200(Anthropic best-practice)/ transition ≤ 400 / hard cap 800
27
+ # 本 hook 在 transition 觸發 P1 warning,hard cap 觸發 P0 block
28
+ case "$FILE_PATH" in
29
+ */CLAUDE.md) BUDGET=400; TRANSITION=800; LABEL="CLAUDE.md" ;; # warn at transition (400), block at hard cap (800); target 200
30
+ */memory/*.md) BUDGET=100; TRANSITION=100; LABEL="memory file" ;;
31
+ # Super-foundational SSOT spec(item-anatomy = Family 1+2 跨 10+ 消費者 SSOT,唯一例外)
32
+ */item-anatomy.spec.md)
33
+ BUDGET=800; TRANSITION=1200; LABEL="super-foundational SSOT spec.md(item-anatomy 例外)" ;;
34
+ # Foundational SSOT specs(2026-04-24 升 cap 800 — spec 內部有 rationale section)
35
+ # 2026-05-18 加 data-table:DS 最複雜 composite + 跨家族 anchor(L1-L4 完整 grid taxonomy,
36
+ # 行對齊 item-anatomy / 浮層對齊 overlay-surface / state 對齊 field-controls)。
37
+ # frontmatter 已標 `foundational_ssot: true`。
38
+ # 2026-05-22 prune:color.spec.md 升 tier 2 cap 1000(per CLAUDE.md「foundational ≤ 800-1200」+ /knowledge-prune Lens 1+2 verdict — 218-line semantic 不可拆 + nested theme + Atlassian rationale 集中一處)。
39
+ */color.spec.md)
40
+ BUDGET=800; TRANSITION=1000; LABEL="foundational SSOT spec.md(color tier 2 cap 1000)" ;;
41
+ */sidebar.spec.md|*/tree-view.spec.md|*/data-table.spec.md)
42
+ BUDGET=500; TRANSITION=800; LABEL="foundational SSOT spec.md" ;;
43
+ *.spec.md) BUDGET=300; TRANSITION=500; LABEL="spec.md" ;;
44
+ *.claude/skills/*/SKILL.md) BUDGET=250; TRANSITION=400; LABEL="SKILL.md" ;;
45
+ *) exit 0 ;;
46
+ esac
47
+
48
+ LINES=$(wc -l < "$FILE_PATH" | tr -d ' ')
49
+ [ "$LINES" -le "$BUDGET" ] && exit 0
50
+
51
+ # Over budget — warn. Hard-block only if also over transition cap.
52
+ if [ "$LINES" -gt "$TRANSITION" ]; then
53
+ MSG="⛔ ${LABEL} at ${FILE_PATH} is ${LINES} lines (hard cap ${TRANSITION}). Must reduce before adding. Run /knowledge-prune or identify which section to remove."
54
+ else
55
+ MSG="⚠️ ${LABEL} at ${FILE_PATH} is ${LINES} lines (budget ${BUDGET}, transition cap ${TRANSITION}). Prefer consolidating over appending. What can you remove/merge to make room?"
56
+ fi
57
+
58
+ ESCAPED=$(printf '%s' "$MSG" | jq -Rs .)
59
+ printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":%s}}\n' "$ESCAPED"
60
+ exit 0
@@ -0,0 +1,87 @@
1
+ #!/bin/bash
2
+ # Header canonical W1 Border ownership enforcement(per header-canonical.spec.md W1):
3
+ # Header 含 Tabs(`<Tabs>` / `<TabsList>` child)→ 必標 `withTabs` prop 讓 border auto-suppress。
4
+ #
5
+ # PreToolUse(Edit / Write)hook:
6
+ # 讀 post-edit 完整 content(disk + new_string merge)→ count(header) ≥ 1 + count(tabs) ≥ 1
7
+ # 時要求 count(withTabs) ≥ count(header)。違反 = BLOCKER。
8
+ #
9
+ # 2026-05-17 Round 3:用 simple grep counting(per-instance awk 在 macOS bash 環境不穩定)。
10
+ # 在「同 file 一 header 一 withTabs」常規 case 抓得到;edge case(混用 instance)需 audit dim 52
11
+ # batch verify 補。
12
+ #
13
+ # Allow escape:檔頭 `// @header-withtabs-allow:` 整檔豁免。
14
+
15
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
16
+
17
+ INPUT=$(cat)
18
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
20
+
21
+ case "${TOOL:-}" in
22
+ Edit|Write|MultiEdit) ;;
23
+ *) exit 0 ;;
24
+ esac
25
+
26
+ case "${FILE_PATH:-}" in
27
+ */packages/design-system/src/**/*.tsx) ;;
28
+ *) exit 0 ;;
29
+ esac
30
+
31
+ # Skip stories
32
+ case "${FILE_PATH:-}" in
33
+ *.stories.tsx|*.anatomy.stories.tsx|*.principles.stories.tsx) exit 0 ;;
34
+ esac
35
+
36
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
37
+ if .tool_input.new_string then .tool_input.new_string
38
+ elif .tool_input.content then .tool_input.content
39
+ else "" end
40
+ ')
41
+
42
+ DISK_CONTENT=""
43
+ if [ -f "$FILE_PATH" ]; then
44
+ DISK_CONTENT=$(cat "$FILE_PATH")
45
+ fi
46
+
47
+ FULL_CONTENT=$(printf '%s\n%s' "${DISK_CONTENT:-}" "${NEW_CONTENT:-}")
48
+
49
+ if echo "${FULL_CONTENT:-}" | grep -qE '@header-withtabs-allow:'; then
50
+ exit 0
51
+ fi
52
+
53
+ # GAP 2 fix(2026-05-18 M34 codify):broaden hardcoded 6-name allowlist 到 generic [A-Z]*Header pattern。
54
+ # 對齊 header-canonical.spec.md「任何 header 含 Tabs」+「未來 Drawer / Card / Table header」前瞻
55
+ HEADERS=$(echo "$FULL_CONTENT" | grep -cE '<[A-Z][a-zA-Z]*Header[[:space:]>]' 2>/dev/null)
56
+ HEADERS=$(echo "${HEADERS:-0}" | head -1)
57
+ TABS=$(echo "$FULL_CONTENT" | grep -cE '<(Tabs|TabsList|TabsTrigger)[[:space:]>]' 2>/dev/null)
58
+ TABS=$(echo "${TABS:-0}" | head -1)
59
+ WITHTABS=$(echo "$FULL_CONTENT" | grep -cE 'withTabs([[:space:]]*=|[[:space:]]*[}>]|[[:space:]]*$)' 2>/dev/null)
60
+ WITHTABS=$(echo "${WITHTABS:-0}" | head -1)
61
+
62
+ # 若無 header 或無 tabs → 不適用
63
+ if [ "${HEADERS:-0}" -lt 1 ] 2>/dev/null || [ "${TABS:-0}" -lt 1 ] 2>/dev/null; then
64
+ exit 0
65
+ fi
66
+
67
+ # 要求 withTabs 出現 ≥ 1 次(寬鬆檢查;per-instance precise 留 Dim 52 batch verify)
68
+ if [ "${WITHTABS:-0}" -lt 1 ] 2>/dev/null; then
69
+ printf '🚨 HEADER + TABS WITHTABS BLOCKER(header-canonical.spec.md W1):\n' >&2
70
+ printf ' File: %s\n' "$FILE_PATH" >&2
71
+ printf ' 檔含 %s 個 header JSX + %s 個 Tabs JSX,但 withTabs prop 出現 0 次。\n' "$HEADERS" "$TABS" >&2
72
+ printf ' Header `border-b` + TabsList `border-b border-border` 會雙線。\n' >&2
73
+ printf '\n SSOT: patterns/header-canonical/header-canonical.spec.md W1\n' >&2
74
+ printf ' 修方向: <ChromeHeader withTabs> 或 <SurfaceHeader withTabs>\n' >&2
75
+ printf ' Escape: 檔頭加 // @header-withtabs-allow: <rationale>\n' >&2
76
+ exit 2
77
+ fi
78
+
79
+ # 警告(per-instance 精度不夠 — 若 header > withTabs,某 instance 可能漏):
80
+ if [ "${WITHTABS:-0}" -lt "${HEADERS:-0}" ] 2>/dev/null; then
81
+ printf '⚠️ HEADER WITHTABS COUNT MISMATCH(W1 soft warn):\n' >&2
82
+ printf ' File: %s\n' "$FILE_PATH" >&2
83
+ printf ' header instance %s 個 / withTabs prop %s 個 — 可能有 instance 漏 prop\n' "$HEADERS" "$WITHTABS" >&2
84
+ printf ' ⚠️ 寬鬆模式 exit 0(不 block);Dim 52 batch verify 補 per-instance 精度\n' >&2
85
+ fi
86
+
87
+ exit 0
@@ -0,0 +1,93 @@
1
+ #!/bin/bash
2
+ # Solo-work canonical 補丁(2026-05-17 user-mandated):
3
+ # 既有 check_solo_workflow.sh R1-R3 抓「2nd branch / PR creation / push main 無 trigger」,
4
+ # 但**不抓「整 session 在 main 上 edit production code」**(M28 sub-rule)。
5
+ #
6
+ # 本 hook 補:PreToolUse Edit/Write 偵測「current branch == main + edit production code
7
+ # + 近 5 條 user msg 無「開 branch / 在 branch 上做」trigger keyword」→ BLOCKER。
8
+ #
9
+ # 對應 CLAUDE.md `# Git solo-work canonical` Step 1 「1 chat = 1 working branch」
10
+ # + memory/feedback_solo_dev_workflow.md SSOT。
11
+ #
12
+ # 起因 2026-05-17:本 session AI 整個 deep audit + 補修 + a11y batch 全直接在 main edit,
13
+ # user 抓「不是只有我說 push 到 main 才真的會 push 到 main 嗎」。R1 只抓 `claude/*` prefix
14
+ # branch 數,沒抓「在 main edit」這個 root cause。
15
+ #
16
+ # Allow escape:
17
+ # - doc-only / governance-only edit(`.claude/**` / `*.spec.md`)豁免 — 不需 Netlify preview
18
+ # - `CLAUDE_BYPASS_MAIN_WORKBENCH=1` env var(audit-logged)
19
+
20
+ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
21
+
22
+ set -uo pipefail
23
+
24
+ INPUT=$(cat)
25
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
26
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
27
+
28
+ case "${TOOL:-}" in
29
+ Edit|Write|MultiEdit) ;;
30
+ *) exit 0 ;;
31
+ esac
32
+
33
+ # Only intercept production code edits (packages/design-system/src / src/app / src/explorations)
34
+ case "${FILE_PATH:-}" in
35
+ */packages/design-system/src/**/*.tsx|*/packages/design-system/src/**/*.ts|*/packages/design-system/src/**/*.css) ;;
36
+ */src/app/**|*/src/explorations/**) ;;
37
+ *) exit 0 ;;
38
+ esac
39
+
40
+ # Skip if env override
41
+ if [ "${CLAUDE_BYPASS_MAIN_WORKBENCH:-0}" = "1" ]; then
42
+ exit 0
43
+ fi
44
+
45
+ # Detect current branch
46
+ CURRENT_BRANCH=$(cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" && git branch --show-current 2>/dev/null || echo "")
47
+
48
+ if [ "${CURRENT_BRANCH:-}" != "main" ] && [ "${CURRENT_BRANCH:-}" != "master" ]; then
49
+ # Not on main — already on working branch, allow
50
+ exit 0
51
+ fi
52
+
53
+ # On main + edit production code → check recent user msg for branch trigger
54
+ TRANSCRIPT="${CLAUDE_TRANSCRIPT_PATH:-}"
55
+ TRIGGER_FOUND=0
56
+ if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
57
+ RECENT_USER=$(tail -200 "$TRANSCRIPT" 2>/dev/null | \
58
+ jq -r 'select(.role == "user") | .content' 2>/dev/null | tail -5 | tr '\n' ' ')
59
+ if echo "${RECENT_USER:-}" | grep -qE '(開.*branch|開.*分支|新.*branch|新.*分支|working branch|在.*branch.*做|在 branch|branch 上|on branch)'; then
60
+ TRIGGER_FOUND=1
61
+ fi
62
+ fi
63
+
64
+ if [ "$TRIGGER_FOUND" = "1" ]; then
65
+ # User explicitly approved branch work, allow
66
+ exit 0
67
+ fi
68
+
69
+ # BLOCKER: editing production code on main without explicit branch trigger
70
+ cat >&2 <<EOF
71
+ 🚨 MAIN-AS-WORKBENCH BLOCKER(check_main_branch_workbench,2026-05-17 P0 codify)
72
+ - 目標: ${FILE_PATH}
73
+ - 範圍: packages/design-system/src / src/app / src/explorations(production code)
74
+ - 當前 branch: ${CURRENT_BRANCH}(= main / master)
75
+ - 近 5 條 user msg branch-trigger keyword: 0 次
76
+
77
+ → Solo-work canonical(CLAUDE.md \`# Git solo-work canonical\`+ memory/feedback_solo_dev_workflow.md SSOT):
78
+ 1 chat = 1 working branch;production code edit **必先**:
79
+ git checkout -b <working-branch-name>
80
+ 然後 commit + push working branch(觸發 Netlify preview),user trigger「push / 合 main」才 merge main。
81
+
82
+ 修法 — 2 選 1:
83
+ (a) 立刻開 working branch:
84
+ git checkout -b $(date +%Y-%m-%d)-<topic>
85
+ 然後重試 Edit。
86
+ (b) Cite recent user verbatim 含「開 branch / working branch / 在 branch 上」trigger keyword
87
+ OR 設 CLAUDE_BYPASS_MAIN_WORKBENCH=1 跑(audit-logged)。
88
+
89
+ 歷史錨例(2026-05-17 起因):本 session 整個 deep audit + 56-element a11y batch + retire batch 全
90
+ 在 main 直接 edit,user 抓「不是只有我說 push 到 main 才真的會 push 到 main 嗎」。R1-R3 沒抓
91
+ 「main-as-workbench」這個 root cause,故升新 hook 攔。
92
+ EOF
93
+ exit 2
@@ -0,0 +1,165 @@
1
+ #!/bin/bash
2
+ # Naming + abstraction unified hook(2026-05-08 cluster D consolidation)
3
+ #
4
+ # Merges 3 PreToolUse hooks(原各檔已 retire,合併入此):
5
+ # D.1 premature abstraction(原 check_premature_abstraction,P0 BLOCK)
6
+ # D.2 internal namespace consistency(原 check_internal_namespace_consistency,P0 BLOCK)
7
+ # D.3 primitive color var in tsx(原 check_primitive_color_var_in_tsx,P1 WARN stderr)
8
+ #
9
+ # Why merge:皆 命名 / 抽象 / token 消費紀律 invariant,共用 INPUT parsing 模式。
10
+ # 散裝是 M17 + Anthropic ≤ 15 hook best-practice 偏離。
11
+ #
12
+ # Per-rule scope 差異(必保留各自 narrow):
13
+ # D.1: Write only + components/X/X.tsx 或 spec.md 主檔 + 新檔
14
+ # D.2: Edit/Write/MultiEdit + *.stories.tsx
15
+ # D.3: Edit/Write/MultiEdit + *.tsx minus Tag/Avatar/Chart/tokens
16
+ #
17
+ # Per-rule allowlist:
18
+ # D.1: 檔頭 10 行內 `// @separate-component-rationale: <world-class refs + 3-test 通過理由>`
19
+ # D.2: (無 allowlist,但若 file 沒 title namespace 自動 skip)
20
+ # D.3: 行尾 `// @primitive-color-allow: <reason>` OR 檔頭 `// primitive-color-allow-blanket`
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
+ # ── D.1 premature abstraction(Write only,新元件 BLOCK)──────────────────────
47
+ if [ "$TOOL" = "Write" ]; then
48
+ case "$FILE_PATH" in
49
+ */packages/design-system/src/components/*/[^.]*.tsx|*/packages/design-system/src/components/*/*.spec.md)
50
+ if [ ! -f "$FILE_PATH" ]; then
51
+ COMPONENT_DIR=$(echo "$FILE_PATH" | sed -E 's|.*/components/([^/]+)/.*|\1|')
52
+ SUFFIX=""
53
+ BASE_NAME=""
54
+ for SFX in Time Range Color Light Dark Filled Outline Compact Rich Variant; do
55
+ if [[ "$COMPONENT_DIR" =~ ${SFX}$ ]] && [ "$COMPONENT_DIR" != "$SFX" ]; then
56
+ BASE=$(echo "$COMPONENT_DIR" | sed -E "s/${SFX}$//")
57
+ [ -z "$BASE" ] && continue
58
+ COMPONENTS_ROOT=$(echo "$FILE_PATH" | sed -E 's|(.*/components)/.*|\1|')
59
+ if [ -d "$COMPONENTS_ROOT/$BASE" ]; then
60
+ SUFFIX="$SFX"; BASE_NAME="$BASE"; break
61
+ fi
62
+ fi
63
+ done
64
+ if [ -n "$SUFFIX" ]; then
65
+ # Allowlist:檔頭 10 行內 rationale comment
66
+ if ! echo "$NEW_CONTENT" | head -10 | grep -qE '//\s*@separate-component-rationale:|^\s*#?\s*@separate-component-rationale:'; then
67
+ cat >&2 <<EOF
68
+
69
+ ┄┄┄ D.1 check_naming_and_abstraction — premature abstraction BLOCKER ┄┄┄
70
+
71
+ [P0] 新元件 \`${COMPONENT_DIR}\`(後綴 \`${SUFFIX}\`)
72
+ 基底元件 \`${BASE_NAME}\` 已存在 → 強烈 signal 應為 prop variant on \`${BASE_NAME}\`。
73
+
74
+ 歷史(M21):
75
+ - DateTimePicker → \`<DatePicker showTime>\`
76
+ - DataTableFilterPanel → sub-file pattern
77
+
78
+ 3-test 通過才能分:
79
+ 1. \`${BASE_NAME}\` 加 prop 達不到同 DOM/behavior?
80
+ 2. ≥3 家 world-class DS 用分離元件而非 prop?(必 cite source)
81
+ 3. value 結構或 contract 真的不同(如 Range = [start, end])?
82
+
83
+ 通過 → spec.md 加 rationale + 檔頭 10 行內加:
84
+ // @separate-component-rationale: <world-class refs + 3-test 通過理由>
85
+
86
+ EOF
87
+ record_worst 2
88
+ fi
89
+ fi
90
+ fi
91
+ ;;
92
+ esac
93
+ fi
94
+
95
+ # ── D.2 internal namespace consistency(stories sibling check,BLOCK)──────────
96
+ case "$FILE_PATH" in
97
+ *.stories.tsx)
98
+ NEW_NS=$(printf '%s' "$NEW_CONTENT" | grep -oE "title:[[:space:]]*['\"]Design System/(Components|Internal)/" | head -1 | grep -oE "(Components|Internal)" || true)
99
+ if [ -z "$NEW_NS" ] && [ -f "$FILE_PATH" ]; then
100
+ NEW_NS=$(grep -oE "title:[[:space:]]*['\"]Design System/(Components|Internal)/" "$FILE_PATH" 2>/dev/null | head -1 | grep -oE "(Components|Internal)" || true)
101
+ fi
102
+ if [ -n "$NEW_NS" ]; then
103
+ DIR=$(dirname "$FILE_PATH")
104
+ INCONSISTENT=""
105
+ while IFS= read -r SIB; do
106
+ [ "$SIB" = "$FILE_PATH" ] && continue
107
+ [ -f "$SIB" ] || continue
108
+ SIB_NS=$(grep -oE "title:[[:space:]]*['\"]Design System/(Components|Internal)/" "$SIB" 2>/dev/null | head -1 | grep -oE "(Components|Internal)" || true)
109
+ if [ -n "$SIB_NS" ] && [ "$SIB_NS" != "$NEW_NS" ]; then
110
+ INCONSISTENT="${INCONSISTENT} - ${SIB} → ${SIB_NS}"$'\n'
111
+ fi
112
+ done < <(find "$DIR" -maxdepth 1 -name '*.stories.tsx' 2>/dev/null)
113
+ if [ -n "$INCONSISTENT" ]; then
114
+ cat >&2 <<EOF
115
+
116
+ ┄┄┄ D.2 check_naming_and_abstraction — sibling stories namespace BLOCKER ┄┄┄
117
+
118
+ [P0] ${FILE_PATH} → ${NEW_NS}
119
+ Sibling stories 不一致:
120
+ ${INCONSISTENT}
121
+ 3 stories(展示 / anatomy / principles)title namespace 必全 Components/ 或全 Internal/。
122
+
123
+ 決策:跑 CLAUDE.md「Internal vs Components 3-test」→ 把全 3 檔統一。
124
+
125
+ EOF
126
+ record_worst 2
127
+ fi
128
+ fi
129
+ ;;
130
+ esac
131
+
132
+ # ── D.3 primitive color var in tsx(P1 WARN stderr only)──────────────────────
133
+ case "$FILE_PATH" in
134
+ *.tsx)
135
+ case "$FILE_PATH" in
136
+ */components/Tag/*|*/components/Avatar/*|*/components/Chart/*|*/tokens/*) ;; # codified primitive-consumer skip
137
+ *)
138
+ if ! echo "$NEW_CONTENT" | grep -q 'primitive-color-allow-blanket'; then
139
+ VIOLATIONS_D3=$(printf '%s' "$NEW_CONTENT" | grep -nE 'var\(--color-[a-z]+-[0-9](-opaque)?\)' | grep -v 'primitive-color-allow' || true)
140
+ if [ -n "$VIOLATIONS_D3" ]; then
141
+ cat >&2 <<EOF
142
+
143
+ ┄┄┄ D.3 check_naming_and_abstraction — primitive color var WARN ┄┄┄
144
+
145
+ [P1] ${FILE_PATH}
146
+ tsx 內直接消費 primitive token \`var(--color-*-N)\`:
147
+ ${VIOLATIONS_D3}
148
+
149
+ ⚠️ Token 命名 rule 4:禁 primitive 色名作 utility,用 semantic alias。
150
+ 修法 3 擇 1:
151
+ 1. 加 semantic alias 在 tokens/color/semantic.css(consumer 用 semantic)
152
+ 2. 既有 semantic 已存在 → \`var(--border)\` / \`var(--bg-disabled)\`
153
+ 3. 例外(Tag / Avatar / Chart 自動 allowed): \`// @primitive-color-allow: <reason>\` 行尾豁免
154
+
155
+ 詳 tokens/color/color.spec.md「架構流派定位」+「Primitive 色票與 Tag / Avatar 的消費」。
156
+
157
+ EOF
158
+ fi
159
+ fi
160
+ ;;
161
+ esac
162
+ ;;
163
+ esac
164
+
165
+ exit $WORST