@qijenchen/design-system 0.1.0-beta.82 → 0.1.0-beta.83

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 (36) hide show
  1. package/CLAUDE.md +2 -2
  2. package/ds-canonical/fork/governance.lock +14 -8
  3. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +15 -4
  4. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +8 -0
  5. package/ds-canonical/fork/hooks/check_story_determinism.sh +37 -0
  6. package/ds-canonical/fork/manifest.json +5 -0
  7. package/ds-canonical/fork/preamble.md +3 -1
  8. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +6 -0
  9. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +7 -0
  10. package/ds-canonical/fork/skills/performance-audit/SKILL.md +4 -2
  11. package/ds-canonical/fork/skills/visual-audit/SKILL.md +8 -0
  12. package/ds-canonical/hooks/check_benchmark_citation.sh +5 -3
  13. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +15 -4
  14. package/ds-canonical/hooks/check_opacity_token_usage.sh +8 -0
  15. package/ds-canonical/hooks/check_propose_without_benchmark.sh +6 -2
  16. package/ds-canonical/hooks/check_ssot_header_declaration.sh +53 -0
  17. package/ds-canonical/hooks/check_story_determinism.sh +37 -0
  18. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +3 -3
  19. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -1
  20. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +2 -2
  21. package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -0
  22. package/ds-canonical/rules/self-verify.md +1 -1
  23. package/ds-canonical/rules/spec-rules.md +1 -1
  24. package/ds-canonical/rules/ui-development.md +2 -0
  25. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +6 -0
  26. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +11 -15
  27. package/ds-canonical/skills/delivery-handoff/SKILL.md +5 -0
  28. package/ds-canonical/skills/design-system-audit/SKILL.md +1 -1
  29. package/ds-canonical/skills/governance-health/SKILL.md +1 -0
  30. package/ds-canonical/skills/new-component/SKILL.md +2 -0
  31. package/ds-canonical/skills/performance-audit/SKILL.md +2 -2
  32. package/ds-canonical/skills/visual-audit/SKILL.md +8 -0
  33. package/llms-full.txt +1 -1
  34. package/llms.txt +1 -1
  35. package/package.json +1 -1
  36. package/src/components/FileItem/file-item.spec.md +9 -0
package/CLAUDE.md CHANGED
@@ -32,7 +32,7 @@
32
32
 
33
33
  ## 行數預算(Anthropic 對齊)
34
34
 
35
- CLAUDE.md target ≤ 200(Anthropic best-practice)/ transition ≤ 400 / hard cap 800。SKILL ≤ 250 / spec ≤ 300(foundational SSOT 例外 ≤ 800-1200)/ memory **per-file ≤ 100 lines** + **MEMORY.md index ≤ 20 entries**(soft 18 / hard 20,session-start hook 攔)。Hooks **26 soft / 60 hard**(SSOT = `session_start_governance_check.sh` Check 7 threshold logic,2026-05-27 升 50→55→60 per codex M31 P0 hooks + baseline + primitive-misuse 3 new hooks)。動態值見 `scripts/sync-governance-counters.mjs` 跑出為準(snapshot 2026-07-02:**31 M-rules / 90 audit dims / 53 hooks** — 數字僅供 sanity check,真值以 script 輸出為準避 drift)。
35
+ CLAUDE.md target ≤ 200(Anthropic best-practice)/ transition ≤ 400 / hard cap 800。SKILL ≤ 250 / spec ≤ 300(foundational SSOT 例外 ≤ 800-1200)/ memory **per-file ≤ 100 lines** + **MEMORY.md index ≤ 20 entries**(soft 18 / hard 20,session-start hook 攔)。Hooks **26 soft / 60 hard**(SSOT = `session_start_governance_check.sh` Check 7 threshold logic,2026-05-27 升 50→55→60 per codex M31 P0 hooks + baseline + primitive-misuse 3 new hooks)。動態值見 `scripts/sync-governance-counters.mjs` 跑出為準(snapshot 2026-07-07:**31 M-rules / 90 audit dims / 55 hooks** — 數字僅供 sanity check,真值以 script 輸出為準避 drift)。
36
36
 
37
37
  ## Anti-bloat L1-L3
38
38
 
@@ -73,7 +73,7 @@ CLAUDE.md target ≤ 200(Anthropic best-practice)/ transition ≤ 400 / hard cap
73
73
 
74
74
  # SSOT 消費 canonical
75
75
 
76
- 寫視覺 code 前必查對照 — 沒列 = 自創。**完整對照表 + 強制 checklist** → `.claude/references/ssot-consultation.md`(SSOT owner;含 9 項決策對應 SSOT + 新元件 tsx 開頭「── 消費的 SSOT ──」段強制要求)。Hook `check_ssot_consultation.sh` 2026-05-XX retired 改靠 mindset #2 + audit dim 1 + check_canonical_propagation hook 接手檢查。
76
+ 寫視覺 code 前必查對照 — 沒列 = 自創。**完整對照表 + 強制 checklist** → `.claude/references/ssot-consultation.md`(SSOT owner;含 9 項決策對應 SSOT + 新元件 tsx 開頭「── 消費的 SSOT ──」段強制要求)。原 `check_ssot_consultation.sh` 已 retired;2026-07-07 `check_ssot_header_declaration.sh` 精簡復活(新建 production tsx 必帶「── 消費的 SSOT ──」段,P0)+ mindset #2 + audit dim 1 + check_canonical_propagation 接手。
77
77
 
78
78
  # 任務導航表
79
79
 
@@ -21,7 +21,7 @@
21
21
  },
22
22
  {
23
23
  "file": "hooks/check_ds_anchor_preflight.sh",
24
- "sha256": "15fb44c8b35042efd03e1992f8b5b7b121a7633cebe556d4ca0a1a7f5483dc35",
24
+ "sha256": "80b9f1be3140da0623b61c00d6f6b5c5f4bdbf3307e2f081ba8f6fc43cad0b52",
25
25
  "sourceHook": "check_ds_anchor_preflight.sh",
26
26
  "bucket": "SHIP_REWRITTEN"
27
27
  },
@@ -57,7 +57,7 @@
57
57
  },
58
58
  {
59
59
  "file": "hooks/check_opacity_token_usage.sh",
60
- "sha256": "f085fdf2b681f6ad20a51863ca580f4c56633091f0b044e05cf1b9ef665fdf0d",
60
+ "sha256": "93123d9244264b821d6d31357c3868d343d7a96182664e02226cccbb8a29d18e",
61
61
  "sourceHook": "check_opacity_token_usage.sh",
62
62
  "bucket": "SHIP_REWRITTEN"
63
63
  },
@@ -73,6 +73,12 @@
73
73
  "sourceHook": "check_sidebar_menu_button_implicit_wrap.sh",
74
74
  "bucket": "SHIP_AS_IS"
75
75
  },
76
+ {
77
+ "file": "hooks/check_story_determinism.sh",
78
+ "sha256": "34b56718a1f15b492c410d199a883d561afd4c51d7445994b84c2e6180ba3d4e",
79
+ "sourceHook": "check_story_determinism.sh",
80
+ "bucket": "SHIP_AS_IS"
81
+ },
76
82
  {
77
83
  "file": "hooks/check_tabs_content_chrome_body_double_gap.sh",
78
84
  "sha256": "656e854fc8c3e61afd7da3705f87d169a2ea04979e446ed453114b42b9bdcfb1",
@@ -108,15 +114,15 @@
108
114
  },
109
115
  {
110
116
  "file": "manifest.json",
111
- "sha256": "588eb31f9ad3c302217e7642b053cb8b4b81dcd2dd1c69959826f6d2317102ed"
117
+ "sha256": "5a8072bafa1d777fe4943f2f21130304101a4a6179270403fae205c4156d583b"
112
118
  },
113
119
  {
114
120
  "file": "preamble.md",
115
- "sha256": "c6333d8a6127d4e0d9b9511e16efaace2d215638b3bd235b31a8b29ef8827d5c"
121
+ "sha256": "3bb47d3c88bcdeba13bc61ac6275799c9208c3001771e3652303e49a97fb0604"
116
122
  },
117
123
  {
118
124
  "file": "skills/bug-fix-rhythm/SKILL.md",
119
- "sha256": "4bca87df4f2777fe76f8e302698d373fb0ba60699922fb8b35c8aefae1c51ae0"
125
+ "sha256": "b5cc2fa5b50b533c06583b64d7eaf4efc62d03d3b9088f6a58cd0b796d78c2e2"
120
126
  },
121
127
  {
122
128
  "file": "skills/code-quality-audit/SKILL.md",
@@ -136,11 +142,11 @@
136
142
  },
137
143
  {
138
144
  "file": "skills/delivery-handoff/SKILL.md",
139
- "sha256": "b3ba781857bed71c1bff537519b1e3858e5f115f25fb7c55967e5b147d360f79"
145
+ "sha256": "9e5c93441f6b56f64d1db46d4b46dc0015a7dd514af952d7e1926087249eaa23"
140
146
  },
141
147
  {
142
148
  "file": "skills/performance-audit/SKILL.md",
143
- "sha256": "7a77f4999b8d4bb6875c105050196fc3d7b451713da791531d186c37e593020b"
149
+ "sha256": "71b63f44f90fa0bfeb1c63e589757bdb917f2d57ea9cf754c9c1b47808716061"
144
150
  },
145
151
  {
146
152
  "file": "skills/product-ui-audit/references/audit-checks.md",
@@ -216,7 +222,7 @@
216
222
  },
217
223
  {
218
224
  "file": "skills/visual-audit/SKILL.md",
219
- "sha256": "0af4957c5ab08f8c04bbc944900ae45b719eed879e667de1fd139392e57c4525"
225
+ "sha256": "0e4193dfb3a24609795a3d6a19f0e9d06da06c671a74d2db1afefa21d869ba2d"
220
226
  }
221
227
  ]
222
228
  }
@@ -55,8 +55,19 @@ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_stri
55
55
  # DS primitive 名單(wrap 這些就觸發 anchor preflight)
