@harness-engineering/cli 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/agents/skills/claude-code/align-design-system/SKILL.md +174 -0
  2. package/dist/agents/skills/claude-code/align-design-system/skill.yaml +57 -0
  3. package/dist/agents/skills/claude-code/audit-brand-compliance/SKILL.md +189 -0
  4. package/dist/agents/skills/claude-code/audit-brand-compliance/skill.yaml +50 -0
  5. package/dist/agents/skills/claude-code/audit-component-anatomy/SKILL.md +181 -0
  6. package/dist/agents/skills/claude-code/audit-component-anatomy/skill.yaml +51 -0
  7. package/dist/agents/skills/claude-code/copy-craft/SKILL.md +192 -0
  8. package/dist/agents/skills/claude-code/copy-craft/skill.yaml +52 -0
  9. package/dist/agents/skills/claude-code/detect-design-drift/SKILL.md +139 -0
  10. package/dist/agents/skills/claude-code/detect-design-drift/skill.yaml +50 -0
  11. package/dist/agents/skills/claude-code/harness-accessibility/SKILL.md +10 -0
  12. package/dist/agents/skills/claude-code/harness-design-craft/SKILL.md +212 -0
  13. package/dist/agents/skills/claude-code/harness-design-craft/skill.yaml +57 -0
  14. package/dist/agents/skills/claude-code/harness-design-pipeline/SKILL.md +266 -0
  15. package/dist/agents/skills/claude-code/harness-design-pipeline/skill.yaml +70 -0
  16. package/dist/agents/skills/claude-code/knowledge-craft/SKILL.md +180 -0
  17. package/dist/agents/skills/claude-code/knowledge-craft/skill.yaml +49 -0
  18. package/dist/agents/skills/claude-code/naming-craft/SKILL.md +172 -0
  19. package/dist/agents/skills/claude-code/naming-craft/skill.yaml +51 -0
  20. package/dist/agents/skills/claude-code/security-craft/SKILL.md +205 -0
  21. package/dist/agents/skills/claude-code/security-craft/skill.yaml +58 -0
  22. package/dist/agents/skills/claude-code/spec-craft/SKILL.md +184 -0
  23. package/dist/agents/skills/claude-code/spec-craft/skill.yaml +55 -0
  24. package/dist/agents/skills/claude-code/test-craft/SKILL.md +212 -0
  25. package/dist/agents/skills/claude-code/test-craft/skill.yaml +58 -0
  26. package/dist/agents/skills/codex/align-design-system/SKILL.md +174 -0
  27. package/dist/agents/skills/codex/align-design-system/skill.yaml +57 -0
  28. package/dist/agents/skills/codex/audit-brand-compliance/SKILL.md +189 -0
  29. package/dist/agents/skills/codex/audit-brand-compliance/skill.yaml +50 -0
  30. package/dist/agents/skills/codex/audit-component-anatomy/SKILL.md +181 -0
  31. package/dist/agents/skills/codex/audit-component-anatomy/skill.yaml +51 -0
  32. package/dist/agents/skills/codex/copy-craft/SKILL.md +192 -0
  33. package/dist/agents/skills/codex/copy-craft/skill.yaml +52 -0
  34. package/dist/agents/skills/codex/detect-design-drift/SKILL.md +139 -0
  35. package/dist/agents/skills/codex/detect-design-drift/skill.yaml +50 -0
  36. package/dist/agents/skills/codex/harness-accessibility/SKILL.md +10 -0
  37. package/dist/agents/skills/codex/harness-design-craft/SKILL.md +212 -0
  38. package/dist/agents/skills/codex/harness-design-craft/skill.yaml +57 -0
  39. package/dist/agents/skills/codex/harness-design-pipeline/SKILL.md +266 -0
  40. package/dist/agents/skills/codex/harness-design-pipeline/skill.yaml +70 -0
  41. package/dist/agents/skills/codex/knowledge-craft/SKILL.md +180 -0
  42. package/dist/agents/skills/codex/knowledge-craft/skill.yaml +49 -0
  43. package/dist/agents/skills/codex/naming-craft/SKILL.md +172 -0
  44. package/dist/agents/skills/codex/naming-craft/skill.yaml +51 -0
  45. package/dist/agents/skills/codex/security-craft/SKILL.md +205 -0
  46. package/dist/agents/skills/codex/security-craft/skill.yaml +58 -0
  47. package/dist/agents/skills/codex/spec-craft/SKILL.md +184 -0
  48. package/dist/agents/skills/codex/spec-craft/skill.yaml +55 -0
  49. package/dist/agents/skills/codex/test-craft/SKILL.md +212 -0
  50. package/dist/agents/skills/codex/test-craft/skill.yaml +58 -0
  51. package/dist/agents/skills/cursor/align-design-system/SKILL.md +174 -0
  52. package/dist/agents/skills/cursor/align-design-system/skill.yaml +57 -0
  53. package/dist/agents/skills/cursor/audit-brand-compliance/SKILL.md +189 -0
  54. package/dist/agents/skills/cursor/audit-brand-compliance/skill.yaml +50 -0
  55. package/dist/agents/skills/cursor/audit-component-anatomy/SKILL.md +181 -0
  56. package/dist/agents/skills/cursor/audit-component-anatomy/skill.yaml +51 -0
  57. package/dist/agents/skills/cursor/copy-craft/SKILL.md +192 -0
  58. package/dist/agents/skills/cursor/copy-craft/skill.yaml +52 -0
  59. package/dist/agents/skills/cursor/detect-design-drift/SKILL.md +139 -0
  60. package/dist/agents/skills/cursor/detect-design-drift/skill.yaml +50 -0
  61. package/dist/agents/skills/cursor/harness-accessibility/SKILL.md +10 -0
  62. package/dist/agents/skills/cursor/harness-design-craft/SKILL.md +212 -0
  63. package/dist/agents/skills/cursor/harness-design-craft/skill.yaml +57 -0
  64. package/dist/agents/skills/cursor/harness-design-pipeline/SKILL.md +266 -0
  65. package/dist/agents/skills/cursor/harness-design-pipeline/skill.yaml +70 -0
  66. package/dist/agents/skills/cursor/knowledge-craft/SKILL.md +180 -0
  67. package/dist/agents/skills/cursor/knowledge-craft/skill.yaml +49 -0
  68. package/dist/agents/skills/cursor/naming-craft/SKILL.md +172 -0
  69. package/dist/agents/skills/cursor/naming-craft/skill.yaml +51 -0
  70. package/dist/agents/skills/cursor/security-craft/SKILL.md +205 -0
  71. package/dist/agents/skills/cursor/security-craft/skill.yaml +58 -0
  72. package/dist/agents/skills/cursor/spec-craft/SKILL.md +184 -0
  73. package/dist/agents/skills/cursor/spec-craft/skill.yaml +55 -0
  74. package/dist/agents/skills/cursor/test-craft/SKILL.md +212 -0
  75. package/dist/agents/skills/cursor/test-craft/skill.yaml +58 -0
  76. package/dist/agents/skills/gemini-cli/align-design-system/SKILL.md +174 -0
  77. package/dist/agents/skills/gemini-cli/align-design-system/skill.yaml +57 -0
  78. package/dist/agents/skills/gemini-cli/audit-brand-compliance/SKILL.md +189 -0
  79. package/dist/agents/skills/gemini-cli/audit-brand-compliance/skill.yaml +50 -0
  80. package/dist/agents/skills/gemini-cli/audit-component-anatomy/SKILL.md +181 -0
  81. package/dist/agents/skills/gemini-cli/audit-component-anatomy/skill.yaml +51 -0
  82. package/dist/agents/skills/gemini-cli/copy-craft/SKILL.md +192 -0
  83. package/dist/agents/skills/gemini-cli/copy-craft/skill.yaml +52 -0
  84. package/dist/agents/skills/gemini-cli/detect-design-drift/SKILL.md +139 -0
  85. package/dist/agents/skills/gemini-cli/detect-design-drift/skill.yaml +50 -0
  86. package/dist/agents/skills/gemini-cli/harness-accessibility/SKILL.md +10 -0
  87. package/dist/agents/skills/gemini-cli/harness-design-craft/SKILL.md +212 -0
  88. package/dist/agents/skills/gemini-cli/harness-design-craft/skill.yaml +57 -0
  89. package/dist/agents/skills/gemini-cli/harness-design-pipeline/SKILL.md +266 -0
  90. package/dist/agents/skills/gemini-cli/harness-design-pipeline/skill.yaml +70 -0
  91. package/dist/agents/skills/gemini-cli/knowledge-craft/SKILL.md +180 -0
  92. package/dist/agents/skills/gemini-cli/knowledge-craft/skill.yaml +49 -0
  93. package/dist/agents/skills/gemini-cli/naming-craft/SKILL.md +172 -0
  94. package/dist/agents/skills/gemini-cli/naming-craft/skill.yaml +51 -0
  95. package/dist/agents/skills/gemini-cli/security-craft/SKILL.md +205 -0
  96. package/dist/agents/skills/gemini-cli/security-craft/skill.yaml +58 -0
  97. package/dist/agents/skills/gemini-cli/spec-craft/SKILL.md +184 -0
  98. package/dist/agents/skills/gemini-cli/spec-craft/skill.yaml +55 -0
  99. package/dist/agents/skills/gemini-cli/test-craft/SKILL.md +212 -0
  100. package/dist/agents/skills/gemini-cli/test-craft/skill.yaml +58 -0
  101. package/dist/{agents-md-2JUQ4FE2.js → agents-md-267R2NUJ.js} +4 -4
  102. package/dist/{analyzer-VY6DJVOU-4AHIJLNS.js → analyzer-VY6DJVOU-WYKV3TTW.js} +2 -2
  103. package/dist/{architecture-CXAVNB5X.js → architecture-ZTEHGIHD.js} +6 -6
  104. package/dist/{assess-project-M626SSPN.js → assess-project-OKDW3YSK.js} +2 -1
  105. package/dist/bin/harness-mcp.js +17 -17
  106. package/dist/bin/harness.js +27 -27
  107. package/dist/{check-phase-gate-XMU6LLUQ.js → check-phase-gate-YJWFDW66.js} +5 -5
  108. package/dist/{chunk-372MGNTI.js → chunk-4EGPFB7V.js} +6185 -124
  109. package/dist/{chunk-HFOB2MPQ.js → chunk-67RP7MLR.js} +3 -3
  110. package/dist/{chunk-5JNSFYZU.js → chunk-6T5AYG5S.js} +6 -6
  111. package/dist/{chunk-5XLLIYYL.js → chunk-6UHDQP2N.js} +1 -1
  112. package/dist/{chunk-YP7I25SL.js → chunk-B3B64OPD.js} +1 -1
  113. package/dist/{chunk-VTTNIZJL.js → chunk-B4OLYXZ4.js} +93 -3
  114. package/dist/{chunk-6AX6R3LM.js → chunk-CXAHK5PJ.js} +9 -9
  115. package/dist/{chunk-BCFDGOMA.js → chunk-H2ZJOJ7A.js} +81 -1
  116. package/dist/{chunk-CQOMVZDC.js → chunk-HLPY3RHE.js} +1641 -292
  117. package/dist/{chunk-Q6ZR2L6Q.js → chunk-HMMSWRWR.js} +2 -2
  118. package/dist/{chunk-YYRQCQPZ.js → chunk-IY2TZLY5.js} +176 -107
  119. package/dist/{chunk-I6K43QQS.js → chunk-MQG7FHXX.js} +3 -3
  120. package/dist/{chunk-43M3RLFQ.js → chunk-OLTVDPA2.js} +2 -2
  121. package/dist/{chunk-JFZOWO7D.js → chunk-OQE6ME2Z.js} +1 -1
  122. package/dist/{chunk-GWE5LL3R.js → chunk-P7EO7USC.js} +10 -10
  123. package/dist/{chunk-24KCOJJG.js → chunk-PARV3G2F.js} +1 -1
  124. package/dist/{chunk-QOKMDDBJ.js → chunk-QUPC37UB.js} +6 -6
  125. package/dist/{chunk-EPUKTTJZ.js → chunk-RC3OI5NK.js} +5 -1
  126. package/dist/{chunk-L5BCZ5XS.js → chunk-S7GEONXL.js} +1 -1
  127. package/dist/{chunk-5FDBXUFW.js → chunk-TIH53QXY.js} +1 -1
  128. package/dist/{chunk-OA6SWTU4.js → chunk-TIZ3L6CU.js} +11 -8
  129. package/dist/{chunk-GWF2OILZ.js → chunk-VG6UK6G6.js} +1 -1
  130. package/dist/{chunk-SZEFU6XC.js → chunk-W3PDVZ4I.js} +1 -1
  131. package/dist/{chunk-VUCGB2KH.js → chunk-WOXR6I3R.js} +1 -1
  132. package/dist/{chunk-K246XQ2L.js → chunk-XTIPZUPR.js} +1 -1
  133. package/dist/{chunk-HMW3VKUF.js → chunk-Z2GUITAS.js} +10 -10
  134. package/dist/{chunk-PFZEADQM.js → chunk-ZKA3TGYS.js} +79 -2
  135. package/dist/{ci-workflow-3JNDZQYD.js → ci-workflow-YGTQTNVC.js} +4 -4
  136. package/dist/{dist-QPWXPCPL.js → dist-R3TOOPJ7.js} +1 -1
  137. package/dist/{dist-OHDQBTSO.js → dist-XIJWWQ5N.js} +3 -3
  138. package/dist/{docs-5RYMDHN3.js → docs-KCTLXK2G.js} +6 -6
  139. package/dist/{engine-GKGT5ULT.js → engine-J64WPH5K.js} +4 -4
  140. package/dist/{entropy-DQTAPSSE.js → entropy-M27JUL3V.js} +5 -5
  141. package/dist/{feedback-WH6XPQJS.js → feedback-W25QO3OL.js} +2 -2
  142. package/dist/{generate-agent-definitions-SZA4QIHN.js → generate-agent-definitions-RNMU5U5R.js} +5 -5
  143. package/dist/{glob-helper-XE2UGEOV.js → glob-helper-EX4YZKZO.js} +1 -1
  144. package/dist/{graph-loader-EQBOIHFZ.js → graph-loader-EIFEOUVI.js} +1 -1
  145. package/dist/index.d.ts +587 -6
  146. package/dist/index.js +27 -27
  147. package/dist/{loader-BNKGM23C.js → loader-HZKJ5ABV.js} +4 -4
  148. package/dist/{mcp-YKKPWUMH.js → mcp-NEAPIDTT.js} +17 -17
  149. package/dist/{performance-54TMJOEU.js → performance-3AVQUDRG.js} +6 -6
  150. package/dist/{review-pipeline-OTDDVPNY.js → review-pipeline-KA7SGUXJ.js} +4 -4
  151. package/dist/{runtime-W5THSNAL.js → runtime-IP6FVV5M.js} +4 -4
  152. package/dist/{scan-4PPP6ZXT.js → scan-LYKLM4EQ.js} +1 -1
  153. package/dist/{security-27HW7CYT.js → security-EAR4FJWI.js} +1 -1
  154. package/dist/{validate-QRVCD4GP.js → validate-EVH3UXIC.js} +5 -5
  155. package/dist/{validate-cross-check-BUAGBAPO.js → validate-cross-check-SNE3SQOB.js} +4 -4
  156. package/package.json +8 -7
