@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.
- package/CLAUDE.md +201 -0
- package/README.md +7 -15
- package/cli-init.mjs +90 -0
- package/ds-canonical/commands/README.md +26 -0
- package/ds-canonical/commands/gov-status.md +79 -0
- package/ds-canonical/hooks/README.md +145 -0
- package/ds-canonical/hooks/_log-fire.sh +44 -0
- package/ds-canonical/hooks/block_prototype_imports.py +111 -0
- package/ds-canonical/hooks/check_app_shell_primary_header_consistency.sh +68 -0
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +88 -0
- package/ds-canonical/hooks/check_audit_sample_escape.sh +73 -0
- package/ds-canonical/hooks/check_benchmark_citation.sh +106 -0
- package/ds-canonical/hooks/check_canonical_propagation.sh +189 -0
- package/ds-canonical/hooks/check_chrome_header_handcraft.sh +70 -0
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +83 -0
- package/ds-canonical/hooks/check_codex_collab_5step.sh +108 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +117 -0
- package/ds-canonical/hooks/check_dim_count_drift.sh +72 -0
- package/ds-canonical/hooks/check_field_controls_contracts.sh +110 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +205 -0
- package/ds-canonical/hooks/check_file_size_budget.sh +60 -0
- package/ds-canonical/hooks/check_header_with_tabs_border.sh +87 -0
- package/ds-canonical/hooks/check_main_branch_workbench.sh +93 -0
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +165 -0
- package/ds-canonical/hooks/check_opacity_token_usage.sh +149 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +194 -0
- package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +56 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +53 -0
- package/ds-canonical/hooks/check_propose_plain_chinese.sh +74 -0
- package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +70 -0
- package/ds-canonical/hooks/check_select_all_canonical.sh +58 -0
- package/ds-canonical/hooks/check_solo_workflow.sh +258 -0
- package/ds-canonical/hooks/check_spec_class_drift.sh +88 -0
- package/ds-canonical/hooks/check_story_invariants.sh +612 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +105 -0
- package/ds-canonical/hooks/check_tab_lg_chrome_header_equal.sh +66 -0
- package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +104 -0
- package/ds-canonical/hooks/enforce_home_charter.sh +44 -0
- package/ds-canonical/hooks/inject_pending_self_audit.sh +204 -0
- package/ds-canonical/hooks/lib/_approval_re.sh +33 -0
- package/ds-canonical/hooks/lib/_code_quality.sh +73 -0
- package/ds-canonical/hooks/lib/_cva_default_sync.sh +69 -0
- package/ds-canonical/hooks/lib/_governance_coverage_check.sh +49 -0
- package/ds-canonical/hooks/lib/_hardcoded_strings.sh +163 -0
- package/ds-canonical/hooks/lib/_layout_space_canonical.sh +56 -0
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +141 -0
- package/ds-canonical/hooks/lib/_person_data_richness.sh +42 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +48 -0
- package/ds-canonical/hooks/lib/_token_hygiene.sh +95 -0
- package/ds-canonical/hooks/log_governance_fires.sh +50 -0
- package/ds-canonical/hooks/log_skill_invokes.sh +41 -0
- package/ds-canonical/hooks/post_edit_dispatcher.sh +62 -0
- package/ds-canonical/hooks/retired/check_anatomy_section_numbering.sh +106 -0
- package/ds-canonical/hooks/retired/check_avatar_hovercard.sh +90 -0
- package/ds-canonical/hooks/retired/check_button_icon_literal.sh.retired-2026-04-28 +38 -0
- package/ds-canonical/hooks/retired/check_container_breathing.sh +142 -0
- package/ds-canonical/hooks/retired/check_governance_compliance.sh +61 -0
- package/ds-canonical/hooks/retired/check_icon_only_padding_formula.sh +104 -0
- package/ds-canonical/hooks/retired/check_item_content_primitive.sh +150 -0
- package/ds-canonical/hooks/retired/check_item_list_gap.sh +153 -0
- package/ds-canonical/hooks/retired/check_sideoffset_canonical.sh +65 -0
- package/ds-canonical/hooks/retired/check_spec_iteration_tag.sh +87 -0
- package/ds-canonical/hooks/retired/check_ssot_consultation.sh +88 -0
- package/ds-canonical/hooks/retired/check_sync_update.sh +20 -0
- package/ds-canonical/hooks/retired/check_third_party_dom_verified.sh +95 -0
- package/ds-canonical/hooks/retired/enforce_home_charter.sh +125 -0
- package/ds-canonical/hooks/retired/post_edit_canonical_interrogate.sh +109 -0
- package/ds-canonical/hooks/retired/pre_edit_spec_check.sh +68 -0
- package/ds-canonical/hooks/retired/pre_new_component_spec.sh +39 -0
- package/ds-canonical/hooks/retired/pre_write_subsumption_check.sh +112 -0
- package/ds-canonical/hooks/retired/stop_meta_self_audit.sh.retired-2026-05-13 +76 -0
- package/ds-canonical/hooks/retired/tests/test_check_anatomy_section_numbering.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_avatar_hovercard.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_container_breathing.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_governance_compliance.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_icon_only_padding_formula.sh +79 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_content_primitive.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_list_gap.sh +163 -0
- package/ds-canonical/hooks/retired/tests/test_check_sideoffset_canonical.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_spec_iteration_tag.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_ssot_consultation.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_sync_update.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_third_party_dom_verified.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_enforce_home_charter.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_edit_spec_check.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_new_component_spec.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_write_subsumption_check.sh +63 -0
- package/ds-canonical/hooks/session_start_governance_check.sh +263 -0
- package/ds-canonical/hooks/stop_passive_logging.sh +322 -0
- package/ds-canonical/hooks/stop_self_audit.sh +450 -0
- package/ds-canonical/hooks/tests/KNOWN-BROKEN.md +15 -0
- package/ds-canonical/hooks/tests/run-all.sh +76 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +143 -0
- package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +140 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +93 -0
- package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +133 -0
- package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +123 -0
- package/ds-canonical/hooks/tests/test_check_code_quality.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +96 -0
- package/ds-canonical/hooks/tests/test_check_cva_default_sync.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +122 -0
- package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +98 -0
- package/ds-canonical/hooks/tests/test_check_field_controls_contracts.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +194 -0
- package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +32 -0
- package/ds-canonical/hooks/tests/test_check_hardcoded_strings.sh +14 -0
- package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_layout_space_canonical.sh +73 -0
- package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +147 -0
- package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +136 -0
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +148 -0
- package/ds-canonical/hooks/tests/test_check_peoplepicker_ssot_drift.sh +108 -0
- package/ds-canonical/hooks/tests/test_check_person_data_richness.sh +58 -0
- package/ds-canonical/hooks/tests/test_check_pixel_quantified_audit.sh +142 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +117 -0
- package/ds-canonical/hooks/tests/test_check_select_all_canonical.sh +125 -0
- package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +201 -0
- package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +135 -0
- package/ds-canonical/hooks/tests/test_check_story_anatomy.sh.broken +197 -0
- package/ds-canonical/hooks/tests/test_check_story_category.sh.broken +187 -0
- package/ds-canonical/hooks/tests/test_check_story_compile_drift.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +209 -0
- package/ds-canonical/hooks/tests/test_check_story_name_jargon.sh.broken +53 -0
- package/ds-canonical/hooks/tests/test_check_story_slot_split.sh +156 -0
- package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +176 -0
- package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +138 -0
- package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +21 -0
- package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +169 -0
- package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +77 -0
- package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +125 -0
- package/ds-canonical/hooks/tests/test_log_governance_fires.sh +10 -0
- package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +7 -0
- package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +108 -0
- package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +143 -0
- package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +95 -0
- package/ds-canonical/hooks/tests/test_stop_governance_drift_check.sh.broken +125 -0
- package/ds-canonical/hooks/tests/test_stop_harvest_corrections.sh +10 -0
- package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +100 -0
- package/ds-canonical/hooks/tests/test_stop_self_audit.sh +76 -0
- package/ds-canonical/hooks/tests/test_stop_tsc_sanity.sh +10 -0
- package/ds-canonical/references/README.md +43 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +74 -0
- package/ds-canonical/references/build-ui-canonicals.md +69 -0
- package/ds-canonical/references/cva-patterns.md +41 -0
- package/ds-canonical/references/drag-canonical.md +331 -0
- package/ds-canonical/references/item-anatomy-recipe.md +225 -0
- package/ds-canonical/references/naming-conventions.md +56 -0
- package/ds-canonical/references/principle-dim-map.json +515 -0
- package/ds-canonical/references/props-naming.md +45 -0
- package/ds-canonical/references/spec-rules.md +58 -0
- package/ds-canonical/references/ssot-consultation.md +63 -0
- package/ds-canonical/references/ssot-index.md +40 -0
- package/ds-canonical/references/story-baseline-registry.json +79 -0
- package/ds-canonical/references/structural-token-retention.md +42 -0
- package/ds-canonical/references/tailwind-gotchas.md +87 -0
- package/ds-canonical/references/ui-dev-rules.md +60 -0
- package/ds-canonical/rules/README.md +34 -0
- package/ds-canonical/rules/meta-patterns.md +87 -0
- package/ds-canonical/rules/self-verify.md +53 -0
- package/ds-canonical/rules/spec-rules.md +25 -0
- package/ds-canonical/rules/story-rules.md +56 -0
- package/ds-canonical/rules/ui-development.md +87 -0
- package/ds-canonical/skills/README.md +88 -0
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +181 -0
- package/ds-canonical/skills/code-quality-audit/SKILL.md +63 -0
- package/ds-canonical/skills/codex-collab/SKILL.md +249 -0
- package/ds-canonical/skills/codex-collab/references/brief-template.md +48 -0
- package/ds-canonical/skills/codex-collab/references/transport.md +58 -0
- package/ds-canonical/skills/codify-corrections/SKILL.md +184 -0
- package/ds-canonical/skills/codify-principle/SKILL.md +151 -0
- package/ds-canonical/skills/component-quality-gate/SKILL.md +102 -0
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +79 -0
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +247 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +123 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +165 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +91 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +229 -0
- package/ds-canonical/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +343 -0
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1260 -0
- package/ds-canonical/skills/design-system-audit/references/checkpoints.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +364 -0
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +175 -0
- package/ds-canonical/skills/design-system-audit/references/spec-template.md +66 -0
- package/ds-canonical/skills/ensure-canonical/SKILL.md +196 -0
- package/ds-canonical/skills/governance-health/SKILL.md +146 -0
- package/ds-canonical/skills/knowledge-prune/SKILL.md +303 -0
- package/ds-canonical/skills/new-component/SKILL.md +170 -0
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +85 -0
- package/ds-canonical/skills/performance-audit/SKILL.md +107 -0
- package/ds-canonical/skills/product-ui-audit/SKILL.md +230 -0
- package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +246 -0
- package/ds-canonical/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/skills/product-ui-audit/references/report-template.md +159 -0
- package/ds-canonical/skills/propose-options/SKILL.md +177 -0
- package/ds-canonical/skills/prototype/SKILL.md +244 -0
- package/ds-canonical/skills/prototype/references/audit-checks.md +37 -0
- package/ds-canonical/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/skills/prototype/references/checkpoints.md +191 -0
- package/ds-canonical/skills/prototype/references/evaluation-matrix.md +141 -0
- package/ds-canonical/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/skills/prototype/references/proposal-template.md +229 -0
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +198 -0
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +159 -0
- package/ds-canonical/skills/story-writing/SKILL.md +122 -0
- package/ds-canonical/skills/story-writing/references/anatomy-standard.md +217 -0
- package/ds-canonical/skills/story-writing/references/category-templates.md +174 -0
- package/ds-canonical/skills/story-writing/references/example-selection.md +70 -0
- package/ds-canonical/skills/story-writing/references/self-check.md +20 -0
- package/ds-canonical/skills/ux-audit/SKILL.md +130 -0
- package/ds-canonical/skills/visual-audit/SKILL.md +245 -0
- package/ds-canonical/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/skills/visual-audit/references/audit-architecture.md +100 -0
- package/ds-canonical/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/package.json +9 -5
- package/src/components/Accordion/accordion.spec.md +114 -0
- package/src/components/Alert/alert.spec.md +197 -0
- package/src/components/AppShell/app-shell.spec.md +331 -0
- package/src/components/AspectRatio/aspect-ratio.spec.md +134 -0
- package/src/components/Avatar/avatar.spec.md +329 -0
- package/src/components/Badge/badge.spec.md +380 -0
- package/src/components/Breadcrumb/breadcrumb.spec.md +257 -0
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +210 -0
- package/src/components/Button/button.spec.md +460 -0
- package/src/components/Calendar/calendar.spec.md +242 -0
- package/src/components/Carousel/carousel.spec.md +253 -0
- package/src/components/Chart/chart.spec.md +155 -0
- package/src/components/Checkbox/checkbox.spec.md +344 -0
- package/src/components/Chip/chip.spec.md +237 -0
- package/src/components/CircularProgress/circular-progress.spec.md +268 -0
- package/src/components/Coachmark/coachmark.spec.md +230 -0
- package/src/components/Combobox/combobox.spec.md +180 -0
- package/src/components/Command/command.spec.md +171 -0
- package/src/components/DataTable/data-table.spec.md +525 -0
- package/src/components/DateGrid/date-grid.spec.md +215 -0
- package/src/components/DatePicker/date-picker.spec.md +334 -0
- package/src/components/DescriptionList/description-list.spec.md +214 -0
- package/src/components/Dialog/dialog.spec.md +202 -0
- package/src/components/DropdownMenu/dropdown-menu.spec.md +250 -0
- package/src/components/Empty/empty.spec.md +214 -0
- package/src/components/Field/field-controls.spec.md +338 -0
- package/src/components/Field/field.spec.md +438 -0
- package/src/components/Field/form-validation.spec.md +152 -0
- package/src/components/FieldControlGroup/field-control-group.spec.md +176 -0
- package/src/components/FileItem/file-item.spec.md +467 -0
- package/src/components/FileUpload/file-upload.spec.md +123 -0
- package/src/components/FileViewer/file-viewer.spec.md +373 -0
- package/src/components/HoverCard/hover-card.spec.md +157 -0
- package/src/components/Input/input.spec.md +193 -0
- package/src/components/LinkInput/link-input.spec.md +130 -0
- package/src/components/Menu/menu-item.spec.md +290 -0
- package/src/components/NameCard/name-card.spec.md +171 -0
- package/src/components/Notice/notice.spec.md +149 -0
- package/src/components/NumberInput/number-input.spec.md +126 -0
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +120 -0
- package/src/components/PeoplePicker/people-picker.spec.md +263 -0
- package/src/components/Popover/popover.spec.md +198 -0
- package/src/components/ProgressBar/progress-bar.spec.md +232 -0
- package/src/components/RadioGroup/radio-group.spec.md +141 -0
- package/src/components/Rating/rating.spec.md +208 -0
- package/src/components/ScrollArea/scroll-area.spec.md +145 -0
- package/src/components/SegmentedControl/segmented-control.spec.md +295 -0
- package/src/components/Select/select.spec.md +299 -0
- package/src/components/SelectMenu/select-menu.spec.md +220 -0
- package/src/components/SelectionControl/selection-item.spec.md +128 -0
- package/src/components/Separator/separator.spec.md +109 -0
- package/src/components/Sheet/sheet.spec.md +148 -0
- package/src/components/Sidebar/sidebar.spec.md +713 -0
- package/src/components/Skeleton/skeleton.spec.md +104 -0
- package/src/components/Slider/slider.spec.md +353 -0
- package/src/components/Steps/steps.spec.md +465 -0
- package/src/components/Switch/switch.spec.md +215 -0
- package/src/components/Tabs/tabs.spec.md +314 -0
- package/src/components/Tag/tag.spec.md +282 -0
- package/src/components/Textarea/textarea.spec.md +151 -0
- package/src/components/TimePicker/time-picker.spec.md +279 -0
- package/src/components/Toast/toast.spec.md +177 -0
- package/src/components/Tooltip/tooltip.spec.md +139 -0
- package/src/components/TreeView/tree-view.spec.md +374 -0
- package/src/patterns/action-bar/action-bar.spec.md +458 -0
- package/src/patterns/element-anatomy/element-anatomy.spec.md +215 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +315 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +1042 -0
- package/src/patterns/header-canonical/header-canonical.spec.md +285 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +191 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +428 -0
- package/src/patterns/resize-handle/resize-handle.spec.md +109 -0
- package/src/tokens/color/color.spec.md +804 -0
- package/src/tokens/density/density.spec.md +127 -0
- package/src/tokens/elevation/elevation.spec.md +81 -0
- package/src/tokens/layoutSpace/layoutSpace.spec.md +314 -0
- package/src/tokens/motion/motion.spec.md +97 -0
- package/src/tokens/opacity/opacity.spec.md +78 -0
- package/src/tokens/orphan-tokens.spec.md +117 -0
- package/src/tokens/radius/radius.spec.md +123 -0
- package/src/tokens/typography/typography.spec.md +202 -0
- package/src/tokens/uiSize/uiSize.spec.md +438 -0
- 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
|