56
56
  DS_PRIMITIVES_RE='<(Sidebar|AppShell|DataTable|Dialog|Sheet|Popover|DropdownMenu|Field|FieldControlGroup|MenuItem|ItemAvatar|ItemLabel|ItemIcon|SegmentedControl|Tabs|TabsList|TabsTrigger|Combobox|Select|DatePicker|TimePicker|TreeView|Tooltip|Coachmark|FileViewer|ScrollArea|Avatar|Badge|Button|ChromeHeader|SurfaceHeader|SurfaceBody|SurfaceFooter|OverlaySurface|NameCard|Toast|FileUpload|DescriptionList|Chart|BulkActionBar|ActionBar|Carousel|Breadcrumb)\b'
57
57
 
58
- # 沒 wrap DS primitive → 跳過(可能是純 utility / hook code)
58
+ # 沒 wrap DS primitive → 檢「純手刻」洞(2026-07-07 治理進化方向 3 b:原本此分支直接放行,
59
+ # 「該用 primitive 卻整個手刻」反而抓不到 — 內部盤點 file:line 證據見 planning/2026-07-07-governance-evolution-roadmap.md)
59
60
  if ! echo "$NEW_CONTENT" | grep -qE "$DS_PRIMITIVES_RE"; then
61
+ # 視覺簽名:row/浮層/chrome 手刻特徵(flex row + padding/border/rounded/shadow 組合)
62
+ HANDCRAFT_SIG=$(echo "$NEW_CONTENT" | grep -cE 'className="[^"]*\b(flex|absolute|fixed)\b[^"]*\b(border|rounded|shadow|px-|py-|gap-)' || true)
63
+ if [ "${HANDCRAFT_SIG:-0}" -ge 3 ] && ! echo "$NEW_CONTENT" | grep -q '@handcraft-ok:'; then
64
+ cat >&2 <<'EOF_HC'
65
+ ⚠️ [第一期 WARN] M23(d)/mindset #2 純手刻嫌疑:本段 production tsx 有 ≥3 處視覺結構 className
66
+ 但零 DS primitive 消費。先查 patterns/(item-anatomy / overlay-surface / ChromeHeader /
67
+ horizontal-overflow)有無現成 primitive;真需自建 → 行內 `@handcraft-ok: <rationale>` +
68
+ spec「自建 + 理由」宣告。誤殺率驗證期後升 P0(roadmap 方向 3)。
69
+ EOF_HC
70
+ fi
60
71
  exit 0
61
72
  fi
62
73
 