@@ -0,0 +1,51 @@
1
+ name: audit-component-anatomy
2
+ version: "0.1.0"
3
+ description: Audit component definitions for missing required anatomy parts (slots, states, sizes) and detect missing-anatomy-component patterns (data without empty states, async without loading boundaries). First programmatic enforcer of component-anatomy rules.
4
+ stability: draft
5
+ cognitive_mode: constructive-architect
6
+ triggers:
7
+ - manual
8
+ - on_new_feature
9
+ platforms:
10
+ - claude-code
11
+ tools:
12
+ - Bash
13
+ - Read
14
+ - Write
15
+ - Edit
16
+ - Glob
17
+ - Grep
18
+ cli:
19
+ command: harness skill run audit-component-anatomy
20
+ args:
21
+ - name: path
22
+ description: Project root path
23
+ required: false
24
+ - name: mode
25
+ description: "fast (definition findings only) or full (definitions + patterns)"
26
+ required: false
27
+ - name: files
28
+ description: Optional glob/path scoping
29
+ required: false
30
+ mcp:
31
+ tool: run_skill
32
+ input:
33
+ skill: audit-component-anatomy
34
+ path: string
35
+ type: rigid
36
+ tier: 2
37
+ phases:
38
+ - name: scan
39
+ description: Identify component types and run convention/pattern audits
40
+ required: true
41
+ - name: evaluate
42
+ description: Apply severity model based on design.strictness
43
+ required: true
44
+ - name: report
45
+ description: Format findings as report + graph edges
46
+ required: true
47
+ state:
48
+ persistent: false
49
+ depends_on:
50
+ - design-component-anatomy
51
+ - harness-accessibility
@@ -0,0 +1,192 @@
1
+ # Copy Craft
2
+
3
+ > LLM-judgment critique of prose-in-code across six surfaces: error messages, log lines, CLI output strings, commit subjects, PR descriptions, and code comments. Primary domain is error messages (universally bad in most codebases). Third member of the craft-pipeline initiative. NO rule-based floor exists — pure ceiling. Emits 3-axis findings (tier × impact × confidence per ADR 0019).
4
+
5
+ ## When to Use
6
+
7
+ - During PR review on code that adds or changes error messages, log lines, or CLI output
8
+ - After a feature ships, to audit error-message quality across the changed surfaces
9
+ - Periodically (per-release) to catch accumulated noise in log lines + comment rot
10
+ - As the user-facing-copy critic alongside design-craft (which owns UI copy)
11
+ - NOT for UI copy in components (use design-craft)
12
+ - NOT for prose documentation in `docs/` (use docs-craft #2 when it ships)
13
+ - NOT for autofix / rewriting (this is judgment-only; v2 may ship `align-copy`)
14
+ - NOT for JSDoc / TSDoc structured API docs (docs-craft territory)
15
+ - NOT for non-TS/JS languages in v1 (v1.x)
16
+
17
+ ## Process
18
+
19
+ ### Phase 1: EXTRACT — Six surfaces, three infrastructures
20
+
21
+ 1. **Read project configuration.** Check `harness.config.json` for:
22
+ - `craft.copy.enabled` — gate (default `true`)
23
+ - `craft.copy.surfaces` — restrict to specific surfaces (default: all 6)
24
+ - `craft.copy.maxFiles` (default 100), `craft.copy.maxItemsPerFile` (default 20)
25
+ - `craft.copy.commitsSince` (default `'1 month ago'`), `craft.copy.prLimit` (default 20)
26
+
27
+ 2. **Source-side surfaces** (errors / logs / CLI output / comments): single TS Compiler API walk per source file. Amortizes parse cost across surfaces.
28
+ - **errors:** `throw new <X>Error("...")` where the constructor name ends in `Error`; also `Err({ message: "..." })` for Result-style returns
29
+ - **logs:** `console.log/info/warn/error/debug` and `logger.X` / `log.X` / `pino.X` / `winston.X` where X is a known level
30
+ - **cli-output:** strings inside files under `packages/*/src/commands/` (configurable via `cliOutputPaths`); takes precedence over `log` for files matching the glob
31
+ - **comments:** `ts.getLeadingCommentRanges()` + `getTrailingCommentRanges()`; excludes JSDoc and license banners
32
+
33
+ 3. **Git surface** (commits): shell-out to `git log --pretty=format:'%H%x09%s' --since=...`. Skip silently when not in a git repo.
34
+
35
+ 4. **GitHub surface** (PR descriptions): shell-out to `gh pr list --json number,title,body`. Skip silently when `gh` binary missing or `gh auth status` fails.
36
+
37
+ 5. **Skipped surfaces** recorded in `summary.skippedSurfaces` with the reason — visible in the report, not a failure.
38
+
39
+ ### Phase 2: CRITIQUE — Per (item, rubric) loop, surface-filtered
40
+
41
+ 8 seed rubrics, each declares which surfaces it applies to:
42
+
43
+ | Rubric | Surfaces |
44
+ | ------------------------------------- | ------------------------------- |
45
+ | `COPY-R001` WHAT/WHY/HOW-TO-FIX | error |
46
+ | `COPY-R002` calm-not-panicky | error, log |
47
+ | `COPY-R003` specific-not-generic | error, log, cli-output |
48
+ | `COPY-R004` signal-not-noise | log |
49
+ | `COPY-R005` grep-survives | log, cli-output |
50
+ | `COPY-R006` describes-change-not-work | commit, pr-description |
51
+ | `COPY-R007` stranger-in-6-months | commit, pr-description, comment |
52
+ | `COPY-R008` WHY-not-WHAT | comment |
53
+
54
+ For each (item, rubric) where the rubric applies to the item's surface:
55
+
56
+ 1. Build prompt with rubric description + surface + context (errorType / logLevel / ref) + snippet (truncated to 1500 chars).
57
+ 2. LLM returns fenced JSON: `null` (rubric doesn't apply / copy is fine) OR `{ tier, impact, confidence, message }`.
58
+ 3. On non-null: emit `CopyFinding` with `cite.rubricId` for ADR 0020 traceability.
59
+
60
+ ### Phase 3: REPORT — Aggregate + cost telemetry
61
+
62
+ Emit `CopyCraftOutput`:
63
+
64
+ ```ts
65
+ {
66
+ findings: CopyFinding[];
67
+ summary: {
68
+ phaseRun: ['critique'];
69
+ durationMs: number;
70
+ llmCalls: { provider, model, count, costUsd };
71
+ catalog: { rubricsApplied: string[]; surfacesScanned: CopySurface[] };
72
+ counts: Record<CopySurface, number>;
73
+ skippedSurfaces: Array<{ surface, reason }>;
74
+ runId: string;
75
+ }
76
+ }
77
+ ```
78
+
79
+ ## Harness Integration
80
+
81
+ - **`harness copy-craft`** — CLI entry. `--files` / `--surfaces` / `--max-files` / `--max-items-per-file` / `--commits-since` / `--pr-limit` / `--json` / `--verbose`.
82
+ - **`mcp__harness__copy_craft`** — MCP tool. Same input/output. Consumed by agents.
83
+ - **Cross-cutting API:** `critiqueCopyInFile(file, opts)` exported. Source-side surfaces only; git surfaces are project-scoped.
84
+ - **Shared craft infrastructure:** imports `LlmProvider` + 3-axis types + `derivePriority` from `packages/cli/src/shared/craft/` (extracted by spec-craft).
85
+
86
+ ## Success Criteria
87
+
88
+ See `docs/changes/craft-pipeline/copy-craft/proposal.md` for the full 39 success criteria. Highlights:
89
+
90
+ - 8 seed rubrics ship in `catalog/rubrics/<id>.ts` (file-per-rubric, matches naming/spec-craft)
91
+ - 3-axis output preserved (tier × impact × confidence, never collapsed)
92
+ - `cite.rubricId` populated on every finding (ADR 0020)
93
+ - Single TS AST walk amortizes parse cost across 4 source surfaces
94
+ - Graceful degradation: `summary.skippedSurfaces` records when git/gh surfaces couldn't run
95
+ - Cross-cutting `critiqueCopyInFile` exported (source surfaces only)
96
+
97
+ ## Examples
98
+
99
+ ### Example: Generic error message
100
+
101
+ **Input:** `src/parse.ts`:
102
+
103
+ ```ts
104
+ throw new Error('parse error');
105
+ ```
106
+
107
+ **Output (mock LLM):**
108
+
109
+ ```
110
+ [error]
111
+ COPY-R001 [foundational/large/medium] src/parse.ts:14 error
112
+ "parse error"
113
+ Doesn't tell WHAT was being parsed, WHY it failed, or HOW the user can
114
+ recover. Try: "Failed to parse design-system/tokens.json at line 12: ..."
115
+ COPY-R003 [polish/medium/high] src/parse.ts:14 error
116
+ "parse error"
117
+ Generic — no operation, no artifact. Name the file being parsed and the
118
+ specific failure mode.
119
+ ```
120
+
121
+ ### Example: Noisy log line
122
+
123
+ **Input:**
124
+
125
+ ```ts
126
+ console.log('entered function');
127
+ ```
128
+
129
+ **Output:**
130
+
131
+ ```
132
+ [log]
133
+ COPY-R004 [foundational/medium/high] src/handler.ts:23 log
134
+ "entered function"
135
+ Pure noise — fires on every invocation; carries no state or decision.
136
+ Either remove or replace with a state-transition log at the relevant
137
+ boundary.
138
+ ```
139
+
140
+ ### Example: Work-not-change commit subject
141
+
142
+ **Input:** A commit with subject `"update tests"`.
143
+
144
+ **Output:**
145
+
146
+ ```
147
+ [commit]
148
+ COPY-R006 [polish/medium/medium] git:abc1234 commit
149
+ "update tests"
150
+ Describes the work, not the change. A reader six months from now needs
151
+ to know what behaviour changed. Try: "ratchet drift threshold to 0.5%
152
+ after Hermes Phase 4 baseline reset" or similar.
153
+ ```
154
+
155
+ ### Example: Missing prerequisites — graceful skip
156
+
157
+ When not in a git repo OR `gh` is missing/unauthenticated, those surfaces silently skip and the report shows:
158
+
159
+ ```
160
+ Skipped surfaces:
161
+ - commit: not a git repo
162
+ - pr-description: gh binary not found
163
+ ```
164
+
165
+ ## Gates
166
+
167
+ - **No autofix.** v2's `align-copy` may add safe rewrites.
168
+ - **No JSDoc / TSDoc.** docs-craft #2 territory.
169
+ - **No PR / review comments.** v1.x.
170
+ - **No commit BODY critique.** Subjects only in v1.
171
+ - **No B' bootstrap.** Same posture as naming/spec-craft.
172
+ - **No graph persistence.** Phase 1 MVP.
173
+ - **No non-TS/JS language support.** v1.x.
174
+ - **No author-attributed signals.** v1.x telemetry.
175
+
176
+ ## Escalation
177
+
178
+ - **When LLM cost is too high:** drop `maxItemsPerFile` (default 20) or scope to specific surfaces with `--surfaces error`. Cost ≈ items × applicable rubrics × per-call.
179
+ - **When intentionally-bad test fixtures get flagged:** scope via `--files` to exclude fixtures. v1.x adds `<!-- copy-craft:skip -->` annotation + JSDoc tag.
180
+ - **When git surface skips though you ARE in a repo:** the walk goes up 10 levels looking for `.git`. If you're deeper than that, run from a closer cwd.
181
+ - **When PR surface skips with `gh auth status` failing:** run `gh auth login` first. Or scope away with `--surfaces error,log,comment`.
182
+ - **When `console.log` in a CLI file gets surface='log' instead of 'cli-output':** verify the file path contains `packages/cli/src/commands/` (or a similar default substring). Override with `craft.copy.cliOutputGlobs` config in v1.x.
183
+
184
+ ## Status
185
+
186
+ **v1 — in implementation.** See:
187
+
188
+ - Spec: `docs/changes/craft-pipeline/copy-craft/proposal.md`
189
+ - Roadmap entry: `craft-pipeline sub-project #5`
190
+ - Sibling craft skills: `naming-craft` (#1), `spec-craft` (#6)
191
+ - Shared infrastructure: `packages/cli/src/shared/craft/` (extracted by spec-craft)
192
+ - Future: `align-copy` (FIX side, v2), docs-craft (#2, prose docs), test-craft (#3), code-craft (#4)
@@ -0,0 +1,52 @@
1
+ name: copy-craft
2
+ version: "0.1.0"
3
+ description: LLM-judgment critique of prose-in-code across six surfaces (error messages, log lines, CLI output, commit subjects, PR descriptions, code comments). Third craft-pipeline ceiling skill; primary domain is error messages (universally bad). Graceful degradation when git/gh prerequisites are absent.
4
+ stability: draft
5
+ cognitive_mode: constructive-architect
6
+ triggers:
7
+ - manual
8
+ - on_pr
9
+ - on_new_feature
10
+ platforms:
11
+ - claude-code
12
+ tools:
13
+ - Bash
14
+ - Read
15
+ - Glob
16
+ - Grep
17
+ cli:
18
+ command: harness copy-craft
19
+ args:
20
+ - name: path
21
+ description: Project root path
22
+ required: false
23
+ - name: files
24
+ description: Optional source file/glob scope
25
+ required: false
26
+ - name: surfaces
27
+ description: Restrict to error/log/cli-output/commit/pr-description/comment
28
+ required: false
29
+ - name: commits-since
30
+ description: Commit window (default 1 month ago)
31
+ required: false
32
+ mcp:
33
+ tool: copy_craft
34
+ input:
35
+ path: string
36
+ type: rigid
37
+ tier: 2
38
+ phases:
39
+ - name: extract
40
+ description: TS Compiler API for source surfaces; git/gh shell-out for commits + PRs
41
+ required: true
42
+ - name: critique
43
+ description: LLM-rubric loop per (item, rubric) where rubric applies to that surface
44
+ required: true
45
+ - name: report
46
+ description: Aggregate findings + per-surface counts + cost telemetry + skipped surfaces
47
+ required: true
48
+ state:
49
+ persistent: false
50
+ depends_on:
51
+ - harness-design-craft
52
+ - naming-craft
@@ -0,0 +1,139 @@
1
+ # Detect Design Drift
2
+
3
+ > Detect design-system drift — hardcoded values where tokens exist, and raw HTML primitives where registered components exist. The rule-based floor of design-pipeline #1 (detect half). Reports findings; never modifies source. Pairs with a separate align-design-system fixer skill (deferred to a sibling sub-project).
4
+
5
+ ## When to Use
6
+
7
+ - Reviewing a PR that touches UI code, when you want to catch divergence from the design system before it ships
8
+ - After adding a new design token, to surface places in the codebase that still use the hardcoded equivalent
9
+ - After registering a new primitive (e.g. Button) in DESIGN.md `## Component Registry`, to find existing raw-HTML usages that should adopt it
10
+ - As part of `harness validate` so drift surfaces continuously, not only on demand
11
+ - As one of three verifiers composed by `harness check-design` (alongside audit-component-anatomy and harness-design-craft)
12
+ - NOT for component-anatomy gaps (use audit-component-anatomy)
13
+ - NOT for aesthetic critique (use harness-design-craft)
14
+ - NOT for fixing drift — this skill detects only; the matching fixer (align-design-system) is a separate sub-project
15
+
16
+ ## Process
17
+
18
+ ### Phase 1: SCAN — Load resolvers + walk files
19
+
20
+ 1. **Read project configuration.** Check `harness.config.json` for:
21
+ - `design.strictness` — `strict` / `standard` / `permissive` (default `standard`)
22
+ - `design.audit.driftDetection.enabled` — gate for the verifier (default `true`)
23
+ - `design.audit.driftDetection.rules.{tokenBypass,primitiveAdoption}` — per-rule toggles
24
+ - `design.audit.driftDetection.fastMode.maxFiles` — validate-time scope cap
25
+
26
+ 2. **Load resolvers (soft-dependency — silent skip when absent):**
27
+ - `design-system/tokens.json` (W3C DTCG format) — parsed by `loadTokenSet`. Extracts colors, font families, spacing scale, and deprecated token paths. Returns `null` when the file doesn't exist; token-bypass rules then skip silently.
28
+ - `design-system/DESIGN.md` `## Component Registry` — parsed by `loadComponentRegistry`. Maps registered component types (Button/Input/Textarea/Link/Anchor) to their HTML primitive tag. Returns `null` when DESIGN.md or the section is absent; primitive-adoption rules then skip silently.
29
+
30
+ 3. **Collect candidate files.** Walk the project root (or honor an explicit `files` arg from the caller). Skip `node_modules`, `dist`, `build`, `coverage`, and any dotfile directory. Honor only these extensions: `.ts`, `.tsx`, `.js`, `.jsx`, `.css`, `.scss`.
31
+
32
+ ### Phase 2: APPLY RULES — Two rule families
33
+
34
+ 1. **Token bypass rules (DRIFT-T\*) — regex-based detection.** For each file when tokens were loaded:
35
+ - **DRIFT-T001** — Hex color literal (`#abc`, `#aabbcc`, etc.) NOT present in the loaded palette. Case-insensitive palette match. Deduplicated per `line:value`.
36
+ - **DRIFT-T002** — Font-family string literal NOT present in the typography palette. System fallbacks (`sans-serif`, `serif`, `monospace`, `system-ui`, `inherit`) are always allowed.
37
+ - **DRIFT-T003** — Pixel margin/padding/gap/positioning value NOT present in the spacing scale. Skipped entirely when no spacing tokens exist.
38
+ - **DRIFT-T004** — Reference to a token flagged `$deprecated: true` (or `$extensions.harness.deprecated: true`) in tokens.json. Matched in both `'token.path.form'` and CSS-var kebab form (`--token-path-form`).
39
+
40
+ 2. **Primitive adoption rules (DRIFT-P\*) — TS Compiler API JSX parsing.** For each `.jsx`/`.tsx` file when the registry was loaded:
41
+ - **DRIFT-P001** — `<button>` JSX where `Button` is registered
42
+ - **DRIFT-P002** — `<input>` JSX where `Input` is registered
43
+ - **DRIFT-P003** — `<a>` JSX where `Link` or `Anchor` is registered
44
+ - **DRIFT-P004** — `<textarea>` JSX where `Textarea` is registered
45
+ - Lowercase JSX = HTML primitive (JSX semantics). Uppercase identifiers are skipped — they're already components. Member expressions (`<Foo.Bar>`) are skipped.
46
+
47
+ ### Phase 3: REPORT — Severity, aggregate, persist
48
+
49
+ 1. **Severity from `design.strictness`:**
50
+ - `strict` — every finding `error` (CI blocks)
51
+ - `standard` — DRIFT-T001/T002/P001 → `error`; everything else → `warn`
52
+ - `permissive` — everything → `info`
53
+
54
+ 2. **Aggregate the run summary.** `bySeverity`, `byCode`, `totalFiles`, `durationMs`. Also report `catalog.rulesApplied` (which rule families fired) and `meta.{tokensLoaded, registryLoaded, mode}` so callers can see what was active.
55
+
56
+ 3. **Persist findings to the graph (when composed by check-design).** The check-design orchestrator passes drift findings to `DesignConstraintAdapter.recordFindings()` alongside anatomy and craft findings. Each finding becomes an idempotent `VIOLATES_design` edge keyed by `(file, code, line)` — re-runs do not double-write.
57
+
58
+ ## Harness Integration
59
+
60
+ - **`harness validate`** — Fast-mode hook runs the detect-drift verifier. Findings respect `design.strictness`. Failures degrade gracefully: if the verifier throws, validate logs a warning and continues with other checks.
61
+ - **`harness check-design`** — One of three composed verifiers. The orchestrator aggregates findings across audit-anatomy, design-craft, and detect-drift; persists all three to the graph; and surfaces a unified report.
62
+ - **`mcp__harness__detect_drift`** — Programmatic API. Input: `{ path, mode, files?, designStrictness?, rules? }`. Output: `{ findings, summary, catalog, meta }`. Consumed by the design-pipeline orchestrator (sub-project #5).
63
+ - **`DesignConstraintAdapter.recordFindings()`** — Generic graph persistence entry point shipped in PR #390. Drift findings reuse the adapter; no extra graph plumbing.
64
+ - **Future align-design-system skill** — Separate sub-project. Reads the same finding codes (DRIFT-T\*, DRIFT-P\*) and applies fixes. Decoupling detect from align keeps each skill testable in isolation and lets the detect side ship first.
65
+
66
+ ## Success Criteria
67
+
68
+ See `docs/changes/design-pipeline/detect-design-drift/proposal.md` for the full 34 success criteria. Highlights:
69
+
70
+ - Zero false positives when tokens.json/DESIGN.md are absent (rules skip silently — no speculative findings)
71
+ - Token-bypass detection runs in &lt; 1s on a 500-file repo (fast-mode budget)
72
+ - Primitive-adoption parses JSX correctly across multi-line / fragmented opening tags (TS Compiler API, not regex)
73
+ - Idempotent graph persistence — re-running produces zero duplicate edges
74
+ - `harness validate` runtime budget &lt; 3s on a 500-file repo with both rule families enabled
75
+ - Both rule families gated independently via config (a project can disable primitive-adoption without disabling token-bypass)
76
+
77
+ ## Examples
78
+
79
+ ### Example: Hardcoded brand color outside the palette
80
+
81
+ **Input:** `src/Card.tsx` contains `const styles = { color: "#ff0000" };`, and `design-system/tokens.json` defines `color.brand.primary` as `#0066cc` (but no `#ff0000`).
82
+
83
+ **Output:**
84
+
85
+ ```
86
+ DRIFT-T001 [error] src/Card.tsx:14 — Hardcoded color "#ff0000" is not in the design token palette
87
+ Fix: Replace "#ff0000" with a token reference (e.g. var(--color-...) or a token-system lookup).
88
+ If the color is intentionally one-off, add it to tokens.json first.
89
+ ```
90
+
91
+ Severity is `error` under `standard` because brand-color drift is high-impact.
92
+
93
+ ### Example: Raw `<button>` where Button is registered
94
+
95
+ **Input:** `src/SaveButton.tsx` contains `<button onClick={...}>Save</button>`, and `design-system/DESIGN.md` `## Component Registry` lists `Button` mapped to `packages/ui/src/Button.tsx`.
96
+
97
+ **Output:**
98
+
99
+ ```
100
+ DRIFT-P001 [error] src/SaveButton.tsx:8 — Raw <button> element where the registered component "Button" should be used
101
+ Fix: Import Button from your component library and replace <button> with <Button>.
102
+ If this raw primitive is intentional (e.g. inside the Button component's own implementation),
103
+ add a JSDoc `@allow-raw-primitive` annotation on the file.
104
+ ```
105
+
106
+ ### Example: Reference to a deprecated token
107
+
108
+ **Input:** `src/legacy.css` contains `.x { color: var(--color-brand-500); }`, and `tokens.json` flags `color.brand.500` as `$deprecated: true` with `$description: "Use color.brand.primary instead"`.
109
+
110
+ **Output:**
111
+
112
+ ```
113
+ DRIFT-T004 [warn] src/legacy.css:3 — Token "color.brand.500" is deprecated and should be migrated
114
+ Fix: Migrate references to "color.brand.500" to the replacement token noted in tokens.json $description,
115
+ or remove the deprecation if the token is still load-bearing.
116
+ ```
117
+
118
+ ## Gates
119
+
120
+ - **No findings without resolved inputs.** If `tokens.json` is absent, all T\* rules silently skip. If `DESIGN.md ## Component Registry` is absent, all P\* rules silently skip. Either resolver failing is not a verifier failure — the project simply hasn't opted in.
121
+ - **No autofix.** Findings include codemod-todo descriptions. The matching fixer (align-design-system) is a separate skill.
122
+ - **No usage-side primitive findings beyond the four registered tags.** Other tags (`<select>`, `<details>`, etc.) are not in scope for v1 — even if the project registers a component for them. Subsumption ships in v1.x.
123
+ - **Strictness from config, not assumed.** Read `design.strictness` from `harness.config.json`; default to `standard` if absent.
124
+
125
+ ## Escalation
126
+
127
+ - **When token-bypass false positives appear in a third-party-shaped value.** E.g. a vendor color brought in via a CSS variable that legitimately doesn't match the palette. Either add the value to `tokens.json` (preferred — makes the palette authoritative) or scope the file via `design.audit.driftDetection.rules.tokenBypass: false` for a narrow path-overrides block (v1.x).
128
+ - **When primitive-adoption fires inside the Button component's own implementation.** That's a true positive that the rule can't tell apart from the usage form. Two paths: (1) add `@allow-raw-primitive` JSDoc on the file (v1.x — annotation honored by the rule), or (2) extract the raw `<button>` into a private internal component until the annotation lands.
129
+ - **When `harness validate` runtime exceeds 3 seconds.** Set `design.audit.driftDetection.fastMode.maxFiles` to cap the scope. The MCP tool ignores the cap (`fast`/`full` are equivalent in v1 — the cap is validate-side only).
130
+ - **When the graph persistence fails.** Skip graph integration for that run; findings still appear in the report. The graph is a consumer, not a gate.
131
+ - **When a project ships its own design-system convention (not tokens.json, not DESIGN.md registry).** v1 reads only the two declared input formats. Either (1) generate a tokens.json adapter that mirrors the existing convention (one-shot script), or (2) wait for v1.x's pluggable resolver interface.
132
+
133
+ ## Status
134
+
135
+ **v1 — in implementation.** See:
136
+
137
+ - Spec: `docs/changes/design-pipeline/detect-design-drift/proposal.md`
138
+ - Roadmap entry: `design-pipeline sub-project #1` (detect half) in `docs/roadmap.md`
139
+ - Sibling: `align-design-system` (fix half — deferred to a separate sub-project)
@@ -0,0 +1,50 @@
1
+ name: detect-design-drift
2
+ version: "0.1.0"
3
+ description: Detect design-system drift — hardcoded values where tokens exist and raw HTML primitives where registered components exist. Reports only; never modifies source. Floor-layer rule-based verifier composed by harness check-design.
4
+ stability: draft
5
+ cognitive_mode: constructive-architect
6
+ triggers:
7
+ - manual
8
+ - on_pr
9
+ - on_new_feature
10
+ platforms:
11
+ - claude-code
12
+ tools:
13
+ - Bash
14
+ - Read
15
+ - Glob
16
+ - Grep
17
+ cli:
18
+ command: harness skill run detect-design-drift
19
+ args:
20
+ - name: path
21
+ description: Project root path
22
+ required: false
23
+ - name: mode
24
+ description: "fast or full (equivalent in v1 — reserved for future scope expansion)"
25
+ required: false
26
+ - name: files
27
+ description: Optional list of files to scope the run
28
+ required: false
29
+ mcp:
30
+ tool: run_skill
31
+ input:
32
+ skill: detect-design-drift
33
+ path: string
34
+ type: rigid
35
+ tier: 2
36
+ phases:
37
+ - name: scan
38
+ description: Load tokens.json and DESIGN.md registry; walk candidate files
39
+ required: true
40
+ - name: apply_rules
41
+ description: Run DRIFT-T* and DRIFT-P* rules per file
42
+ required: true
43
+ - name: report
44
+ description: Aggregate severity, format findings, persist to graph
45
+ required: true
46
+ state:
47
+ persistent: false
48
+ depends_on:
49
+ - audit-component-anatomy
50
+ - harness-design-craft
@@ -31,6 +31,15 @@
31
31
  - If `i18n.enabled` is false or absent, scan for `lang`/`dir` as normal (these remain part of the accessibility audit).
32
32
  - This deduplication prevents the same finding from appearing in both the accessibility report and the i18n report.
33
33
 
34
+ 2.6. **Check for component-anatomy audit overlap.** Read `harness.config.json` for `design.audit.componentAnatomy.enabled` (default `true` when absent):
35
+
36
+ - If `enabled: true`, **defer** `A11Y-010` (interactive elements without accessible labels) and `A11Y-050` (`<input>`/`<select>`/`<textarea>` without associated `<label>`) findings for components whose identified type is in the anatomy catalog. Load the catalog component-type set via `getCatalogTypes()` from `audit-component-anatomy`'s public export so this skill has zero rule-content duplication.
37
+ - Identify each scanned JSX element's component type using the same 3-layer resolver `audit-component-anatomy` uses: (1) JSDoc `@component-type X` tag, (2) `design-system/DESIGN.md` `## Component Registry` mapping, (3) top-level export-name catalog match.
38
+ - For components in the catalog: `audit-component-anatomy` owns the label-slot finding as `ANAT-D*` (definition-side in v1, `ANAT-U*` in v2). Do not emit `A11Y-010` / `A11Y-050` for them.
39
+ - For raw HTML elements (`<button>`, `<input>`, `<select>`, `<textarea>`) and unidentified components: scan for `A11Y-010` / `A11Y-050` as normal.
40
+ - If `enabled: false` or absent: scan for `A11Y-010` / `A11Y-050` as normal (no deferral).
41
+ - Same i18n-style deduplication pattern as step 2.5 — prevents one root cause from appearing in both the accessibility report and the anatomy report.
42
+
34
43
  3. **Scan component files.** Search all files matching `.tsx`, `.jsx`, `.vue`, `.svelte`, `.html` for the following violations:
35
44
 
36
45
  **Images and media:**
@@ -183,6 +192,7 @@ This phase is optional. It applies fixes only for **mechanical issues** -- viola
183
192
  - **`harness-impact-analysis`** -- When tokens change (palette update, new colors), impact analysis traces affected components. The accessibility skill uses this to determine which components need re-scanning.
184
193
  - **`harness-design-system`** -- Dependency. When contrast failures originate from token definitions (not component code), escalate to harness-design-system to fix at the source.
185
194
  - **`harness-i18n` deduplication** -- When `i18n.enabled: true` in config, `lang` and `dir` attribute checks are deferred to the i18n skill. This prevents duplicate findings across the accessibility and i18n reports. When i18n is not enabled, these checks remain part of the accessibility scan.
195
+ - **`audit-component-anatomy` deduplication** -- When `design.audit.componentAnatomy.enabled: true` in config (default), `A11Y-010` and `A11Y-050` are deferred to `audit-component-anatomy` for components in its catalog (Button, Input, Select, Modal, Card, Tabs, etc.). The anatomy audit owns the label-slot finding for those components; this skill still scans raw HTML and unidentified components. Same i18n-style deduplication pattern.
186
196
 
187
197
  ## Success Criteria
188
198