@@ -127,6 +138,6 @@ cat >&2 <<EOF
127
138
  - \`.claude/references/ssot-index.md\` Step 0.1 high-risk interface owner mapping
128
139
  EOF
129
140
 
130
- # Soft warn (exit 0):inject context AI 自決,Stop hook backstop。
131
- # 對齊 check_substantive_edit_approval_preflight.sh hybrid pattern(soft pre + hard stop)。
132
- exit 0
141
+ # P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27「SSOT canonical P0 禁 soft」
142
+ # doctrine;bypass env CLAUDE_BYPASS_DS_ANCHOR=1 已備且 audit-logged,升級不增誤殺)。
143
+ exit 2
@@ -46,6 +46,10 @@ esac
46
46
 
47
47
  NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""')
48
48
  [ -z "$NEW_CONTENT" ] && exit 0
49
+ # Per-line escape(2026-07-07 方向 2 升 P0 配套):行內含 `@token-registry-ok: <rationale>` 豁免該行
50
+ NEW_CONTENT=$(printf '%s
51
+ ' "$NEW_CONTENT" | grep -v '@token-registry-ok:' || true)
52
+ [ -z "$NEW_CONTENT" ] && exit 0
49
53
 
50
54
  # Resolve registry path(absolute via $CLAUDE_PROJECT_DIR or relative fallback)
51
55
  REGISTRY="${CLAUDE_PROJECT_DIR:-}/node_modules/@qijenchen/design-system/src/tokens/utility-registry.json"
@@ -152,7 +156,11 @@ EOF_HEAD
152
156
  bg-neutral-N → semantic utility(bg-surface 等)或 bg-[var(--color-neutral-N)]
153
157
 
154
158
  詳 utility-registry.json `_meta.spec_sources` + M23「DS 內既有 canonical 優先」。
159
+ Per-line escape:行尾註解 `@token-registry-ok: <rationale>`(audit 可追)。
155
160
  EOF_BODY
161
+ # P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27「SSOT canonical 必 P0」doctrine;
162
+ # 檔級豁免(anatomy/principles stories / token 自家)+ 行級 escape 已備,升級不增誤殺)
163
+ exit 2
156
164
  fi
157
165
 
158
166
  exit 0
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+ # check_story_determinism.sh — stories 寫入時 determinism 哨(2026-07-07 治理進化軌道 7)
3
+ #
4
+ # Why:story 用真實時間/亂數 → VR baseline 換日假 breach(anchor:2026-07-07 calendar 三 scenario
5
+ # 0.07–0.51% 週期性假紅,三修才根治:story 釘日期 → 元件內部時間 → runner 偏移時鐘)。
6
+ # runner 層已有偏移時鐘兜底(scripts/visual-audit.mjs),本 hook = 寫入層提前攔,
7
+ # 讓 determinism 在出生點就成立(非 VR 撞到才修)。
8
+ #
9
+ # 偵測(新寫入內容):裸 `new Date()`(無參數)/ `Date.now()` / `Math.random()`。
10
+ # escape:行內 `@nondeterministic-ok: <rationale>`(如「即時鐘 demo,VR 不截」)。
11
+ # 第一期 WARN(exit 0):存量 10 檔(dry-run 2026-07-07,DateGrid 家族為主)未清,
12
+ # 誤殺率驗證期後升 P0(對齊 handcraft 偵測同節奏;roadmap 方向 1 KPI 隨頻回顧)。
13
+
14
+ set -euo pipefail
15
+ INPUT=$(cat)
16
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
17
+ case "$TOOL" in Edit|Write|MultiEdit) ;; *) exit 0 ;; esac
18
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
19
+ case "$FILE_PATH" in *.stories.tsx) ;; *) exit 0 ;; esac
20
+
21
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
22
+ (.tool_input.content // "") + "\n" +
23
+ (.tool_input.new_string // "") + "\n" +
24
+ ([.tool_input.edits[]? | .new_string] | join("\n"))
25
+ ' 2>/dev/null || echo "")
26
+ [ -z "${NEW_CONTENT//[[:space:]]/}" ] && exit 0
27
+
28
+ HITS=$(printf '%s\n' "$NEW_CONTENT" | grep -v '@nondeterministic-ok:' | grep -nE 'new Date\(\)|Date\.now\(\)|Math\.random\(\)' || true)
29
+ [ -z "$HITS" ] && exit 0
30
+
31
+ cat >&2 <<EOF
32
+ ⚠️ [第一期 WARN] story 非決定性時間/亂數(VR baseline 換日假 breach 根因):
33
+ $HITS
34
+ → 釘固定值(new Date(2026, 6, 15) / 常數 seed);真需即時值 → 行內 \`@nondeterministic-ok: <rationale>\`。
35
+ Anchor:2026-07-07 calendar VR 三修;runner 偏移時鐘為兜底非藉口。roadmap 方向 1(planning/2026-07-07-governance-evolution-roadmap.md)。
36
+ EOF
37
+ exit 0
@@ -64,6 +64,11 @@
64
64
  "sourceHook": "check_chrome_header_avatar_canonical.sh",
65
65
  "bucket": "SHIP_AS_IS"
66
66
  },
67
+ {
68
+ "file": "hooks/check_story_determinism.sh",
69
+ "sourceHook": "check_story_determinism.sh",
70
+ "bucket": "SHIP_AS_IS"
71
+ },
67
72
  {
68
73
  "file": "hooks/check_opacity_token_usage.sh",
69
74
  "sourceHook": "check_opacity_token_usage.sh",
@@ -198,6 +198,8 @@ paths:
198
198
 
199
199
  **元件不得自包全域 Provider**(Tooltip / Theme / Toast / Portal)— 由應用層統一設定。**判斷**:Context 是行為狀態(open / size)→ 可包;全域外觀配置(delay / theme / portal / variant defaults)→ 禁止。
200
200
 
201
+ **shadcn 目錄後續新增元件 vs DS 既有(2026-07-07 codify,anchor:Attachment vs FileItem)**:「遵循 shadcn 框架」= 結構 idiom(forwardRef / Slot / data-* / cva),**非**「必須消費目錄每個元件」。shadcn 是 copy-in scaffold 非 runtime 依賴——遷移無上游更新流入。目錄新增元件時二分:(a) 帶 primitive / behavior 增益(Radix core、新 interaction model)且 DS 無等價 → 走 `/new-component` 近親評估;(b) **純組合式且 DS 已有 Family-compliant 等價物 → 不換架構、不改名**(M23 禁外部覆蓋既有 canonical),只做 API 對照:值得的 state / pattern 逐項評估為 DS prop 演進(SSOT-affecting → ASK),該元件 spec 補 M22 benchmark 對照行 + known-gap 記錄。同類先例:Empty / Item(≈item-anatomy)/ Field / Spinner(≈CircularProgress)/ InputGroup(≈FieldControlGroup)。
202
+
201
203
 
202
204
  ---
203
205
  ## rules/story-rules.md
@@ -310,7 +312,7 @@ paths:
310
312
  - **Pre-edit**:`check_substantive_edit_approval_preflight.sh`(production code)+ `stop_self_audit.sh`(spec/canonical 補位)+ `check_ds_anchor_preflight.sh`(M29 anchor)
311
313
  - **Post-edit**:`stop_self_audit.sh` Mechanism 1(claim-verify-gap)BLOCKER
312
314
  - **Pre-final(宣告完成前)**:`stop_self_audit.sh` Mechanism 7(完整性宣告閘)BLOCKER — 宣告「全做完 / 全部完成」+ 本 turn 實質改動但**無全庫 stale-ref 掃描證據** → block。**觸發器 = 「宣告完成」本身,非等 user 問第二次**(2026-06-03 user-authorized,根治重複 failure)
313
- - **Pre-final(重大 / SSOT / 模型 / 跨多檔改動)**:除 M7 自掃外,**宣告完成前必跑「獨立對抗稽核」**(multi-agent Workflow,每路假設「還有 loose end」主動去找 + cite 證據)。**理由**:self-grep 系統性漏(self-assessment unreliable,對齊 `feedback_ai_ground_truth_unreliable_mechanical_primary`)+ 信任機械閘(preflight / R4 / hook BLOCKER)勝於自評。**小改 = M7 自掃即可**,不需對抗稽核(避免過度)。2026-06-03 user-authorized,根治「宣告做完 → user 問第 N 次 → 才補掃出 loose end」
315
+ - **Pre-final(中型以上 = ≥3 substantive、任何 canonical/SSOT/模型改動、或宣告「殘項/債歸零」類完整性 claim;2026-07-07 軌道 4 收緊,Workflow 成本已低無理由跳)**:除 M7 自掃外,**宣告完成前必跑「獨立對抗稽核」**(multi-agent Workflow,每路假設「還有 loose end」主動去找 + cite 證據)。**理由**:self-grep 系統性漏(self-assessment unreliable,對齊 `feedback_ai_ground_truth_unreliable_mechanical_primary`)+ 信任機械閘(preflight / R4 / hook BLOCKER)勝於自評。**小改 = M7 自掃即可**,不需對抗稽核(避免過度)。2026-06-03 user-authorized,根治「宣告做完 → user 問第 N 次 → 才補掃出 loose end」
314
316
  - **Pre-commit**:`scripts/audit-content-quality.mjs --check` + `scripts/extract-canonical-rules.mjs` 各 fail = block
315
317
 
316
318
  ## Anti-pattern(永久 ban)
@@ -57,6 +57,12 @@ description: Batch-end-verify rhythm + parallel tool batch + user-listed N-rule
57
57
  - 漏 N 條中第 K 條當「做完」report
58
58
  - 改寫 user wording 為自己 paraphrase(verbatim 保留)
59
59
 
60
+ **Sweep-first, edit-second(2026-07-07 治理進化軌道 2 codify)**:任一 fix 屬「跨切面決策」
61
+ (改名 / 詞彙統一 / 規則性改動,可寫成 grep 謂詞者)→ **先跑謂詞產出「sweep manifest」**
62
+ (grep 指令 + 全庫命中清單,artifact 化),照清單做完、驗證清單歸零,才算該 fix 完成。
63
+ **禁**「先改直覺想到的檔、稽核抓漏再補」(anchor:2026-07-07 meta 詞彙統一三波 11→16→9 檔,
64
+ 規則在散文裡 → 三輪才收斂;寫成謂詞後一個 grep 掃平)。
65
+
60
66
  ### Phase 1 — Parallel tool batch(原 M32(d))
61
67
 
62
68
  Read / Grep / Glob 多檔需求 → **single message multi-tool-call**:
@@ -3,6 +3,8 @@ name: delivery-handoff
3
3
  description: Generate stakeholder-ready handoff documentation when a product / feature is confirmed final. Produces Storybook handoff page + UI flow diagram + component usage inventory + token consumption report + a11y checklist + per-screen spec — a Figma-like inspectable delivery package. Invoke via /delivery-handoff when user says「要交付」「handoff」「交付文件」「給 X 團隊的文件」「產品確認要上線了」. **只在產品確認 final 後才 invoke**,非 prototype 階段。
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # Delivery Handoff Workflow
7
9
 
8
10
  Purpose: 產品 / feature 通過 prototype → audit → stakeholder 決策後確認 final,需要產 **Figma-like inspectable handoff documentation** 給工程 / PM / QA / 其他 stakeholder。
@@ -60,6 +62,11 @@ Purpose: 產品 / feature 通過 prototype → audit → stakeholder 決策後
60
62
 
61
63
  ### Phase 1 — Inventory 生成
62
64
 
65
+ **Deterministic 源優先(2026-07-07 治理進化收尾:本 skill 原「幾乎零機械依賴」列冊補強)**:
66
+ - Component / story 母集 = `node scripts/gen-ds-story-manifest.mjs` 產出的 manifest(禁憑記憶列)
67
+ - Token 使用 = `rg -o -- '--[a-z-]+' <feature files> | sort | uniq -c`(機械計數,非印象)
68
+ - 消費統計交叉驗 `scripts/audit-orphan-tokens.mjs` 的消費通道邏輯(同一套 5 通道定義)
69
+
63
70
  scan 目標 feature 的 code,自動 inventory:
64
71
 
65
72
  1. **Component 清單**(哪些 DS 元件被此 feature 消費):
@@ -3,6 +3,8 @@ name: performance-audit
3
3
  description: Performance audit for design-system components and product UI. Checks render count, unnecessary re-renders, memoization gaps, bundle size impact, useEffect chains, context thrashing. Invoke when user says「這元件效能如何」「為什麼很卡」「bundle 變大」「re-render 太多」, auto-invoked by `/component-quality-gate` Phase 4.5 (advanced mode) and `/design-system-audit` Dimension D3.
4
4
  ---
5
5
 
6
+ > **⚠️ Fork 工具註記(build 自動加)**:本 skill 提到的 `scripts/*.mjs` 或非標準 `npm run <audit>` 是 **DS-author repo 的機械工具,未隨 fork 套件附帶**(Claude Code 不掃 node_modules,fork 也無這些 executor + dep)。你的 product fork 用本 skill 的**方法論**(human / AI judgment)+ 既有 committed governance hook 的機械強制即可;要 mechanical 腳本層(截圖 / CI gate)請自行設置對應工具,或把該檢查 PR 回 DS repo 跑。
7
+
6
8
  # Performance Audit — 元件效能稽核
7
9
 
8
10
  ## 存在意義
@@ -47,7 +49,7 @@ description: Performance audit for design-system components and product UI. Chec
47
49
 
48
50
  **工具**:
49
51
  - React DevTools Profiler record → 看 flame graph
50
- - `why-did-you-render` dev dep 觸發警告(未來可加)
52
+ - `why-did-you-render` dev dep 觸發警告(**尚未落地**;trigger = 下一個 render-storm 類 bug 實例時一併引入,roadmap 殘項列冊)
51
53
  - 直接 grep pattern(inline style / arrow onClick)
52
54
 
53
55
  ### Phase 2 — Memoization / dependency
@@ -66,7 +68,7 @@ description: Performance audit for design-system components and product UI. Chec
66
68
 
67
69
  **工具**:
68
70
  - `npx vite build --report`(vite bundle visualizer)
69
- - bundle-size CI check(未來可加)
71
+ - **bundle-size gate(已落地 2026-07-07)**:`npm run check:bundle-size`(budget SSOT = `packages/design-system/bundle-budget.json`,total + top-8 entry 各 +10% headroom;release-preflight 內建必跑;蓄意增大 → `--init` 更新 budget + commit 說明)
70
72
 
71
73
  ### Phase F — Report(必 STOP,對齊分權 canonical)
72
74
 
@@ -54,6 +54,14 @@ description: Pixel-level visual audit for design-system components based on user
54
54
  - 沒有 screenshot → **本 skill 拒跑**(見 Preconditions)
55
55
  - 只要看一眼順不順 → 不做 audit(這不 mechanical)
56
56
 
57
+ ## Baseline 更新鐵律(2026-07-07 軌道 5 codify)
58
+
59
+ **覆蓋任何 snapshots-baseline/*.png 前,模型必 Read 新舊兩張圖並寫出「觀察到的具體差異」**
60
+ (diff 百分比是訊號、圖才是真相)。禁只看 pct 就 cp。錨例:2026-07-07 VR 換日 bug——釘日期後
61
+ pct 一模一樣,只有看圖才發現 today 圈仍在真實日期(元件內部時間);FileViewer 40.3% 只有看圖
62
+ 才知道是 zoom 94→100(fit 被凍壞)非渲染炸裂。配套:roadmap 方向 7 accept-baseline workflow
63
+ (script 化時「看圖步驟」不可省)。
64
+
57
65
  ## Preconditions(硬規則)
58
66
 
59
67
  **本 skill 在下列任一缺失下拒跑,回報 user 補齊後再 invoke**:
@@ -82,7 +82,7 @@ if [ -n "$VIOLATIONS" ]; then
82
82
 
83
83
  ┄┄┄┄ check_benchmark_citation — world-class benchmark claim 缺 source ┄┄┄┄
84
84
 
85
- [P1 WARN] ${FILE_PATH}
85
+ [P0 BLOCKER] ${FILE_PATH}
86
86
  偵測到 benchmark claim 段缺 inline citation:
87
87
  ${VIOLATIONS}
88
88
  M22 canonical(2026-05-02):**寫 spec / code 含「Ant / Material / Polaris / ...」claim 必附**:
@@ -99,8 +99,10 @@ M22 canonical(2026-05-02):**寫 spec / code 含「Ant / Material / Polaris / ...
99
99
  - \`// @benchmark-citation-allow: <reason>\`(legacy 過渡期暫掛)
100
100
  - \`// @benchmark-unverified-blanket: <reason>\`(M22 (d) file-level 撤回)
101
101
  EOF
102
- # Soft warning(P1)— print to stderr, don't block(exit 1 vs exit 2)
103
- exit 1
102
+ # P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27 doctrine「SSOT canonical
103
+ # 必 P0 BLOCKER 禁 P1 WARN」— escape 已備:@benchmark-unverified 逐句撤回 /
104
+ # @benchmark-citation-allow / @benchmark-unverified-blanket 檔級,故升級不增誤殺)
105
+ exit 2
104
106
  fi
105
107
 
106
108
  exit 0
@@ -55,8 +55,19 @@ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_stri
55
55
  # DS primitive 名單(wrap 這些就觸發 anchor preflight)
56
56
  DS_PRIMITIVES_RE='<(Sidebar|AppShell|DataTable|Dialog|Sheet|Popover|DropdownMenu|Field|FieldControlGroup|MenuItem|ItemAvatar|ItemLabel|ItemIcon|SegmentedControl|Tabs|TabsList|TabsTrigger|Combobox|Select|DatePicker|TimePicker|TreeView|Tooltip|Coachmark|FileViewer|ScrollArea|Avatar|Badge|Button|ChromeHeader|SurfaceHeader|SurfaceBody|SurfaceFooter|OverlaySurface|NameCard|Toast|FileUpload|DescriptionList|Chart|BulkActionBar|ActionBar|Carousel|Breadcrumb)\b'
57
57
 
58
- # 沒 wrap DS primitive → 跳過(可能是純 utility / hook code)
58
+ # 沒 wrap DS primitive → 檢「純手刻」洞(2026-07-07 治理進化方向 3 b:原本此分支直接放行,
59
+ # 「該用 primitive 卻整個手刻」反而抓不到 — 內部盤點 file:line 證據見 planning/2026-07-07-governance-evolution-roadmap.md)
59
60
  if ! echo "$NEW_CONTENT" | grep -qE "$DS_PRIMITIVES_RE"; then
61
+ # 視覺簽名:row/浮層/chrome 手刻特徵(flex row + padding/border/rounded/shadow 組合)
62
+ HANDCRAFT_SIG=$(echo "$NEW_CONTENT" | grep -cE 'className="[^"]*\b(flex|absolute|fixed)\b[^"]*\b(border|rounded|shadow|px-|py-|gap-)' || true)
63
+ if [ "${HANDCRAFT_SIG:-0}" -ge 3 ] && ! echo "$NEW_CONTENT" | grep -q '@handcraft-ok:'; then
64
+ cat >&2 <<'EOF_HC'
65
+ ⚠️ [第一期 WARN] M23(d)/mindset #2 純手刻嫌疑:本段 production tsx 有 ≥3 處視覺結構 className
66
+ 但零 DS primitive 消費。先查 patterns/(item-anatomy / overlay-surface / ChromeHeader /
67
+ horizontal-overflow)有無現成 primitive;真需自建 → 行內 `@handcraft-ok: <rationale>` +
68
+ spec「自建 + 理由」宣告。誤殺率驗證期後升 P0(roadmap 方向 3)。
69
+ EOF_HC
70
+ fi
60
71
  exit 0
61
72
  fi
62
73
 
@@ -127,6 +138,6 @@ cat >&2 <<EOF
127
138
  - \`.claude/references/ssot-index.md\` Step 0.1 high-risk interface owner mapping
128
139
  EOF
129
140
 
130
- # Soft warn (exit 0):inject context AI 自決,Stop hook backstop。
131
- # 對齊 check_substantive_edit_approval_preflight.sh hybrid pattern(soft pre + hard stop)。
132
- exit 0
141
+ # P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27「SSOT canonical P0 禁 soft」
142
+ # doctrine;bypass env CLAUDE_BYPASS_DS_ANCHOR=1 已備且 audit-logged,升級不增誤殺)。
143
+ exit 2
@@ -46,6 +46,10 @@ esac
46
46
 
47
47
  NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""')
48
48
  [ -z "$NEW_CONTENT" ] && exit 0
49
+ # Per-line escape(2026-07-07 方向 2 升 P0 配套):行內含 `@token-registry-ok: <rationale>` 豁免該行
50
+ NEW_CONTENT=$(printf '%s
51
+ ' "$NEW_CONTENT" | grep -v '@token-registry-ok:' || true)
52
+ [ -z "$NEW_CONTENT" ] && exit 0
49
53
 
50
54
  # Resolve registry path(absolute via $CLAUDE_PROJECT_DIR or relative fallback)
51
55
  REGISTRY="${CLAUDE_PROJECT_DIR:-}/packages/design-system/src/tokens/utility-registry.json"
@@ -152,7 +156,11 @@ EOF_HEAD
152
156
  bg-neutral-N → semantic utility(bg-surface 等)或 bg-[var(--color-neutral-N)]
153
157
 
154
158
  詳 utility-registry.json `_meta.spec_sources` + M23「DS 內既有 canonical 優先」。
159
+ Per-line escape:行尾註解 `@token-registry-ok: <rationale>`(audit 可追)。
155
160
  EOF_BODY
161
+ # P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27「SSOT canonical 必 P0」doctrine;
162
+ # 檔級豁免(anatomy/principles stories / token 自家)+ 行級 escape 已備,升級不增誤殺)
163
+ exit 2
156
164
  fi
157
165
 
158
166
  exit 0
@@ -10,6 +10,10 @@
10
10
  # source,不可憑印象 propose」+ propose-options/SKILL.md。
11
11
  #
12
12
  # 注意:本 hook 走 UserPromptSubmit(在 AI reply 前 inject context),
13
+ # ⚠️ fail-closed 設計約束(2026-07-07 治理進化方向 2 稽核結論):UserPromptSubmit 的 exit 2
14
+ # 擋的是 user 輸入本身 = 錯誤工具語義,本 hook **不可**升 exit 2。propose 家族的 fail-closed
15
+ # 端 = check_propose_pre_grep_verify.sh(M18 Q0 配對)+ stop_self_audit claim-verify 閘;
16
+ # 本 hook 職責 = 事前 P0 措辭 directive 注入(非 soft 建議)。
13
17
  # 跟 check_propose_pre_grep_verify.sh(走 PostToolUse 偵測 propose 已成型後再警示)互補。
14
18
 
15
19
  source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
@@ -43,9 +47,9 @@ if [ "$HAS_FETCH" -ge 2 ]; then
43
47
  exit 0
44
48
  fi
45
49
 
46
- # Soft inject
50
+ # P0-directive inject(exit 0 by design — 見檔頭 fail-closed 設計約束)
47
51
  cat <<EOF
48
- ⚠️ M26 Propose-without-benchmark gate
52
+ 🚨 M26 Propose-without-benchmark gate(P0 directive — 未 fetch 就 propose = M26 違規,非建議)
49
53
 
50
54
  → User prompt 含 propose / visual / behavior decision trigger keyword,但過去 ~20 turns
51
55
  Web fetch / WebSearch tool_use count = $HAS_FETCH(< 2)。
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # check_ssot_header_declaration.sh — 新建 production tsx 必帶「── 消費的 SSOT ──」宣告段
3
+ # (2026-07-07 治理進化方向 3 洞 a:原 check_ssot_consultation.sh 2026-05-XX retired 後,
4
+ # CLAUDE.md「# SSOT 消費 canonical」+ ssot-consultation.md:30-51 強制的檔頭宣告段
5
+ # **無任何機械驗證** — 內部盤點實證。本 hook = 精簡復活版,只驗「新檔有無宣告段」,
6
+ # 不驗宣告品質(品質靠 audit dim 1 + M18/M29)。)
7
+ #
8
+ # Scope(ratchet,對齊 Polaris stylelint-adoption 策略):
9
+ # - 只攔 **Write 新檔**(components/ patterns/ 的 .tsx,磁碟上不存在)— 存量檔 Edit 豁免
10
+ # - stories / test / index.ts 豁免
11
+ # - escape:內容含 `@ssot-header-exempt: <rationale>`(audit 可追)
12
+ # P0 BLOCKER(user 2026-05-27「SSOT canonical 必 P0」doctrine)。
13
+
14
+ set -euo pipefail
15
+
16
+ INPUT=$(cat)
17
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
18
+ [ "$TOOL" = "Write" ] || exit 0
19
+
20
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
21
+ case "$FILE_PATH" in
22
+ */packages/design-system/src/components/*.tsx|*/packages/design-system/src/patterns/*.tsx) ;;
23
+ *) exit 0 ;;
24
+ esac
25
+ case "$FILE_PATH" in
26
+ *.stories.tsx|*.test.tsx|*/index.ts|*/index.tsx) exit 0 ;;
27
+ esac
28
+
29
+ # Ratchet:磁碟已存在 = 存量覆寫,豁免(存量清理另走 audit)
30
+ [ -f "$FILE_PATH" ] && exit 0
31
+
32
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
33
+ [ -z "${CONTENT//[[:space:]]/}" ] && exit 0
34
+
35
+ # 宣告段或 escape 任一存在 → 過
36
+ if echo "$CONTENT" | grep -q "消費的 SSOT"; then exit 0; fi
37
+ if echo "$CONTENT" | grep -q "@ssot-header-exempt:"; then exit 0; fi
38
+
39
+ cat >&2 <<'EOF'
40
+ 🚨 [P0 BLOCKER] 新建 production tsx 缺「── 消費的 SSOT ──」檔頭宣告段
41
+
42
+ CLAUDE.md「# SSOT 消費 canonical」+ .claude/references/ssot-consultation.md:30-51 強制:
43
+ 新元件 tsx 開頭必列本檔消費的 components / patterns / tokens / spec(沒列 = 自創 = mindset #2 違規)。
44
+
45
+ 修法 — 2 選 1:
46
+ (a) 檔頭補宣告段(格式見 ssot-consultation.md;近例:pagination.tsx 檔頭):
47
+ // ── 消費的 SSOT ──
48
+ // - <primitive / token / spec 清單 + 消費點一句>
49
+ (b) 真的零 SSOT 可消費(罕見,純 utility)→ 內容加 `// @ssot-header-exempt: <rationale>`
50
+
51
+ 對應:mindset #2 / M23 / 治理進化 roadmap 方向 3(planning/2026-07-07-governance-evolution-roadmap.md)
52
+ EOF
53
+ exit 2
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+ # check_story_determinism.sh — stories 寫入時 determinism 哨(2026-07-07 治理進化軌道 7)
3
+ #
4
+ # Why:story 用真實時間/亂數 → VR baseline 換日假 breach(anchor:2026-07-07 calendar 三 scenario
5
+ # 0.07–0.51% 週期性假紅,三修才根治:story 釘日期 → 元件內部時間 → runner 偏移時鐘)。
6
+ # runner 層已有偏移時鐘兜底(scripts/visual-audit.mjs),本 hook = 寫入層提前攔,
7
+ # 讓 determinism 在出生點就成立(非 VR 撞到才修)。
8
+ #
9
+ # 偵測(新寫入內容):裸 `new Date()`(無參數)/ `Date.now()` / `Math.random()`。
10
+ # escape:行內 `@nondeterministic-ok: <rationale>`(如「即時鐘 demo,VR 不截」)。
11
+ # 第一期 WARN(exit 0):存量 10 檔(dry-run 2026-07-07,DateGrid 家族為主)未清,
12
+ # 誤殺率驗證期後升 P0(對齊 handcraft 偵測同節奏;roadmap 方向 1 KPI 隨頻回顧)。
13
+
14
+ set -euo pipefail
15
+ INPUT=$(cat)
16
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
17
+ case "$TOOL" in Edit|Write|MultiEdit) ;; *) exit 0 ;; esac
18
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
19
+ case "$FILE_PATH" in *.stories.tsx) ;; *) exit 0 ;; esac
20
+
21
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '
22
+ (.tool_input.content // "") + "\n" +
23
+ (.tool_input.new_string // "") + "\n" +
24
+ ([.tool_input.edits[]? | .new_string] | join("\n"))
25
+ ' 2>/dev/null || echo "")
26
+ [ -z "${NEW_CONTENT//[[:space:]]/}" ] && exit 0
27
+
28
+ HITS=$(printf '%s\n' "$NEW_CONTENT" | grep -v '@nondeterministic-ok:' | grep -nE 'new Date\(\)|Date\.now\(\)|Math\.random\(\)' || true)
29
+ [ -z "$HITS" ] && exit 0
30
+
31
+ cat >&2 <<EOF
32
+ ⚠️ [第一期 WARN] story 非決定性時間/亂數(VR baseline 換日假 breach 根因):
33
+ $HITS
34
+ → 釘固定值(new Date(2026, 6, 15) / 常數 seed);真需即時值 → 行內 \`@nondeterministic-ok: <rationale>\`。
35
+ Anchor:2026-07-07 calendar VR 三修;runner 偏移時鐘為兜底非藉口。roadmap 方向 1(planning/2026-07-07-governance-evolution-roadmap.md)。
36
+ EOF
37
+ exit 0
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Hook(PreToolUse Edit/Write/MultiEdit):spec.md / .tsx in packages/design-system/src/
5
5
  # 含 world-class benchmark claim(Ant / Material / Polaris ...)必附 inline citation
6
- # (URL / GitHub #L / snapshots/ / @benchmark-unverified)。違 P1 soft warn(exit 1)。
6
+ # (URL / GitHub #L / snapshots/ / @benchmark-unverified)。違 P0 BLOCKER(exit 2,2026-07-07 方向 2 升級;escape marker 既有)。
7
7
  # 整檔 escape:前 5 行含 @benchmark-citation-allow: 或 @benchmark-unverified-blanket:。
8
8
 
9
9
  set -u
@@ -45,10 +45,10 @@ expect_pass_silent() {
45
45
 
46
46
  expect_warn() {
47
47
  local name="$1"; local needle="$2"
48
- if [ "$EXIT" = "1" ] && echo "$STDERR_TEXT" | grep -qF "$needle"; then
48
+ if [ "$EXIT" = "2" ] && echo "$STDERR_TEXT" | grep -qF "$needle"; then
49
49
  echo " PASS $name"; PASS=$((PASS+1))
50
50
  else
51
- echo " FAIL $name (expected exit=1 + needle '$needle', got exit $EXIT)"
51
+ echo " FAIL $name (expected exit=2 + needle '$needle', got exit $EXIT)"
52
52
  echo " --- stderr ---"; echo "$STDERR_TEXT" | sed 's/^/ /'; echo " --- end ---"
53
53
  FAIL=$((FAIL+1)); FAILED_TESTS="${FAILED_TESTS}\n - $name"
54
54
  fi
@@ -28,7 +28,9 @@ echo "Test 3: fork-user without plugin → inject"
28
28
  TMP=$(mktemp -d); echo '{"dependencies":{"@qijenchen/design-system":"^1"}}' > "$TMP/package.json"
29
29
  # Override HOME/CWD so plugin install detect 一定 fail
30
30
  STDOUT=$(cd "$TMP" && HOME=/tmp/no-plugin echo '{"hook_event_name":"SessionStart"}' | bash "$HOOK")
31
- if echo "$STDOUT" | grep -q "Fork-user plugin not installed"; then
31
+ # 2026-07-07 stale needle:hook 訊息 2026-06 演進為「Fork-user DS 治理鏈未就位」,
32
+ # 舊 needle「Fork-user plugin not installed」永 FAIL(main 既存紅;hook 行為本身正確)。
33
+ if echo "$STDOUT" | grep -q "Fork-user DS 治理鏈未就位"; then
32
34
  echo " PASS"; PASS=$((PASS+1))
33
35
  else
34
36
  echo " FAIL: no inject (output: ${STDOUT:0:200})"; FAIL=$((FAIL+1))
@@ -47,10 +47,10 @@ expect_pass_silent() {
47
47
 
48
48
  expect_warn() {
49
49
  local name="$1"; local needle="$2"
50
- if [ "$EXIT" = "0" ] && echo "$STDERR_TEXT" | grep -qF "$needle"; then
50
+ if [ "$EXIT" = "2" ] && echo "$STDERR_TEXT" | grep -qF "$needle"; then
51
51
  echo " PASS $name"; PASS=$((PASS+1))
52
52
  else
53
- echo " FAIL $name (expected warn '$needle', got exit $EXIT)"
53
+ echo " FAIL $name (expected exit=2 + needle '$needle', got exit $EXIT)"
54
54
  echo " --- stderr ---"; echo "$STDERR_TEXT" | sed 's/^/ /'; echo " --- end ---"
55
55
  FAIL=$((FAIL+1)); FAILED_TESTS="${FAILED_TESTS}\n - $name"
56
56
  fi
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # test_check_ssot_header_declaration.sh — 新建 production tsx 必帶「── 消費的 SSOT ──」段
3
+ # (2026-07-07 治理進化方向 3 洞 a;P0 exit 2;ratchet:只攔 Write 新檔)
4
+
5
+ HOOK="$(cd "$(dirname "$0")/.." && pwd)/check_ssot_header_declaration.sh"
6
+ if [ ! -f "$HOOK" ]; then echo "FATAL: hook not found: $HOOK"; exit 1; fi
7
+
8
+ PASS=0; FAIL=0; FAILED_TESTS=""
9
+
10
+ run_hook() {
11
+ local file_path="$1"; local content="$2"; local tool="${3:-Write}"
12
+ STDERR_TEXT=$(jq -n --arg t "$tool" --arg f "$file_path" --arg c "$content" \
13
+ '{tool_name:$t, tool_input:{file_path:$f, content:$c}}' | bash "$HOOK" 2>&1 >/dev/null)
14
+ EXIT=$?
15
+ }
16
+
17
+ expect_block() {
18
+ local name="$1"
19
+ if [ "$EXIT" = "2" ] && echo "$STDERR_TEXT" | grep -qF "消費的 SSOT"; then
20
+ echo " PASS $name"; PASS=$((PASS+1))
21
+ else
22
+ echo " FAIL $name (expected exit=2, got $EXIT)"; FAIL=$((FAIL+1)); FAILED_TESTS="${FAILED_TESTS}\n - $name"
23
+ fi
24
+ }
25
+ expect_silent() {
26
+ local name="$1"
27
+ if [ "$EXIT" = "0" ]; then
28
+ echo " PASS $name"; PASS=$((PASS+1))
29
+ else
30
+ echo " FAIL $name (expected exit=0, got $EXIT)"; FAIL=$((FAIL+1)); FAILED_TESTS="${FAILED_TESTS}\n - $name"
31
+ fi
32
+ }
33
+
34
+ echo "=== check_ssot_header_declaration tests ==="
35
+
36
+ # 1. 新元件 tsx 無宣告段 → BLOCK
37
+ run_hook "/repo/packages/design-system/src/components/NewThing/new-thing.tsx" 'import * as React from "react"
38
+ export const NewThing = () => <div className="flex border rounded-md px-2" />'
39
+ expect_block "1. 新元件 tsx 無 SSOT 段 → BLOCK"
40
+
41
+ # 2. 有宣告段 → pass
42
+ run_hook "/repo/packages/design-system/src/components/NewThing/new-thing.tsx" '// ── 消費的 SSOT ──
43
+ // - Button(pill layout)/ --layout-space-tight
44
+ import * as React from "react"'
45
+ expect_silent "2. 有「── 消費的 SSOT ──」段 → pass"
46
+
47
+ # 3. escape marker → pass
48
+ run_hook "/repo/packages/design-system/src/components/NewThing/util.tsx" '// @ssot-header-exempt: 純型別 utility,零視覺
49
+ export type X = string'
50
+ expect_silent "3. @ssot-header-exempt escape → pass"
51
+
52
+ # 4. stories 檔 → 豁免
53
+ run_hook "/repo/packages/design-system/src/components/NewThing/new-thing.stories.tsx" 'export default {}'
54
+ expect_silent "4. stories.tsx 豁免"
55
+
56
+ # 5. 非 DS production 路徑 → 豁免
57
+ run_hook "/repo/apps/template/src/App.tsx" 'export const App = () => <div className="flex border px-2" />'
58
+ expect_silent "5. apps/ 路徑豁免"
59
+
60
+ # 6. Edit 工具(存量檔)→ 豁免(ratchet)
61
+ run_hook "/repo/packages/design-system/src/components/Button/button.tsx" 'whatever' "Edit"
62
+ expect_silent "6. Edit(存量 ratchet)豁免"
63
+
64
+ # 7. patterns/ 新檔無宣告 → BLOCK
65
+ run_hook "/repo/packages/design-system/src/patterns/new-pattern/new-pattern.tsx" 'export const P = () => <div className="absolute shadow px-2" />'
66
+ expect_block "7. patterns/ 新檔無 SSOT 段 → BLOCK"
67
+
68
+ echo ""
69
+ echo "=== Summary ==="
70
+ echo "Passed: $PASS / $((PASS+FAIL))"
71
+ if [ "$FAIL" -gt 0 ]; then printf "Failed:%b\n" "$FAILED_TESTS"; exit 1; fi
72
+ exit 0
@@ -34,7 +34,7 @@
34
34
  - **Pre-edit**:`check_substantive_edit_approval_preflight.sh`(production code)+ `stop_self_audit.sh`(spec/canonical 補位)+ `check_ds_anchor_preflight.sh`(M29 anchor)
35
35
  - **Post-edit**:`stop_self_audit.sh` Mechanism 1(claim-verify-gap)BLOCKER
36
36
  - **Pre-final(宣告完成前)**:`stop_self_audit.sh` Mechanism 7(完整性宣告閘)BLOCKER — 宣告「全做完 / 全部完成」+ 本 turn 實質改動但**無全庫 stale-ref 掃描證據** → block。**觸發器 = 「宣告完成」本身,非等 user 問第二次**(2026-06-03 user-authorized,根治重複 failure)
37
- - **Pre-final(重大 / SSOT / 模型 / 跨多檔改動)**:除 M7 自掃外,**宣告完成前必跑「獨立對抗稽核」**(multi-agent Workflow,每路假設「還有 loose end」主動去找 + cite 證據)。**理由**:self-grep 系統性漏(self-assessment unreliable,對齊 `feedback_ai_ground_truth_unreliable_mechanical_primary`)+ 信任機械閘(preflight / R4 / hook BLOCKER)勝於自評。**小改 = M7 自掃即可**,不需對抗稽核(避免過度)。2026-06-03 user-authorized,根治「宣告做完 → user 問第 N 次 → 才補掃出 loose end」
37
+ - **Pre-final(中型以上 = ≥3 substantive、任何 canonical/SSOT/模型改動、或宣告「殘項/債歸零」類完整性 claim;2026-07-07 軌道 4 收緊,Workflow 成本已低無理由跳)**:除 M7 自掃外,**宣告完成前必跑「獨立對抗稽核」**(multi-agent Workflow,每路假設「還有 loose end」主動去找 + cite 證據)。**理由**:self-grep 系統性漏(self-assessment unreliable,對齊 `feedback_ai_ground_truth_unreliable_mechanical_primary`)+ 信任機械閘(preflight / R4 / hook BLOCKER)勝於自評。**小改 = M7 自掃即可**,不需對抗稽核(避免過度)。2026-06-03 user-authorized,根治「宣告做完 → user 問第 N 次 → 才補掃出 loose end」
38
38
  - **Pre-commit**:`scripts/audit-content-quality.mjs --check` + `scripts/extract-canonical-rules.mjs` 各 fail = block
39
39
 
40
40
  ## Anti-pattern(永久 ban)
@@ -15,7 +15,7 @@ paths:
15
15
  - 編輯 spec 或建新元件時必對照 **Polaris / Material / Ant / Atlassian / Carbon / Apple HIG** 的 7 維度:何時用 / 何時不用 / 近親分界 / 常見誤解 / 相關 links / 空值 / 驗證 / Loading / a11y 預設。SegmentedControl spec 是本專案 template
16
16
  - 編輯 spec 必交叉比對相關 spec + Storybook,確認無矛盾 / 術語一致 / 無重複
17
17
  - 與既有 spec 有邏輯衝突 / 概念混淆 → 主動提出討論,不默默改 / 不迴避
18
- - 所有元件遵循 shadcn 框架(forwardRef / Slot / data-* / cva),不從零重寫
18
+ - 所有元件遵循 shadcn 框架(forwardRef / Slot / data-* / cva),不從零重寫——**「框架」= 結構 idiom,非「必須消費 shadcn 目錄每個元件」;目錄後續新增 vs DS 既有等價物 → 見 ui-development.md「shadcn 目錄後續新增元件 vs DS 既有」**(2026-07-07 clarifier,防「FileItem 是 Attachment 的從零重寫」類誤讀)
19
19
  - 每個元件 spec「定位」段必明確宣告實作基礎:`基於 Radix X` / `基於 cmdk / sonner` / native / `自建 + 理由`(自建必說明為何不用現有 primitive)
20
20
  - Spec 文字品質:不描述視覺形狀 / 實作細節(「窄長形」「會變寬」「zero layout shift」屬 story 不進 spec);術語一致;「禁止事項(❌)」列所有常見誤用
21
21
  - **a11y 段強制**:互動元件 spec.md 必含 `## A11y 預設` 段(列 ARIA + Keyboard map);純視覺 indicator(Badge / Tag / Separator / Skeleton)豁免明文寫「本元件無互動」
@@ -90,3 +90,5 @@ paths:
90
90
  **cva 適用**:className-only 差異 → cva;style 物件 → object map + `style={{}}`;不同 JSX 樹 → conditional rendering。
91
91
 
92
92
  **元件不得自包全域 Provider**(Tooltip / Theme / Toast / Portal)— 由應用層統一設定。**判斷**:Context 是行為狀態(open / size)→ 可包;全域外觀配置(delay / theme / portal / variant defaults)→ 禁止。
93
+
94
+ **shadcn 目錄後續新增元件 vs DS 既有(2026-07-07 codify,anchor:Attachment vs FileItem)**:「遵循 shadcn 框架」= 結構 idiom(forwardRef / Slot / data-* / cva),**非**「必須消費目錄每個元件」。shadcn 是 copy-in scaffold 非 runtime 依賴——遷移無上游更新流入。目錄新增元件時二分:(a) 帶 primitive / behavior 增益(Radix core、新 interaction model)且 DS 無等價 → 走 `/new-component` 近親評估;(b) **純組合式且 DS 已有 Family-compliant 等價物 → 不換架構、不改名**(M23 禁外部覆蓋既有 canonical),只做 API 對照:值得的 state / pattern 逐項評估為 DS prop 演進(SSOT-affecting → ASK),該元件 spec 補 M22 benchmark 對照行 + known-gap 記錄。同類先例:Empty / Item(≈item-anatomy)/ Field / Spinner(≈CircularProgress)/ InputGroup(≈FieldControlGroup)。
@@ -55,6 +55,12 @@ description: Batch-end-verify rhythm + parallel tool batch + user-listed N-rule
55
55
  - 漏 N 條中第 K 條當「做完」report
56
56
  - 改寫 user wording 為自己 paraphrase(verbatim 保留)
57
57
 
58
+ **Sweep-first, edit-second(2026-07-07 治理進化軌道 2 codify)**:任一 fix 屬「跨切面決策」
59
+ (改名 / 詞彙統一 / 規則性改動,可寫成 grep 謂詞者)→ **先跑謂詞產出「sweep manifest」**
60
+ (grep 指令 + 全庫命中清單,artifact 化),照清單做完、驗證清單歸零,才算該 fix 完成。
61
+ **禁**「先改直覺想到的檔、稽核抓漏再補」(anchor:2026-07-07 meta 詞彙統一三波 11→16→9 檔,
62
+ 規則在散文裡 → 三輪才收斂;寫成謂詞後一個 grep 掃平)。
63
+
58
64
  ### Phase 1 — Parallel tool batch(原 M32(d))
59
65
 
60
66
  Read / Grep / Glob 多檔需求 → **single message multi-tool-call**:
@@ -86,13 +86,11 @@ detect_mode() {
86
86
  **🚨 反抽樣鐵律 — 「機械涵蓋」必先 breadth-verify(2026-06-05 user 抓 story-title 又抽樣,verbatim「這個問題已經問你一百次了結果你還是抽樣」)**:把某 judgment dim 標為「DETERMINISTIC/HOOK 已兜底、不在 judgment risk」**= 一種抽樣**,除非該 gate 的偵測廣度被**證明**對齊 canonical。**強制兩步,缺一即視同抽樣**:
87
87
  1. **Breadth-test**:對該 gate 注入一個「已知違規」樣本 → 跑 gate → 確認被抓(exit 1)→ revert。沒通過 = 該 gate **不算**涵蓋該 dim,退回 PURE-JUDGMENT 跑完整枚舉。
88
88
  2. **完整枚舉 + partition-review**(judgment dim sample-proof 跑法):deterministic 腳本枚舉**全部單元**(eg. `gen-ds-story-manifest.mjs` 全 926 name)→ 機械 auto-pass 明確合規者 → 剩餘 candidate **切 N chunk,每 chunk agent 審它每一個**(chunk 互斥涵蓋全集 → 每單元剛好審一次,數學上不可能漏)。**禁** agent 自選 top N。
89
- - **錨例**:dim 40/41/43 被當「story-quality:check 已涵蓋」排除 NO-SAMPLE gate 有 detection gap(只抓 100%-English,漏中英混雜)+ scope gap(漏 anatomy/principles) 放過 84 違規,還回報「0 violations」假綠燈。修:gate `name_mixed_english`(Dim 41b)+ 全 storyFiles scope + breadth-verified(注入 `Hover Compact 測試` 確認抓到)。對齊 M7 子規則 M34「spec broad + hook narrow = gap 必補」。
89
+ - **錨例**:dim 40/41/43 被當「story-quality:check 已涵蓋」排除 → gate 有 detection gap(只抓 100%-English)+ scope gap(漏 anatomy/principles),放過 84 違規還報「0 violations」假綠。修:補 `name_mixed_english` + 全 storyFiles scope + breadth-verified。對齊 M34「spec broad + hook narrow = gap 必補」。
90
90
 
91
91
  ### A.1b — Claim-vs-code + docblock + spec-internal adversarial verification(MANDATORY,NO-SAMPLE,per-component)
92
92
 
93
- **2026-05-30 anchor(user verbatim 質問「之前他媽都在偷懶?」)**:獨立 adversarial 再審抓到 **403 findings / 64 單元 / 0 全乾淨**,其中 **202 FALSE_CLAIM**(anatomy/a11y/principles/spec 系統性記載 code 根本沒有的行為:Calendar 宣稱方向鍵導覽 / Tooltip·HoverCard 宣稱 focus trap / Alert 記不存在的 `actions` prop / Select 宣稱「用原生 select」但桌機自建 cmdk / AspectRatio spec 說「無 wrapper」但 code wrap)。**根因**:前期 audit story-content dim(12/24/25/30/43 等)當「散文層 looks-fine 掃」跑,**沒 adversarial 讀 .tsx(+ wrap 的 lib)逐句比對宣稱**。這是「偷懶」的具體 failure mode。
94
-
95
- **為何不能只靠 deterministic grep**:2026-05-30 嘗試建 `audit-anatomy-prop-existence.mjs`(已刪除不存在)機械驗 prop-existence,但 **prop passthrough(元件 `...props` 轉發 Radix/react-day-picker)使 naive grep 必 over-flag 合法 prop** → 不可靠 → 刪除。**結論:FALSE_CLAIM 驗證本質需 LLM 讀 source 判斷,無法純 grep gate → 故必用「強制 + 報告驗證確認真跑」的機制保證**。
93
+ **2026-05-30 anchor(user verbatim「之前他媽都在偷懶?」)**:獨立 adversarial 再審抓 **403 findings / 64 單元 / 202 FALSE_CLAIM**(doc 系統性記載 code 沒有的行為,如 Calendar 宣稱方向鍵導覽、Alert 記不存在的 `actions` prop)。根因 = 前期把 story-content dim(12/24/25/30/43 等)當「散文 looks-fine 掃」,沒 adversarial 讀 .tsx(+ wrap 的 lib)逐句比對宣稱。**為何不能純 grep**:prop passthrough(`...props` 轉發 Radix 等)使 naive prop-existence grep 必 over-flag 合法 prop(2026-05-30 建過即刪)→ **FALSE_CLAIM 驗證本質需 LLM 讀 source,故用「強制 + report-validator 確認真跑」機制保證**。
96
94
 
97
95
  **強制流程**(deep-audit 每次必跑,no skip):
98
96
  0. **機械 gate 先跑(deterministic,2026-06-02 加)**:`npm run typecheck:stories`(deterministic 抓 stories 的 `{var}`-undefined / prop 型別錯 —— **這是 SizeMatrix `{size}` crash 的真防線**;主 tsc -b exclude stories 故必跑此)+ `node scripts/storybook-smoke-test.mjs --full`(runtime crash render 掃)。先過才進 adversarial read。Anchor:2026-06-02 Field SizeMatrix `{size}` JSX-undefined crash 隨 beta.44 ship。**注**:smoke 全覆蓋 coverage-gate(防靜默-skip 假綠燈)attempted 但 CI server 規模化降級(~60 story 後 timeout 撞 20-min budget)→ **defer**(需 robust-server / browser-recycle + 可靠測試環境);故 typecheck:stories 是目前 deterministic 主防線。
@@ -188,6 +186,10 @@ Brief 必含 4 段(完整 template SSOT → `references/phase-b-codex-brief.md`,
188
186
 
189
187
  Deep-audit 收尾**必自動跑** `/knowledge-prune` deep — **前提鐵律:確保產出不打折、以產出完美為前提**(quality-first,= knowledge-prune SKILL 2026-06-02 核心前提:只清真冗餘提升 signal,每一條真實 invariant / 機械防線必完整保留,retire 前必確認保護已被別處覆蓋;2026-06-11 user verbatim「請確保有確保產出不打折且產出完美,且未來也必須要確保產出不打折以產出為完美的前提跑 knowledge prune deep」)。governance headroom / session-start trigger 命中時 scope 聚焦觸發點,否則 quarterly scope。**禁問 user「要不要跑」**(anchor:2026-06-11 user verbatim「deep audit cross codex不是會自動…跑 knowledge prune deep?為何每次都要問我是否要跑?」)。分權不變:P0+P1(表達層對齊/清 stale)AUTO 執行;**P2(retire 機械防線 / 動 canonical)整理成候選表進 C.1 拍板清單** — user 只拍板 P2 內容,不拍板「跑不跑」。
190
188
 
189
+ ### C.0b — 判準化 harvest(predicate-ization,2026-07-07 user 拍板治理進化方向 1)
190
+
191
+ Deep-audit 收尾(C.0a 旁)**必跑**:讀 `node scripts/audit-coverage-matrix.mjs` 的 PURE-JUDGMENT gap list,**選 top 1-3 個本輪已充分理解的 judgment 維度,當場寫成 deterministic script**(invariant .mjs / hook rule);寫不成的必記一行「為何不能謂詞化」(品味 / 需 LLM 讀 source / 外部演進類)。**雙柱模型**:完整稽核 = 永久機構(存量 / 外來 / 漂移 / 防線腐化 / 品味五類永遠需要);謂詞化 = 稽核的機械化引擎——每類問題轉謂詞後,同一謂詞對存量與外來元件全量零抽樣免費掃,稽核不縮編、機械部分逐季變厚。KPI(PURE-JUDGMENT 佔比 trend)由 `/governance-health` 月度追蹤。SSOT → `.claude/planning/2026-07-07-governance-evolution-roadmap.md`。錨例:2026-07-07 selected/active meta 謂詞、VR shifted-clock(當場謂詞化,存量 63 元件一次掃平)。
192
+
191
193
  ### C.0 — 收斂判準(rerun stop gate,2026-06-01)
192
194
 
193
195
  決定「**再 rerun 嗎**」必過此 gate:deep-audit = LLM 對抗式 non-deterministic + 高假陽性,**追零 = 跑步機 + 誘發 regression**。STOP 判準 = **某輪 adversarial 二次驗證後真 material/regression = 0(只剩 marginal + false-positive)**——不追零、不過早收。收斂靠 CI gate + 寫入時紀律,非 audit loop。三分類表 + 「改一處看 N 處」→ `references/triage-rubric.md`「收斂判準」。
@@ -198,17 +200,12 @@ Deep-audit 收尾**必自動跑** `/knowledge-prune` deep — **前提鐵律:確
198
200
  ## Deep Audit Cross-Codex 完整報告(N 日期)
199
201
 
200
202
  ### Phase A 結果
201
- - 全 dim findings: <P0 N / P1 M / P2 K>
202
- - Autonomous landed: <N 項> commit <hash>
203
- - SSOT-UI/UX 已拍板: <M 項>
204
- - SSOT-UI/UX 待拍板: <列出 + 簡述>
203
+ - 全 dim findings: <P0/P1/P2> / Autonomous landed: <N 項> commit <hash>
204
+ - SSOT-UI/UX 已拍板: <M 項> / 待拍板: <列出 + 簡述>
205
205
 
206
206
  ### Phase B 結果
207
- - Codex 抓 + Claude 漏: <N 項>
208
- - Claude + Codex 漏: <M 項>
209
- - Cite battle: <K 題,各題 verdict + evidence>
210
- - 共識 SSOT-UI/UX 待拍板: <列出 + 簡述>
211
- - 共識 autonomous landed: <N 項> commit <hash>
207
+ - Codex 抓 Claude 漏: <N 項> / Claude 抓 Codex 漏: <M 項> / Cite battle: <K 題,各題 verdict + evidence>
208
+ - 共識 SSOT-UI/UX 待拍板: <列出 + 簡述> / 共識 autonomous landed: <N 項> commit <hash>
212
209
 
213
210
  ### 待你拍板(中文人話)
214
211
  <決策 1-N(per A.2 format)>
@@ -216,8 +213,7 @@ Deep-audit 收尾**必自動跑** `/knowledge-prune` deep — **前提鐵律:確
216
213
  **每題必附「SSOT 理由:」一句**(= 為何這是「會影響 SSOT 的 UI/UX 增刪改」:新 API contract / 改 canonical 語意 / 新視覺 design language,三類之一,具體指出)。**寫不出 SSOT 理由 = 該題不是拍板題,移回 AUTO 自己做**(2026-06-11 user 第 3 次糾正 codify:bug fix / a11y 對齊 W3C / 對齊 spec 既有意圖 / story 內容 / 治理 / 補 rationale 文件 / dead code 清除,全部 AUTO 不問)。Hook \`check_audit_post_report_validator.sh\` Validator H 機械強制。
217
214
 
218
215
  ### Verify artifact
219
- - tsc PASS / invariant PASS / content-quality PASS / visual probe PASS
220
- - file:line + before / after diff link
216
+ - tsc / invariant / content-quality / visual probe PASS + file:line + diff link;**backlog 項必附 verifyCmd+fixedSignal(可攜指令),對帳 = `node scripts/verify-backlog.mjs`(2026-07-07 軌道 3;anchor:C1 十筆考古 9 筆早已修)**
221
217
  ```
222
218
 
223
219
  ### C.2 — Push trigger gate(M28 solo-work canonical)
@@ -60,6 +60,11 @@ Purpose: 產品 / feature 通過 prototype → audit → stakeholder 決策後
60
60
 
61
61
  ### Phase 1 — Inventory 生成
62
62
 
63
+ **Deterministic 源優先(2026-07-07 治理進化收尾:本 skill 原「幾乎零機械依賴」列冊補強)**:
64
+ - Component / story 母集 = `node scripts/gen-ds-story-manifest.mjs` 產出的 manifest(禁憑記憶列)
65
+ - Token 使用 = `rg -o -- '--[a-z-]+' <feature files> | sort | uniq -c`(機械計數,非印象)
66
+ - 消費統計交叉驗 `scripts/audit-orphan-tokens.mjs` 的消費通道邏輯(同一套 5 通道定義)
67
+
63
68
  scan 目標 feature 的 code,自動 inventory:
64
69
 
65
70
  1. **Component 清單**(哪些 DS 元件被此 feature 消費):
@@ -74,7 +74,7 @@ Grouped by theme. Each runs as an independent subagent; many can parallelize.
74
74
 
75
75
  | # | Audit | What it catches |
76
76
  |---|-------|-----------------|
77
- | 14 | **命名一致性** | PascalCase folder / kebab-case file / hook naming / spec chapter 中文 / identifier 英文 / single-file 語言統一 |
77
+ | 14 | **命名一致性** | 結構核心已謂詞化:`node scripts/naming-structure-invariant.mjs`(PascalCase folder / kebab file / 主檔對應,2026-07-07 C.0b harvest 首跑);語義部分留 judgment:hook naming / spec chapter 中文 / identifier 英文 / single-file 語言統一 |
78
78
  | 15 | **Cross-doc 一致性** | CLAUDE.md 自身 + cross-spec full dup(Rule-of-3)+ tsx docblock-spec drift + stale upgrade markers + **(2026-06-04 加)docblock-vs-同檔-code + spec 段內描述性一致(Mode 表 / typography / padding / gap cross-section)**。詳 `audit-prompts.md` Dim 15。**Enforcement 升級**:本 dim 易被「散文 skim」淺跑漏(2026-06-04 anchor:tsx docblock `閱讀模式`/`hover-bg` 自 2026-04-23 stale 多次沒抓)→ 改由 `/deep-audit-cross-codex` A.1b **強制 adversarial per-component**(Validator F 覆蓋率閘),不再可 skim |
79
79
 
80
80
  ### Group F — Architecture compliance (P1 priority, session-learned)
@@ -43,6 +43,7 @@ ls -la .claude/logs/*.jsonl
43
43
  | **File size trend**(weekly snapshot)| `metric-snapshots.jsonl`(若存在)| CLAUDE.md 增速 > 5 line/week = sprawl alert |
44
44
  | **Benchmark freshness**(external)| `.claude/benchmarks/last-fetch.txt` | > 30 天 = 過期 |
45
45
  | **Infra-ref integrity**(2026-05-30 加,根因防線)| `node scripts/check-dangling-infra-ref.mjs` + `node scripts/check-skill-deadref.mjs`(fail-open report)| bucket-B > 0(死 hook ref)OR removed-section/line-number ref > 0 = infra-self drift → 提議 repoint。錨例:2026-05-30 抓 40 處死 hook ref + env-smoke set-e bug |
46
+ | **判準化佔比 trend**(2026-07-07 加,治理進化方向 1 KPI)| `node scripts/audit-coverage-matrix.mjs`(PURE-JUDGMENT / HOOK / DETERMINISTIC 三級計數)| PURE-JUDGMENT 佔比應逐月持平或降;連 2 月上升 = deep-audit C.0b 判準化 harvest 沒在跑 → flag。SSOT → `.claude/planning/2026-07-07-governance-evolution-roadmap.md` |
46
47
 
47
48
  ### Phase 2 — Analysis(fire-driven auto-propose)
48
49
 
@@ -47,6 +47,7 @@ description: Create-phase workflow for building a new design-system component fr
47
47
  - SSOT anchor(往哪指)
48
48
  3. **查世界級對照**:Polaris / Material / Atlassian / Ant / Apple HIG 有沒對應元件?**至少 2 個** DS 的做法,記 3 行筆記(naming / API / 視覺模式)。
49
49
  4. **查 baseline 狀況**:`ls packages/design-system/src/components/` 確認名字衝突;`grep` CLAUDE.md「失敗記憶索引」看有沒同類別的歷史 bug。
50
+ 5. **產出「primitive 覆蓋對照表」artifact**(2026-07-07 治理進化方向 3 洞 c,強制產物非心算):逐 anatomy 段列「這段 × 消費哪個既有 primitive」——row → item-anatomy、浮層 → overlay-surface、chrome 標題列 → ChromeHeader、橫向溢出 → horizontal-overflow、輸入 chrome → field-wrapper、pill → Button「Pill Layout」。**每段必有著落;對不上的段才准自建,且必附「自建 + 理由」**(進 spec 定位段)。寫 tsx 時此表轉錄為檔頭「── 消費的 SSOT ──」段(hook `check_ssot_header_declaration.sh` P0 驗新檔必有)。
50
51
 
51
52
  ### Checkpoint 1 — 定位 Proposal(STOP 點)
52
53
 
@@ -54,6 +55,7 @@ description: Create-phase workflow for building a new design-system component fr
54
55
  - 元件名稱(按 CLAUDE.md `# 命名與語言一致性` 三重 test)
55
56
  - 近親元件清單 + 跟本元件的異同一句話
56
57
  - 世界級對照 2 個(Ant Design 叫 X / Material 叫 Y)
58
+ - **primitive 覆蓋對照表**(Phase 1 step 5 artifact;自建段落 + 理由醒目標出)
57
59
  - **問 user**:「這個 positioning 對嗎?要不要改名 / 重 scope?」
58
60
 
59
61
  **User 點頭才進 Phase 2。** 定位錯的話在此階段轉向最便宜。
@@ -47,7 +47,7 @@ description: Performance audit for design-system components and product UI. Chec
47
47
 
48
48
  **工具**:
49
49
  - React DevTools Profiler record → 看 flame graph
50
- - `why-did-you-render` dev dep 觸發警告(未來可加)
50
+ - `why-did-you-render` dev dep 觸發警告(**尚未落地**;trigger = 下一個 render-storm 類 bug 實例時一併引入,roadmap 殘項列冊)
51
51
  - 直接 grep pattern(inline style / arrow onClick)
52
52
 
53
53
  ### Phase 2 — Memoization / dependency
@@ -66,7 +66,7 @@ description: Performance audit for design-system components and product UI. Chec
66
66
 
67
67
  **工具**:
68
68
  - `npx vite build --report`(vite bundle visualizer)
69
- - bundle-size CI check(未來可加)
69
+ - **bundle-size gate(已落地 2026-07-07)**:`npm run check:bundle-size`(budget SSOT = `packages/design-system/bundle-budget.json`,total + top-8 entry 各 +10% headroom;release-preflight 內建必跑;蓄意增大 → `--init` 更新 budget + commit 說明)
70
70
 
71
71
  ### Phase F — Report(必 STOP,對齊分權 canonical)
72
72
 
@@ -52,6 +52,14 @@ description: Pixel-level visual audit for design-system components based on user
52
52
  - 沒有 screenshot → **本 skill 拒跑**(見 Preconditions)
53
53
  - 只要看一眼順不順 → 不做 audit(這不 mechanical)
54
54
 
55
+ ## Baseline 更新鐵律(2026-07-07 軌道 5 codify)
56
+
57
+ **覆蓋任何 snapshots-baseline/*.png 前,模型必 Read 新舊兩張圖並寫出「觀察到的具體差異」**
58
+ (diff 百分比是訊號、圖才是真相)。禁只看 pct 就 cp。錨例:2026-07-07 VR 換日 bug——釘日期後
59
+ pct 一模一樣,只有看圖才發現 today 圈仍在真實日期(元件內部時間);FileViewer 40.3% 只有看圖
60
+ 才知道是 zoom 94→100(fit 被凍壞)非渲染炸裂。配套:roadmap 方向 7 accept-baseline workflow
61
+ (script 化時「看圖步驟」不可省)。
62
+
55
63
  ## Preconditions(硬規則)
56
64
 
57
65
  **本 skill 在下列任一缺失下拒跑,回報 user 補齊後再 invoke**:
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @qijenchen/design-system — 完整設計參考(llms-full)
2
2
 
3
- > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.82
3
+ > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.83
4
4
 
5
5
  # Components
6
6
 
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qijenchen/design-system
2
2
 
3
3
  > World-class React design system(Radix/shadcn + Tailwind v4 + 自訂 design token)。
4
- > 56 components + 4 public patterns + design tokens。v0.1.0-beta.82
4
+ > 56 components + 4 public patterns + design tokens。v0.1.0-beta.83
5
5
 
6
6
  本檔由 source(spec.md frontmatter + Storybook index)build-time 自動生成,**禁手改**(CI --check drift gate 守)。
7
7
  每元件 / pattern 的完整 variants / sizes / 禁止事項 全文見 [llms-full.txt](./llms-full.txt)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qijenchen/design-system",
3
- "version": "0.1.0-beta.82",
3
+ "version": "0.1.0-beta.83",
4
4
  "private": false,
5
5
  "description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
6
6
  "type": "module",
@@ -8,6 +8,7 @@ traits:
8
8
  benchmark:
9
9
  - Ant Design Upload (file list): github.com/ant-design/ant-design/tree/master/components/upload
10
10
  - Polaris Thumbnail: github.com/Shopify/polaris/tree/main/polaris-react/src/components/Thumbnail
11
+ - shadcn Attachment (2026-06 chat 套件): ui.shadcn.com/docs/components/attachment
11
12
  ---
12
13
 
13
14
  <!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
@@ -472,6 +473,14 @@ FileItem 決策維度是 `mode`(compact / rich)× `status`(uploading / completed
472
473
 
473
474
  ColorMatrix 已建:展示 status × 元素(filename / description / progress bar / status icon)色彩矩陣,明示 status 只驅動 **progress bar + status icon + description** 升階,**不染容器背景**(避免整 row 轉紅蓋過其他 metadata)。容器本身無 hover-bg / selected / disabled state——FileItem 三型態皆 permanent-anchored,反向於 MenuItem / DataTable flush row 的 hover-bg primitive(詳「Hover 行為 canonical」段),且 interface 無 `disabled` prop。
474
475
 
476
+ ## 與 shadcn Attachment 的分界(2026-07-07 codify,目錄新增元件謂詞 anchor)
477
+
478
+ shadcn 2026-06 chat 套件的 Attachment(ui.shadcn.com/docs/components/attachment)與 FileItem 同情境(附件 + 上傳狀態 + 動作)。**不遷移架構、不改名**:Attachment 為純組合式(無 Radix primitive 核心,AttachmentAction 即其 Button),無 primitive 增益且缺我們的 item-anatomy 幾何深度與 ProgressBar 量化 a11y(其進度僅 title shimmer,SR 無量化值);shadcn 為 copy-in scaffold 無上游更新流入;改名破壞 File* 家族(FileUpload / FileViewer)+ npm breaking + `completed` lifecycle family(props-naming.md)。判準 SSOT → `ui-development.md`「shadcn 目錄後續新增元件 vs DS 既有」。
479
+
480
+ **Known-gaps(對照後承認、留 anchor,現無產品需求不動)**:
481
+ - `processing` / `idle` state:Attachment 5 態 enum 可表達「傳完但伺服器處理中」(掃毒 / transcode / AI ingestion);我們 3 態 + undefined 表達不了 — 未來需求出現時走 prop 演進 ASK
482
+ - AttachmentTrigger stacking-order pattern(整卡鍵盤可達、actions 各自 focusable、不觸 nested-interactive — 解了我們明文 punt 的「整列 Enter 開啟」trade-off,見「Hover 行為 canonical」)與 `orientation="vertical"` + Group 橫向 scroll(chat composer tile;gallery 既定走 Grid / Carousel):皆屬 SSOT-affecting,對應需求出現時 ASK 再議
483
+
475
484
  ---
476
485
 
477
486
  ## 相關