@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.
- package/dist/agents/skills/claude-code/align-design-system/SKILL.md +174 -0
- package/dist/agents/skills/claude-code/align-design-system/skill.yaml +57 -0
- package/dist/agents/skills/claude-code/audit-brand-compliance/SKILL.md +189 -0
- package/dist/agents/skills/claude-code/audit-brand-compliance/skill.yaml +50 -0
- package/dist/agents/skills/claude-code/audit-component-anatomy/SKILL.md +181 -0
- package/dist/agents/skills/claude-code/audit-component-anatomy/skill.yaml +51 -0
- package/dist/agents/skills/claude-code/copy-craft/SKILL.md +192 -0
- package/dist/agents/skills/claude-code/copy-craft/skill.yaml +52 -0
- package/dist/agents/skills/claude-code/detect-design-drift/SKILL.md +139 -0
- package/dist/agents/skills/claude-code/detect-design-drift/skill.yaml +50 -0
- package/dist/agents/skills/claude-code/harness-accessibility/SKILL.md +10 -0
- package/dist/agents/skills/claude-code/harness-design-craft/SKILL.md +212 -0
- package/dist/agents/skills/claude-code/harness-design-craft/skill.yaml +57 -0
- package/dist/agents/skills/claude-code/harness-design-pipeline/SKILL.md +266 -0
- package/dist/agents/skills/claude-code/harness-design-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/claude-code/knowledge-craft/SKILL.md +180 -0
- package/dist/agents/skills/claude-code/knowledge-craft/skill.yaml +49 -0
- package/dist/agents/skills/claude-code/naming-craft/SKILL.md +172 -0
- package/dist/agents/skills/claude-code/naming-craft/skill.yaml +51 -0
- package/dist/agents/skills/claude-code/security-craft/SKILL.md +205 -0
- package/dist/agents/skills/claude-code/security-craft/skill.yaml +58 -0
- package/dist/agents/skills/claude-code/spec-craft/SKILL.md +184 -0
- package/dist/agents/skills/claude-code/spec-craft/skill.yaml +55 -0
- package/dist/agents/skills/claude-code/test-craft/SKILL.md +212 -0
- package/dist/agents/skills/claude-code/test-craft/skill.yaml +58 -0
- package/dist/agents/skills/codex/align-design-system/SKILL.md +174 -0
- package/dist/agents/skills/codex/align-design-system/skill.yaml +57 -0
- package/dist/agents/skills/codex/audit-brand-compliance/SKILL.md +189 -0
- package/dist/agents/skills/codex/audit-brand-compliance/skill.yaml +50 -0
- package/dist/agents/skills/codex/audit-component-anatomy/SKILL.md +181 -0
- package/dist/agents/skills/codex/audit-component-anatomy/skill.yaml +51 -0
- package/dist/agents/skills/codex/copy-craft/SKILL.md +192 -0
- package/dist/agents/skills/codex/copy-craft/skill.yaml +52 -0
- package/dist/agents/skills/codex/detect-design-drift/SKILL.md +139 -0
- package/dist/agents/skills/codex/detect-design-drift/skill.yaml +50 -0
- package/dist/agents/skills/codex/harness-accessibility/SKILL.md +10 -0
- package/dist/agents/skills/codex/harness-design-craft/SKILL.md +212 -0
- package/dist/agents/skills/codex/harness-design-craft/skill.yaml +57 -0
- package/dist/agents/skills/codex/harness-design-pipeline/SKILL.md +266 -0
- package/dist/agents/skills/codex/harness-design-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/codex/knowledge-craft/SKILL.md +180 -0
- package/dist/agents/skills/codex/knowledge-craft/skill.yaml +49 -0
- package/dist/agents/skills/codex/naming-craft/SKILL.md +172 -0
- package/dist/agents/skills/codex/naming-craft/skill.yaml +51 -0
- package/dist/agents/skills/codex/security-craft/SKILL.md +205 -0
- package/dist/agents/skills/codex/security-craft/skill.yaml +58 -0
- package/dist/agents/skills/codex/spec-craft/SKILL.md +184 -0
- package/dist/agents/skills/codex/spec-craft/skill.yaml +55 -0
- package/dist/agents/skills/codex/test-craft/SKILL.md +212 -0
- package/dist/agents/skills/codex/test-craft/skill.yaml +58 -0
- package/dist/agents/skills/cursor/align-design-system/SKILL.md +174 -0
- package/dist/agents/skills/cursor/align-design-system/skill.yaml +57 -0
- package/dist/agents/skills/cursor/audit-brand-compliance/SKILL.md +189 -0
- package/dist/agents/skills/cursor/audit-brand-compliance/skill.yaml +50 -0
- package/dist/agents/skills/cursor/audit-component-anatomy/SKILL.md +181 -0
- package/dist/agents/skills/cursor/audit-component-anatomy/skill.yaml +51 -0
- package/dist/agents/skills/cursor/copy-craft/SKILL.md +192 -0
- package/dist/agents/skills/cursor/copy-craft/skill.yaml +52 -0
- package/dist/agents/skills/cursor/detect-design-drift/SKILL.md +139 -0
- package/dist/agents/skills/cursor/detect-design-drift/skill.yaml +50 -0
- package/dist/agents/skills/cursor/harness-accessibility/SKILL.md +10 -0
- package/dist/agents/skills/cursor/harness-design-craft/SKILL.md +212 -0
- package/dist/agents/skills/cursor/harness-design-craft/skill.yaml +57 -0
- package/dist/agents/skills/cursor/harness-design-pipeline/SKILL.md +266 -0
- package/dist/agents/skills/cursor/harness-design-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/cursor/knowledge-craft/SKILL.md +180 -0
- package/dist/agents/skills/cursor/knowledge-craft/skill.yaml +49 -0
- package/dist/agents/skills/cursor/naming-craft/SKILL.md +172 -0
- package/dist/agents/skills/cursor/naming-craft/skill.yaml +51 -0
- package/dist/agents/skills/cursor/security-craft/SKILL.md +205 -0
- package/dist/agents/skills/cursor/security-craft/skill.yaml +58 -0
- package/dist/agents/skills/cursor/spec-craft/SKILL.md +184 -0
- package/dist/agents/skills/cursor/spec-craft/skill.yaml +55 -0
- package/dist/agents/skills/cursor/test-craft/SKILL.md +212 -0
- package/dist/agents/skills/cursor/test-craft/skill.yaml +58 -0
- package/dist/agents/skills/gemini-cli/align-design-system/SKILL.md +174 -0
- package/dist/agents/skills/gemini-cli/align-design-system/skill.yaml +57 -0
- package/dist/agents/skills/gemini-cli/audit-brand-compliance/SKILL.md +189 -0
- package/dist/agents/skills/gemini-cli/audit-brand-compliance/skill.yaml +50 -0
- package/dist/agents/skills/gemini-cli/audit-component-anatomy/SKILL.md +181 -0
- package/dist/agents/skills/gemini-cli/audit-component-anatomy/skill.yaml +51 -0
- package/dist/agents/skills/gemini-cli/copy-craft/SKILL.md +192 -0
- package/dist/agents/skills/gemini-cli/copy-craft/skill.yaml +52 -0
- package/dist/agents/skills/gemini-cli/detect-design-drift/SKILL.md +139 -0
- package/dist/agents/skills/gemini-cli/detect-design-drift/skill.yaml +50 -0
- package/dist/agents/skills/gemini-cli/harness-accessibility/SKILL.md +10 -0
- package/dist/agents/skills/gemini-cli/harness-design-craft/SKILL.md +212 -0
- package/dist/agents/skills/gemini-cli/harness-design-craft/skill.yaml +57 -0
- package/dist/agents/skills/gemini-cli/harness-design-pipeline/SKILL.md +266 -0
- package/dist/agents/skills/gemini-cli/harness-design-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/gemini-cli/knowledge-craft/SKILL.md +180 -0
- package/dist/agents/skills/gemini-cli/knowledge-craft/skill.yaml +49 -0
- package/dist/agents/skills/gemini-cli/naming-craft/SKILL.md +172 -0
- package/dist/agents/skills/gemini-cli/naming-craft/skill.yaml +51 -0
- package/dist/agents/skills/gemini-cli/security-craft/SKILL.md +205 -0
- package/dist/agents/skills/gemini-cli/security-craft/skill.yaml +58 -0
- package/dist/agents/skills/gemini-cli/spec-craft/SKILL.md +184 -0
- package/dist/agents/skills/gemini-cli/spec-craft/skill.yaml +55 -0
- package/dist/agents/skills/gemini-cli/test-craft/SKILL.md +212 -0
- package/dist/agents/skills/gemini-cli/test-craft/skill.yaml +58 -0
- package/dist/{agents-md-2JUQ4FE2.js → agents-md-267R2NUJ.js} +4 -4
- package/dist/{analyzer-VY6DJVOU-4AHIJLNS.js → analyzer-VY6DJVOU-WYKV3TTW.js} +2 -2
- package/dist/{architecture-CXAVNB5X.js → architecture-ZTEHGIHD.js} +6 -6
- package/dist/{assess-project-M626SSPN.js → assess-project-OKDW3YSK.js} +2 -1
- package/dist/bin/harness-mcp.js +17 -17
- package/dist/bin/harness.js +27 -27
- package/dist/{check-phase-gate-XMU6LLUQ.js → check-phase-gate-YJWFDW66.js} +5 -5
- package/dist/{chunk-372MGNTI.js → chunk-4EGPFB7V.js} +6185 -124
- package/dist/{chunk-HFOB2MPQ.js → chunk-67RP7MLR.js} +3 -3
- package/dist/{chunk-5JNSFYZU.js → chunk-6T5AYG5S.js} +6 -6
- package/dist/{chunk-5XLLIYYL.js → chunk-6UHDQP2N.js} +1 -1
- package/dist/{chunk-YP7I25SL.js → chunk-B3B64OPD.js} +1 -1
- package/dist/{chunk-VTTNIZJL.js → chunk-B4OLYXZ4.js} +93 -3
- package/dist/{chunk-6AX6R3LM.js → chunk-CXAHK5PJ.js} +9 -9
- package/dist/{chunk-BCFDGOMA.js → chunk-H2ZJOJ7A.js} +81 -1
- package/dist/{chunk-CQOMVZDC.js → chunk-HLPY3RHE.js} +1641 -292
- package/dist/{chunk-Q6ZR2L6Q.js → chunk-HMMSWRWR.js} +2 -2
- package/dist/{chunk-YYRQCQPZ.js → chunk-IY2TZLY5.js} +176 -107
- package/dist/{chunk-I6K43QQS.js → chunk-MQG7FHXX.js} +3 -3
- package/dist/{chunk-43M3RLFQ.js → chunk-OLTVDPA2.js} +2 -2
- package/dist/{chunk-JFZOWO7D.js → chunk-OQE6ME2Z.js} +1 -1
- package/dist/{chunk-GWE5LL3R.js → chunk-P7EO7USC.js} +10 -10
- package/dist/{chunk-24KCOJJG.js → chunk-PARV3G2F.js} +1 -1
- package/dist/{chunk-QOKMDDBJ.js → chunk-QUPC37UB.js} +6 -6
- package/dist/{chunk-EPUKTTJZ.js → chunk-RC3OI5NK.js} +5 -1
- package/dist/{chunk-L5BCZ5XS.js → chunk-S7GEONXL.js} +1 -1
- package/dist/{chunk-5FDBXUFW.js → chunk-TIH53QXY.js} +1 -1
- package/dist/{chunk-OA6SWTU4.js → chunk-TIZ3L6CU.js} +11 -8
- package/dist/{chunk-GWF2OILZ.js → chunk-VG6UK6G6.js} +1 -1
- package/dist/{chunk-SZEFU6XC.js → chunk-W3PDVZ4I.js} +1 -1
- package/dist/{chunk-VUCGB2KH.js → chunk-WOXR6I3R.js} +1 -1
- package/dist/{chunk-K246XQ2L.js → chunk-XTIPZUPR.js} +1 -1
- package/dist/{chunk-HMW3VKUF.js → chunk-Z2GUITAS.js} +10 -10
- package/dist/{chunk-PFZEADQM.js → chunk-ZKA3TGYS.js} +79 -2
- package/dist/{ci-workflow-3JNDZQYD.js → ci-workflow-YGTQTNVC.js} +4 -4
- package/dist/{dist-QPWXPCPL.js → dist-R3TOOPJ7.js} +1 -1
- package/dist/{dist-OHDQBTSO.js → dist-XIJWWQ5N.js} +3 -3
- package/dist/{docs-5RYMDHN3.js → docs-KCTLXK2G.js} +6 -6
- package/dist/{engine-GKGT5ULT.js → engine-J64WPH5K.js} +4 -4
- package/dist/{entropy-DQTAPSSE.js → entropy-M27JUL3V.js} +5 -5
- package/dist/{feedback-WH6XPQJS.js → feedback-W25QO3OL.js} +2 -2
- package/dist/{generate-agent-definitions-SZA4QIHN.js → generate-agent-definitions-RNMU5U5R.js} +5 -5
- package/dist/{glob-helper-XE2UGEOV.js → glob-helper-EX4YZKZO.js} +1 -1
- package/dist/{graph-loader-EQBOIHFZ.js → graph-loader-EIFEOUVI.js} +1 -1
- package/dist/index.d.ts +587 -6
- package/dist/index.js +27 -27
- package/dist/{loader-BNKGM23C.js → loader-HZKJ5ABV.js} +4 -4
- package/dist/{mcp-YKKPWUMH.js → mcp-NEAPIDTT.js} +17 -17
- package/dist/{performance-54TMJOEU.js → performance-3AVQUDRG.js} +6 -6
- package/dist/{review-pipeline-OTDDVPNY.js → review-pipeline-KA7SGUXJ.js} +4 -4
- package/dist/{runtime-W5THSNAL.js → runtime-IP6FVV5M.js} +4 -4
- package/dist/{scan-4PPP6ZXT.js → scan-LYKLM4EQ.js} +1 -1
- package/dist/{security-27HW7CYT.js → security-EAR4FJWI.js} +1 -1
- package/dist/{validate-QRVCD4GP.js → validate-EVH3UXIC.js} +5 -5
- package/dist/{validate-cross-check-BUAGBAPO.js → validate-cross-check-SNE3SQOB.js} +4 -4
- package/package.json +8 -7
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Align Design System
|
|
2
|
+
|
|
3
|
+
> Apply codemods for safe DRIFT-T001/T002/T003 token-bypass findings (replace hex / font-family / px-spacing literals with token references) and emit precise suggestions for DRIFT-T004 (deprecated tokens) and all DRIFT-P\* (primitive adoption). The FIX half of design-pipeline sub-project #1, paired with detect-design-drift.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- After detect-design-drift reports DRIFT-T001/T002/T003 findings — align replaces literals with token references where the fix is unambiguous
|
|
8
|
+
- Before a PR that touches UI code lands — pair with detect-design-drift to surface AND fix drift in one shot
|
|
9
|
+
- Inside a (future) #5 design-pipeline orchestrator's convergence loop — align is the FIX step the loop runs between DETECT and VERIFY
|
|
10
|
+
- When you want to dry-run the fixes first (`--dry-run`) and review the diff before writing
|
|
11
|
+
- NOT for primitive-adoption codemods — v1 emits suggestions only for DRIFT-P\*; the prop-translation work lives in v1.x
|
|
12
|
+
- NOT for adding new tokens to tokens.json (no auto-add of palette entries; that's a separate intentional act)
|
|
13
|
+
- NOT for non-design-system fixes (use cleanup-dead-code, align-documentation, etc. for their respective domains)
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
|
|
17
|
+
### Phase 1: GATHER — Load drift findings
|
|
18
|
+
|
|
19
|
+
1. **Read project configuration.** Check `harness.config.json` for:
|
|
20
|
+
- `design.strictness` — passes through to detect-design-drift
|
|
21
|
+
- `design.audit.driftDetection.*` — passes through to detect
|
|
22
|
+
|
|
23
|
+
2. **In standalone mode (default):** invoke `detect-design-drift` internally with the same project root + strictness; receive the full `DriftFinding[]`.
|
|
24
|
+
|
|
25
|
+
3. **In pipeline mode:** read `.harness/handoff.json` and pull `pipeline.driftFindings` (pre-classified by the orchestrator). Honor `pipeline.fixBatch` if present to limit application to a specific subset of findings (the orchestrator may apply fixes in batches across iterations).
|
|
26
|
+
|
|
27
|
+
### Phase 2: CLASSIFY — Pre-flight safe-codemod vs suggestion
|
|
28
|
+
|
|
29
|
+
For each finding, the pre-flight classifier inspects file context and chooses:
|
|
30
|
+
|
|
31
|
+
- **DRIFT-T001 (hex)** — safe-codemod iff token import already present in the file AND hex is in single string-literal context (not a template literal or concatenation) AND exactly one palette token matches by value. Otherwise → suggestion.
|
|
32
|
+
- **DRIFT-T002 (font-family)** — same shape as T001 against the typography palette.
|
|
33
|
+
- **DRIFT-T003 (px spacing)** — safe-codemod iff token import present AND px matches a spacing token's `$value` EXACTLY (no rounding) AND not in an arithmetic expression. Otherwise → suggestion.
|
|
34
|
+
- **DRIFT-T004 (deprecated)** — always suggestion in v1. Migration target may not be in the token's `$description`.
|
|
35
|
+
- **DRIFT-P\* (primitive adoption)** — always suggestion in v1. Prop translation across `<button>` ⇄ `<Button>` is genuinely ambiguous (event handlers, ref forwarding, class merging) — codemods deferred to v1.x.
|
|
36
|
+
|
|
37
|
+
Token import discovery recognizes three forms:
|
|
38
|
+
|
|
39
|
+
- ES named: `import { tokens } from '...'`
|
|
40
|
+
- ES default: `import tokens from '...'`
|
|
41
|
+
- CJS: `const tokens = require('...')`
|
|
42
|
+
|
|
43
|
+
### Phase 3: APPLY — Codemod or emit suggestion
|
|
44
|
+
|
|
45
|
+
For each finding classified as `safe-codemod`:
|
|
46
|
+
|
|
47
|
+
1. Read the source file (cached per-run to avoid re-reads when multiple findings hit the same file).
|
|
48
|
+
2. Locate the exact match position from the finding's `line` + `evidence.snippet`.
|
|
49
|
+
3. Compute the replacement using file-extension-aware syntax:
|
|
50
|
+
- `.ts` / `.tsx` / `.js` / `.jsx` → `tokens.<dotted.path>`
|
|
51
|
+
- `.css` / `.scss` → `var(--<dotted-path-as-kebab>)`
|
|
52
|
+
4. Replace in-place. Emit `FixOutcome.applied` with a structured diff (file / line / before / after).
|
|
53
|
+
5. If `--dry-run`: compute the diff but DO NOT write to disk.
|
|
54
|
+
6. If the file changed between detect-time and apply-time: skip with `kind: 'skipped-unsafe', reason: 'file changed since finding'`.
|
|
55
|
+
|
|
56
|
+
For each finding classified as `suggestion`: emit a human-readable description plus a preview of the suggested change. No file mutation.
|
|
57
|
+
|
|
58
|
+
### Phase 4: REPORT — Aggregate + (pipeline-mode) handoff writeback
|
|
59
|
+
|
|
60
|
+
1. Aggregate `FixOutcome[]` into a summary: counts by kind (applied / suggestion / skipped / failed) plus files modified and duration.
|
|
61
|
+
2. Build a `catalog` of finding codes that produced codemods and codes that produced suggestions.
|
|
62
|
+
3. In pipeline mode: write `pipeline.fixesApplied: FixOutcome[]` back to `.harness/handoff.json` so the orchestrator can re-verify only the affected findings on the next loop iteration.
|
|
63
|
+
|
|
64
|
+
## Harness Integration
|
|
65
|
+
|
|
66
|
+
- **`harness align-design-system`** — the CLI entry point. `--dry-run` for preview; `--write` is the default. Standard `--json` / `--verbose` / `--quiet` flags.
|
|
67
|
+
- **`harness align-design-system --mode pipeline`** — orchestrator-driven mode. Reads pre-classified findings from handoff.json; writes outcomes back.
|
|
68
|
+
- **`mcp__harness__align_design_system`** — MCP tool for agent consumption. Same input/output shape as the function call.
|
|
69
|
+
- **`detect-design-drift`** — soft dependency. Standalone mode invokes detect internally; pipeline mode trusts the orchestrator to have done it.
|
|
70
|
+
- **`harness check-design`** — composes detect (as 3rd verifier) into a single-pass design check. align is the matching FIX step; together they form the DETECT → FIX cycle that the (future) #5 orchestrator will loop.
|
|
71
|
+
- **`DesignConstraintAdapter`** — align does NOT write to the graph. The graph already tracks `VIOLATES_design` edges (the findings). Re-running detect after align shows the delta — no separate fix-edge needed.
|
|
72
|
+
|
|
73
|
+
## Success Criteria
|
|
74
|
+
|
|
75
|
+
See `docs/changes/design-pipeline/align-design-system/proposal.md` for the full 34 success criteria. Highlights:
|
|
76
|
+
|
|
77
|
+
- T001/T002/T003 codemods write to disk only when pre-flight classifier returns `safe-codemod`
|
|
78
|
+
- Codemods are idempotent — running twice produces zero additional changes on the second run
|
|
79
|
+
- Classifier downgrades to suggestion when token import is missing, when value appears in template/concatenation, or when multiple tokens share the value
|
|
80
|
+
- Classifier ALWAYS returns suggestion for T004 + all P\* findings (no source inspection)
|
|
81
|
+
- Pipeline mode reads `pipeline.driftFindings` from handoff.json; standalone mode runs detect internally
|
|
82
|
+
- Pipeline mode writes `pipeline.fixesApplied` back to handoff.json
|
|
83
|
+
- `--dry-run` produces identical `FixOutcome` shapes but never writes files
|
|
84
|
+
- Re-running detect after align produces strictly fewer T001/T002/T003 findings
|
|
85
|
+
|
|
86
|
+
## Examples
|
|
87
|
+
|
|
88
|
+
### Example: Apply a T001 codemod (hex → token reference)
|
|
89
|
+
|
|
90
|
+
**Input:**
|
|
91
|
+
|
|
92
|
+
`design-system/tokens.json` has:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{ "color": { "brand": { "primary": { "$type": "color", "$value": "#0066cc" } } } }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`src/Card.tsx` has:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { tokens } from '@/design-system/tokens';
|
|
102
|
+
const styles = { color: '#0066cc' }; // raw literal where token exists
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Output:**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
src/Card.tsx
|
|
109
|
+
✓ DRIFT-T001:2 — Hex color "#0066cc" should use a token reference instead of a raw literal
|
|
110
|
+
before: const styles = { color: "#0066cc" };
|
|
111
|
+
after: const styles = { color: tokens.color.brand.primary };
|
|
112
|
+
|
|
113
|
+
Summary: 1 applied, 0 suggestions, 0 skipped, 0 failed (1 files modified, 5ms)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Example: Downgrade to suggestion (no token import)
|
|
117
|
+
|
|
118
|
+
Same finding as above, but `src/Card.tsx` does NOT import tokens. align emits a suggestion instead:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
src/Card.tsx
|
|
122
|
+
? DRIFT-T001:2 — Hex color "#0066cc" should use a token reference instead of a raw literal
|
|
123
|
+
|
|
124
|
+
Summary: 0 applied, 1 suggestions, 0 skipped, 0 failed (0 files modified, 3ms)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
(Run with `--verbose` to see the suggestion text and the classifier's reason for downgrading.)
|
|
128
|
+
|
|
129
|
+
### Example: Suggestion-only for DRIFT-P001
|
|
130
|
+
|
|
131
|
+
**Input:**
|
|
132
|
+
|
|
133
|
+
`design-system/DESIGN.md` registers `Button` in `## Component Registry`. `src/SaveButton.tsx` has:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
export const S = () => <button onClick={() => save()}>Save</button>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Output:**
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
src/SaveButton.tsx
|
|
143
|
+
? DRIFT-P001:1 — Raw <button> element where the registered component "Button" should be used
|
|
144
|
+
|
|
145
|
+
(use --verbose to see prop-translation suggestion)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
v1 never auto-applies primitive adoption — prop translation across `<button>` ⇄ `<Button>` is the kind of judgment-call that benefits from a human or LLM review.
|
|
149
|
+
|
|
150
|
+
## Gates
|
|
151
|
+
|
|
152
|
+
- **No autofix without classifier approval.** Every codemod application goes through `classifyFinding`. If the classifier returns `suggestion`, NO file write occurs — even for the same finding code in the same file.
|
|
153
|
+
- **No autofix when token import is missing.** Adding the import line is its own ambiguity surface (alias? barrel? relative?). v1 skips with a suggestion.
|
|
154
|
+
- **No autofix for non-exact px matches.** "Round 13px to the nearest 16px" is a design decision the tool shouldn't make.
|
|
155
|
+
- **No autofix for primitive adoption.** v1 has no codemod for DRIFT-P\* — always suggestions.
|
|
156
|
+
- **No autofix in pipeline mode unless a `pipeline.driftFindings` field is present.** Empty handoff = empty run.
|
|
157
|
+
- **No graph writes.** align modifies source files; the graph is read-only from align's perspective.
|
|
158
|
+
|
|
159
|
+
## Escalation
|
|
160
|
+
|
|
161
|
+
- **When a T001 finding has multiple matching tokens (ambiguous):** the classifier downgrades to suggestion. To resolve, declare a primary token in DESIGN.md `## Token Primary Resolution Overrides` (v1.x), OR pick one in the source manually and re-run.
|
|
162
|
+
- **When the codemod corrupts a file (rare):** every application includes a structured diff. Recover via `git checkout <file>`. If the same input repeatedly corrupts, report the case — pre-flight classifier rules are conservative by design.
|
|
163
|
+
- **When pipeline-mode run finds no `pipeline.driftFindings` field:** align exits cleanly with empty outcomes. The orchestrator's contract is to write the field BEFORE invoking align.
|
|
164
|
+
- **When `--dry-run` shows fixes you don't want applied:** scope with `--files <glob>` to apply only specific files, or invoke align in pipeline mode with a curated `pipeline.fixBatch` list.
|
|
165
|
+
- **When you want primitive-adoption fixes today:** apply the suggestion manually. The v1.x sub-project will add prop-translation tables + import resolution + revert-on-test-fail.
|
|
166
|
+
- **When align is invoked without detect having run first (standalone mode):** standalone mode runs detect internally — no manual ordering needed. Pipeline mode trusts the orchestrator to populate findings.
|
|
167
|
+
|
|
168
|
+
## Status
|
|
169
|
+
|
|
170
|
+
**v1 — in implementation.** See:
|
|
171
|
+
|
|
172
|
+
- Spec: `docs/changes/design-pipeline/align-design-system/proposal.md`
|
|
173
|
+
- Roadmap entry: `design-pipeline sub-project #1` (align half) in `docs/roadmap.md`
|
|
174
|
+
- Sibling: `detect-design-drift` (detect half — shipped PR #396)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: align-design-system
|
|
2
|
+
version: "0.1.0"
|
|
3
|
+
description: Apply codemods for safe DRIFT-T001/T002/T003 token-bypass findings; emit precise suggestions for DRIFT-T004 (deprecated tokens) and all DRIFT-P* (primitive adoption). FIX half of design-pipeline sub-project #1; pairs with detect-design-drift.
|
|
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
|
+
- Write
|
|
16
|
+
- Edit
|
|
17
|
+
- Glob
|
|
18
|
+
- Grep
|
|
19
|
+
cli:
|
|
20
|
+
command: harness align-design-system
|
|
21
|
+
args:
|
|
22
|
+
- name: path
|
|
23
|
+
description: Project root path
|
|
24
|
+
required: false
|
|
25
|
+
- name: dry-run
|
|
26
|
+
description: Compute diffs without writing files
|
|
27
|
+
required: false
|
|
28
|
+
- name: files
|
|
29
|
+
description: Optional file/glob scope (standalone mode only)
|
|
30
|
+
required: false
|
|
31
|
+
- name: mode
|
|
32
|
+
description: standalone (default) or pipeline (read findings from handoff.json)
|
|
33
|
+
required: false
|
|
34
|
+
mcp:
|
|
35
|
+
tool: run_skill
|
|
36
|
+
input:
|
|
37
|
+
skill: align-design-system
|
|
38
|
+
path: string
|
|
39
|
+
type: rigid
|
|
40
|
+
tier: 2
|
|
41
|
+
phases:
|
|
42
|
+
- name: gather
|
|
43
|
+
description: Load drift findings (run detect-design-drift in standalone mode; read pipeline.driftFindings in pipeline mode)
|
|
44
|
+
required: true
|
|
45
|
+
- name: classify
|
|
46
|
+
description: Per-finding pre-flight check decides safe-codemod vs suggestion
|
|
47
|
+
required: true
|
|
48
|
+
- name: apply
|
|
49
|
+
description: Write codemods for safe T001/T002/T003; emit suggestions for everything else
|
|
50
|
+
required: true
|
|
51
|
+
- name: report
|
|
52
|
+
description: Emit FixOutcome[] + aggregate summary; write pipeline.fixesApplied in pipeline mode
|
|
53
|
+
required: true
|
|
54
|
+
state:
|
|
55
|
+
persistent: false
|
|
56
|
+
depends_on:
|
|
57
|
+
- detect-design-drift
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Audit Brand Compliance
|
|
2
|
+
|
|
3
|
+
> Rule-based brand-semantics audit. Detects (a) tokens used in forbidden contexts per their `$extensions.harness.brand.forbidden_contexts` metadata (BRAND-T001) and (b) UI copy containing phrases listed in `DESIGN.md ## Brand Rules → voice.forbidden_phrases` (BRAND-V001). The 4th composed verifier in `harness check-design`, alongside audit-component-anatomy, design-craft critique, and detect-design-drift.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- After authoring or editing `DESIGN.md ## Brand Rules` — verify the new constraints are enforceable on the existing codebase
|
|
8
|
+
- After adding `$extensions.harness.brand` metadata to a token — discover existing call sites in forbidden contexts
|
|
9
|
+
- As part of `harness validate` (fast-mode hook, gated by `design.audit.brandCompliance.enabled`)
|
|
10
|
+
- As the 4th composed verifier in `harness check-design` (the unified design check)
|
|
11
|
+
- Before a PR with UI copy or token-usage changes lands
|
|
12
|
+
- NOT for tone-by-context rules (deferred to v1.x — requires component-state inference)
|
|
13
|
+
- NOT for reading-level or sentence-length rules (deferred to v1.x — ship with tone-context)
|
|
14
|
+
- NOT for asset-usage rules (deferred to v1.x — requires image-tag scanning)
|
|
15
|
+
- NOT for semantic-token-alias enforcement (overlaps with detect-design-drift T001 — design after both have shipped)
|
|
16
|
+
- NOT for brand-rule authoring (use `harness-design` skill to draft DESIGN.md sections)
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
### Phase 1: LOAD — Parse the two input sources
|
|
21
|
+
|
|
22
|
+
1. **Read project configuration.** Check `harness.config.json` for:
|
|
23
|
+
- `design.strictness` — `strict` / `standard` / `permissive` (default `standard`)
|
|
24
|
+
- `design.audit.brandCompliance.enabled` — gate (default `true`)
|
|
25
|
+
- `design.audit.brandCompliance.rules.{tokenMisuse,voice}` — per-rule toggles
|
|
26
|
+
|
|
27
|
+
2. **Load `design-system/DESIGN.md` `## Brand Rules`.** The parser extracts:
|
|
28
|
+
- `voice.forbiddenPhrases: string[]` — used by BRAND-V001 in v1
|
|
29
|
+
- `voice.constant`, `voice.readingLevel`, `voice.maxSentenceWords` — parsed but unused in v1 (forward-compat)
|
|
30
|
+
- `toneByContext`, `assets`, `semanticTokenAliases` — parsed but unused (v1.x)
|
|
31
|
+
- Returns `null` when DESIGN.md absent or `## Brand Rules` section missing → BRAND-V001 silently skips.
|
|
32
|
+
|
|
33
|
+
3. **Load `design-system/tokens.json` `$extensions.harness.brand`.** Walks the DTCG token tree capturing per-token `role`, `approved_contexts`, `forbidden_contexts`. Returns `null` when no token carries the extension → BRAND-T\* silently skips.
|
|
34
|
+
|
|
35
|
+
### Phase 2: SCAN — Apply the two rule families
|
|
36
|
+
|
|
37
|
+
1. **BRAND-T001 — token misuse (regex-based).** For each token whose `forbidden_contexts` is non-empty:
|
|
38
|
+
- Find every reference to the token's dotted path in source (recognizes three forms):
|
|
39
|
+
- `tokens.X.Y.Z` (JS accessor)
|
|
40
|
+
- `var(--X-Y-Z)` (CSS var, kebab-cased)
|
|
41
|
+
- `'X.Y.Z'` / `"X.Y.Z"` (string literal)
|
|
42
|
+
- Inspect surrounding context (same line + nearest non-blank previous and next line) for the v1 context-vocabulary keywords: `cta`, `selection`, `focus`, `data-visualization`, `decorative`, `background`, `text`, `border`, `error`, `success`, `warning`.
|
|
43
|
+
- If a forbidden context matches: emit BRAND-T001.
|
|
44
|
+
|
|
45
|
+
2. **BRAND-V001 — forbidden phrases (TS Compiler API).** For each `.tsx`/`.jsx` file:
|
|
46
|
+
- Walk the JSX tree.
|
|
47
|
+
- For each `JsxText` node: case-insensitive substring scan for any forbiddenPhrase.
|
|
48
|
+
- For each `JsxAttribute` whose initializer is a string literal: same scan.
|
|
49
|
+
- Deduplicate per `(file, line, phrase)`.
|
|
50
|
+
|
|
51
|
+
### Phase 3: REPORT — Aggregate and surface
|
|
52
|
+
|
|
53
|
+
1. **Severity from `design.strictness`** (uses `severityFor`):
|
|
54
|
+
- `strict` — all findings `error`
|
|
55
|
+
- `standard` — BRAND-T001 `error` (declared violation), BRAND-V001 `warn` (copy nuance)
|
|
56
|
+
- `permissive` — all findings `info`
|
|
57
|
+
|
|
58
|
+
2. **Aggregate `bySeverity` and `byCode`** into the standard Verifier shape: `{ findings, summary, catalog, meta }`.
|
|
59
|
+
|
|
60
|
+
3. **Persist findings to the graph (when composed by check-design).** check-design routes brand findings through `DesignConstraintAdapter.recordFindings()` alongside anatomy / craft / drift. v1 uses the shared `VIOLATES_design` edge; v1.x may add a brand-specific edge.
|
|
61
|
+
|
|
62
|
+
## Harness Integration
|
|
63
|
+
|
|
64
|
+
- **`harness validate`** — Fast-mode hook gated by `design.audit.brandCompliance.enabled`. Degrades gracefully on failure (single warning; other checks continue).
|
|
65
|
+
- **`harness check-design`** — Composes brand as the 4th verifier alongside audit-anatomy, design-craft critique, and detect-design-drift. This is the canonical invocation path.
|
|
66
|
+
- **`mcp__harness__audit_brand`** — MCP tool. Input: `{ path, mode, files?, designStrictness?, rules? }`. Output: `{ findings, summary, catalog, meta }`. Consumed by check-design and the (future) #5 design-pipeline orchestrator.
|
|
67
|
+
- **`DesignConstraintAdapter.recordFindings()`** — Generic graph persistence entry point shipped in PR #390. Brand findings reuse the adapter (no graph schema changes in v1).
|
|
68
|
+
- **`harness-design` skill** — Authors `DESIGN.md ## Brand Rules`. audit-brand-compliance is the matching enforcer.
|
|
69
|
+
- **`Verifier<F>` interface** — Extracted in this PR at the 4th-verifier threshold. Lives at `packages/cli/src/shared/verifier.ts`. Adding a 5th verifier requires only a type-alias declaration of conformance.
|
|
70
|
+
|
|
71
|
+
## Success Criteria
|
|
72
|
+
|
|
73
|
+
See `docs/changes/design-pipeline/audit-brand-compliance/proposal.md` for the full 34 success criteria. Highlights:
|
|
74
|
+
|
|
75
|
+
- DESIGN.md parser returns `null` when section absent (silent-skip pattern)
|
|
76
|
+
- Token-extensions walker returns `null` when no token carries `$extensions.harness.brand`
|
|
77
|
+
- BRAND-T001 fires on `tokens.X`, `var(--x)`, and `'X'` reference forms
|
|
78
|
+
- BRAND-T001 honors approved_contexts (no finding when context is allowed)
|
|
79
|
+
- BRAND-V001 fires on JSX text + string-typed JSX attributes (case-insensitive)
|
|
80
|
+
- BRAND-V001 deduplicates per `(file, line, phrase)`
|
|
81
|
+
- Verifier interface extraction: anatomy / drift / brand all declare structural conformance
|
|
82
|
+
- `harness check-design` test extended for 4-verifier composition (zero regressions)
|
|
83
|
+
- MCP tool count bumps 72 → 73
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
### Example: Token used in forbidden context
|
|
88
|
+
|
|
89
|
+
**Input:**
|
|
90
|
+
|
|
91
|
+
`design-system/tokens.json`:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"color": {
|
|
96
|
+
"brand": {
|
|
97
|
+
"500": {
|
|
98
|
+
"$type": "color",
|
|
99
|
+
"$value": "#3b82f6",
|
|
100
|
+
"$extensions": {
|
|
101
|
+
"harness": {
|
|
102
|
+
"brand": {
|
|
103
|
+
"role": "primary",
|
|
104
|
+
"approved_contexts": ["cta", "selection", "focus"],
|
|
105
|
+
"forbidden_contexts": ["data-visualization", "decorative"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`src/Chart.tsx`:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// data-visualization color palette
|
|
119
|
+
const palette = [tokens.color.brand.500, ...];
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Output:**
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
BRAND-T001 [error] src/Chart.tsx:2 — Token "color.brand.500" is used in forbidden context "data-visualization"
|
|
126
|
+
Fix: Token "color.brand.500" is not approved for the "data-visualization" context.
|
|
127
|
+
Use an approved token (allowed contexts: cta, selection, focus), or update
|
|
128
|
+
tokens.json $extensions.harness.brand if the policy is wrong.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Example: Forbidden phrase in UI copy
|
|
132
|
+
|
|
133
|
+
**Input:**
|
|
134
|
+
|
|
135
|
+
`DESIGN.md`:
|
|
136
|
+
|
|
137
|
+
```markdown
|
|
138
|
+
## Brand Rules
|
|
139
|
+
|
|
140
|
+
### Voice
|
|
141
|
+
|
|
142
|
+
forbidden_phrases:
|
|
143
|
+
|
|
144
|
+
- "click here"
|
|
145
|
+
- "best-in-class"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`src/Cta.tsx`:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
export const Cta = () => <a href="/x">Click here</a>;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Output:**
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
BRAND-V001 [warn] src/Cta.tsx:1 — UI copy contains forbidden phrase "click here" — declared at DESIGN.md ## Brand Rules → Voice → forbidden_phrases
|
|
158
|
+
Fix: Rewrite to avoid "click here". If the phrase is unavoidable for this context,
|
|
159
|
+
remove it from voice.forbidden_phrases (or scope the audit) — but the default
|
|
160
|
+
policy is that brand voice trumps convenience.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Gates
|
|
164
|
+
|
|
165
|
+
- **No findings without parsed inputs.** DESIGN.md absent → BRAND-V001 skips silently. tokens.json `$extensions.harness.brand` absent on every token → BRAND-T001 skips silently. Either resolver returning null is NOT a verifier failure.
|
|
166
|
+
- **No `.ts`/`.js` file scans for BRAND-V001.** Only `.jsx`/`.tsx` (user-visible JSX). Doc copy in `.md` is a different audience.
|
|
167
|
+
- **No tone-by-context inference.** v1 only matches the explicit context-vocabulary keywords against surrounding source text. v1.x adds component-state inference.
|
|
168
|
+
- **No autofix.** audit-only. The matching `align-brand-compliance` fix-side skill is deferred until detect signals demand.
|
|
169
|
+
- **No graph schema changes.** v1 reuses `VIOLATES_design` via `recordFindings()`. v1.x may add `VIOLATES_brand` edge for queryability.
|
|
170
|
+
- **Strictness from config, not assumed.** Read `design.strictness` from `harness.config.json`; default `standard` if absent.
|
|
171
|
+
|
|
172
|
+
## Escalation
|
|
173
|
+
|
|
174
|
+
- **When BRAND-T001 false-positives on a far-context reference:** the v1 context inference is intentionally narrow (same line + adjacent non-blank). For a token used in a "background" context where the keyword appears 10 lines away, v1 misses it. v1.x adds richer context inference; for now, either widen the surrounding comment or accept the miss.
|
|
175
|
+
- **When BRAND-V001 false-positives on a substring (e.g., "as is" in "as issued"):** v1 uses substring match. Add word-boundary regex in v1.x. For now, rephrase the copy or remove the phrase from voice.forbidden_phrases.
|
|
176
|
+
- **When a project ships tokens with a different `$extensions` shape:** v1 reads only `harness.brand`. Document the actual shape your project uses and add it to the schema sketch in ADR 0028 — DTCG `$extensions` namespaces are vendor-prefixed and additions are forward-compatible.
|
|
177
|
+
- **When `harness validate` runtime exceeds 3 seconds:** Set `design.audit.brandCompliance.fastMode.maxFiles` to cap the scope. The MCP tool ignores the cap (`fast`/`full` equivalent in v1).
|
|
178
|
+
- **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.
|
|
179
|
+
- **When you want tone-by-context rules today:** Manual audit until v1.x ships. Component-state inference (empty/error/success/loading) requires JSX-context analysis that's a separate brainstorm.
|
|
180
|
+
|
|
181
|
+
## Status
|
|
182
|
+
|
|
183
|
+
**v1 — in implementation.** See:
|
|
184
|
+
|
|
185
|
+
- Spec: `docs/changes/design-pipeline/audit-brand-compliance/proposal.md`
|
|
186
|
+
- ADR (input schema source): `docs/knowledge/decisions/0028-brand-guidelines-source-of-truth.md`
|
|
187
|
+
- Roadmap entry: `design-pipeline sub-project #3` in `docs/roadmap.md`
|
|
188
|
+
- Sibling rule-based audits: `audit-component-anatomy` (#2), `detect-design-drift` (#1)
|
|
189
|
+
- Cross-cutting: extracts `Verifier<F>` interface at `packages/cli/src/shared/verifier.ts` (deferred until 4th data point — this is it)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: audit-brand-compliance
|
|
2
|
+
version: "0.1.0"
|
|
3
|
+
description: Rule-based brand-semantics audit. Detects token misuse (BRAND-T001 via $extensions.harness.brand.forbidden_contexts) and voice violations (BRAND-V001 via DESIGN.md voice.forbidden_phrases). 4th composed verifier in harness check-design. Triggers extraction of the formal verifier interface.
|
|
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 audit-brand-compliance
|
|
19
|
+
args:
|
|
20
|
+
- name: path
|
|
21
|
+
description: Project root path
|
|
22
|
+
required: false
|
|
23
|
+
- name: mode
|
|
24
|
+
description: "fast or full (equivalent in v1)"
|
|
25
|
+
required: false
|
|
26
|
+
- name: files
|
|
27
|
+
description: Optional list of files to scope the scan
|
|
28
|
+
required: false
|
|
29
|
+
mcp:
|
|
30
|
+
tool: run_skill
|
|
31
|
+
input:
|
|
32
|
+
skill: audit-brand-compliance
|
|
33
|
+
path: string
|
|
34
|
+
type: rigid
|
|
35
|
+
tier: 2
|
|
36
|
+
phases:
|
|
37
|
+
- name: load
|
|
38
|
+
description: Parse DESIGN.md ## Brand Rules and tokens.json $extensions.harness.brand
|
|
39
|
+
required: true
|
|
40
|
+
- name: scan
|
|
41
|
+
description: Walk JSX text/attributes for forbidden phrases; scan source for token-misuse-in-context
|
|
42
|
+
required: true
|
|
43
|
+
- name: report
|
|
44
|
+
description: Aggregate findings; emit BRAND-T* / BRAND-V001 with severity per design.strictness
|
|
45
|
+
required: true
|
|
46
|
+
state:
|
|
47
|
+
persistent: false
|
|
48
|
+
depends_on:
|
|
49
|
+
- detect-design-drift
|
|
50
|
+
- harness-design
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Audit Component Anatomy
|
|
2
|
+
|
|
3
|
+
> Detect missing required anatomy parts in component definitions and missing-anatomy-component patterns in composition. First programmatic enforcer of component-anatomy rules — finds what design-component-anatomy reference content prescribes.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- Reviewing component-library code for completeness of slots, states, sizes
|
|
8
|
+
- Detecting compositional patterns where a needed anatomy component is missing (e.g., `.map()` over data with no empty branch; async actions with no loading boundary)
|
|
9
|
+
- After component-library changes to verify anatomy contracts still hold
|
|
10
|
+
- Before merging UI changes to catch anatomy regressions
|
|
11
|
+
- When `on_new_feature` triggers fire and the feature touches a component definition or usage
|
|
12
|
+
- NOT for token validation (use harness-design-system)
|
|
13
|
+
- NOT for accessibility compliance (use harness-accessibility — overlap is documented; this skill defers label-association findings to a11y)
|
|
14
|
+
- NOT for aesthetic critique or polish suggestions (use harness-design-craft)
|
|
15
|
+
- NOT for declared-anti-pattern enforcement (use harness-design)
|
|
16
|
+
|
|
17
|
+
## Process
|
|
18
|
+
|
|
19
|
+
### Phase 1: SCAN — Identify component types and run audits
|
|
20
|
+
|
|
21
|
+
1. **Read project configuration.** Check `harness.config.json` for:
|
|
22
|
+
- `design.strictness` — `strict`/`standard`/`permissive` (default `standard`)
|
|
23
|
+
- `design.audit.componentAnatomy.enabled` — gate for the entire audit AND the harness-accessibility deferral (default `true`)
|
|
24
|
+
- `design.audit.componentAnatomy.catalog` — `"default"` or path to project-supplied catalog
|
|
25
|
+
- `design.audit.componentAnatomy.patterns` — `"all"`, `"none"`, or explicit list of pattern codes
|
|
26
|
+
- `design.audit.componentAnatomy.fastMode.{patterns,maxFiles}` — fast-mode controls
|
|
27
|
+
|
|
28
|
+
2. **Load source-of-truth catalogs (resolution order, most specific wins):**
|
|
29
|
+
- Component author's JSDoc `@component-type X` + `@anatomy-slot ...` etc. — file-level self-declaration
|
|
30
|
+
- `design-system/DESIGN.md` `## Component Registry` + `## Component Anatomy Overrides` — project-level
|
|
31
|
+
- Built-in convention library (20 component types: Button, Input, Select, Modal/Dialog, EmptyState, Card, Tabs, Menu, Toast, Form, Accordion, Tooltip, Popover, Drawer, Slider, Switch, Checkbox, Radio, Avatar, Badge) — fallback default
|
|
32
|
+
- No match for a file's component-type → silent skip (no false positives)
|
|
33
|
+
|
|
34
|
+
3. **Identify component type per file** using the same resolution order:
|
|
35
|
+
- JSDoc `@component-type X` tag → file IS X
|
|
36
|
+
- DESIGN.md Component Registry mapping → use that
|
|
37
|
+
- Top-level export name matches a catalog entry → use that
|
|
38
|
+
- No match → skip silently
|
|
39
|
+
|
|
40
|
+
4. **Run convention audit (definition findings, `ANAT-D*` codes).** For each catalogued component file:
|
|
41
|
+
- Parse component definition via TypeScript Compiler API (AST)
|
|
42
|
+
- Extract exported component's prop type / interface
|
|
43
|
+
- Check that each required anatomy part (slot, state, variant, size) is exposed as a prop or sub-component
|
|
44
|
+
- Emit `ANAT-D{NNN}` finding for each missing required part
|
|
45
|
+
- Emit `ANAT-D000` info finding when JSDoc declaration diverges from catalog convention
|
|
46
|
+
|
|
47
|
+
5. **Run pattern audit (pattern-presence findings, `ANAT-P*` codes).** For each file matching pattern targets:
|
|
48
|
+
- Parse via tree-sitter (`tree-sitter-typescript` + `tree-sitter-tsx`)
|
|
49
|
+
- Run each enabled pattern's S-expression query
|
|
50
|
+
- Post-process captures to confirm pattern presence (e.g., `.map()` without an empty-branch guard)
|
|
51
|
+
- Emit `ANAT-P{NNN}` finding for each match
|
|
52
|
+
- Skip patterns NOT enabled in `design.audit.componentAnatomy.patterns`
|
|
53
|
+
|
|
54
|
+
6. **Check for harness-accessibility overlap deferral.** When `design.audit.componentAnatomy.enabled = true`, this skill OWNS label-slot definition findings; harness-accessibility defers A11Y-010 and A11Y-050 for catalogued components. Track deferrals in `meta.deferralsToHarnessDesign` count.
|
|
55
|
+
|
|
56
|
+
### Phase 2: EVALUATE — Apply severity model
|
|
57
|
+
|
|
58
|
+
1. **Map finding severity using `design.strictness`:**
|
|
59
|
+
- `strict` — all findings are `error` severity (CI blocks)
|
|
60
|
+
- `standard` — required-part missing → `error`; optional-part missing → `warn`; info findings → `info`
|
|
61
|
+
- `permissive` — all findings → `info` (nothing blocks)
|
|
62
|
+
|
|
63
|
+
2. **Cross-reference with graph constraints.** If a graph exists at `.harness/graph/`:
|
|
64
|
+
- Query existing `VIOLATES_CRAFT` edges via extended `DesignConstraintAdapter`
|
|
65
|
+
- Identify NEW violations (not in graph) and RESOLVED violations (in graph but not in current scan)
|
|
66
|
+
|
|
67
|
+
3. **De-duplicate via deferral.** When this skill produces a finding whose root-cause is also tracked by harness-accessibility, prefer this skill's finding (anatomy-level cause) and increment deferrals counter.
|
|
68
|
+
|
|
69
|
+
### Phase 3: REPORT — Format and persist findings
|
|
70
|
+
|
|
71
|
+
1. **Write VIOLATES_CRAFT edges to graph.** Each finding becomes:
|
|
72
|
+
- Source: component file (`code_file` node) or component (`component` node)
|
|
73
|
+
- Target: `design_rule` node keyed by finding code (`ANAT-D023`, `ANAT-P001`)
|
|
74
|
+
- Metadata: severity, line, message, evidence snippet, runId
|
|
75
|
+
- Idempotent: re-running the audit produces no duplicate edges
|
|
76
|
+
|
|
77
|
+
2. **Format the report.** Grouped by component file, with finding codes linked to `docs/changes/design-pipeline/audit-component-anatomy/finding-codes.md`. Each finding includes:
|
|
78
|
+
- Code (e.g., `ANAT-D023`)
|
|
79
|
+
- Severity per `design.strictness`
|
|
80
|
+
- File path + line number
|
|
81
|
+
- Component type (if identified)
|
|
82
|
+
- Message + evidence snippet
|
|
83
|
+
- Fix hint (concrete next-step text — not "fix this")
|
|
84
|
+
|
|
85
|
+
3. **Emit summary.** Total findings, breakdown by severity, count of `meta.deferralsToHarnessDesign`, mode (`fast`/`full`), `runId`.
|
|
86
|
+
|
|
87
|
+
## Harness Integration
|
|
88
|
+
|
|
89
|
+
- **`harness validate`** — Fast-mode audit hook (convention catalog only; patterns are opt-in via `fastMode.patterns: true`). Findings respect `design.strictness`.
|
|
90
|
+
- **`mcp__harness__audit_anatomy`** — Programmatic API (input: path, mode, files, designStrictness, catalog; output: findings, summary, catalog applied, deferrals count). Consumed by harness check-design verifier (sub-project #4) and design-pipeline orchestrator (sub-project #5).
|
|
91
|
+
- **`DesignConstraintAdapter`** — Extended to register `ANAT-*` rule code namespace and write VIOLATES_CRAFT edges. Mirrors how harness-accessibility uses it for A11Y-\* codes.
|
|
92
|
+
- **`harness-accessibility`** — Coordinates via i18n-style deferral pattern. When `design.audit.componentAnatomy.enabled = true`, a11y defers A11Y-010 and A11Y-050 for catalogued components.
|
|
93
|
+
- **`design-component-anatomy`** (knowledge skill) — Source of convention vocabulary (slot, variant, state, size, exclusivity, required). This skill's catalog operationalizes that knowledge.
|
|
94
|
+
|
|
95
|
+
## Success Criteria
|
|
96
|
+
|
|
97
|
+
See `docs/changes/design-pipeline/audit-component-anatomy/proposal.md` for the full 30 success criteria. Highlights:
|
|
98
|
+
|
|
99
|
+
- Convention findings produced for known component types only (silent skip for unknown — zero false positives)
|
|
100
|
+
- JSDoc self-declaration overrides convention; DESIGN.md overrides convention but not JSDoc
|
|
101
|
+
- Pattern false-positive rate ≤ 5% on the 50-fixture corpus
|
|
102
|
+
- harness-accessibility deferral works (one finding for one root cause, not two)
|
|
103
|
+
- Fast-mode runtime ≤ 3s on 500-file repo
|
|
104
|
+
|
|
105
|
+
## Examples
|
|
106
|
+
|
|
107
|
+
### Example: Button missing a required state
|
|
108
|
+
|
|
109
|
+
**Input:** `packages/ui/src/Button.tsx` exports a `Button` component whose prop type omits any `loading` prop.
|
|
110
|
+
|
|
111
|
+
**Output:**
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
ANAT-D002 [error] Button.tsx — Button convention requires state:loading (exclusive)
|
|
115
|
+
File: packages/ui/src/Button.tsx
|
|
116
|
+
Component: Button (identified via export-name match)
|
|
117
|
+
Convention: APG/button + Radix Primitives Button
|
|
118
|
+
Fix: Add a `loading?: boolean` prop, OR add `@anatomy-state loading exclusive`
|
|
119
|
+
JSDoc tag to the component if loading is intentionally omitted.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Severity is `error` under `design.strictness: standard` because the missing part is `required: true` in the Button convention.
|
|
123
|
+
|
|
124
|
+
### Example: Empty list with no fallback (pattern-presence finding)
|
|
125
|
+
|
|
126
|
+
**Input:** `src/pages/Dashboard.tsx` contains `{items.map(item => <Card item={item} />)}` with no empty-state branch.
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
ANAT-P001 [warn] Dashboard.tsx:42 — map() over data with no empty-state branch
|
|
132
|
+
File: src/pages/Dashboard.tsx:42
|
|
133
|
+
Pattern: ANAT-P001 map-without-empty
|
|
134
|
+
Fix: Wrap the map in a conditional that renders <EmptyState> when items.length === 0:
|
|
135
|
+
items.length === 0 ? <EmptyState ... /> : items.map(...)
|
|
136
|
+
Or extract a guard component that handles both branches.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Severity is `warn` for pattern-presence findings under `design.strictness: standard`; `error` under `strict`.
|
|
140
|
+
|
|
141
|
+
### Example: Catalogued component with JSDoc divergence
|
|
142
|
+
|
|
143
|
+
**Input:** A Tabs.tsx file has `@anatomy-slot trigger` JSDoc but the convention library says Tabs requires both `trigger` AND `panel`.
|
|
144
|
+
|
|
145
|
+
**Output:**
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
ANAT-D000 [info] Tabs.tsx — JSDoc declaration omits 2 conventional anatomy parts
|
|
149
|
+
File: packages/ui/src/Tabs.tsx
|
|
150
|
+
Component: Tabs (identified via JSDoc @component-type)
|
|
151
|
+
Divergence: declared slots [trigger]; convention also expects [panel, list]
|
|
152
|
+
Note: JSDoc wins (resolution order #1). This is an info finding to surface
|
|
153
|
+
the divergence — no action required if intentional.
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The `ANAT-D000` info code surfaces JSDoc-vs-convention divergence so authors can confirm the divergence is intentional.
|
|
157
|
+
|
|
158
|
+
## Gates
|
|
159
|
+
|
|
160
|
+
- **No findings without a parsed catalog.** If the convention library or DESIGN.md catalog cannot be loaded, stop with an explanatory error — do not emit speculative findings.
|
|
161
|
+
- **No usage-side findings in v1.** Only `ANAT-D*` (definition) and `ANAT-P*` (pattern) codes. Usage findings (`ANAT-U*`) ship in v2.
|
|
162
|
+
- **No autofix.** Findings include fix-hint text only; source files are never modified by this skill.
|
|
163
|
+
- **Strictness from config, not assumed.** Read `design.strictness` from `harness.config.json`; default to `standard` if absent.
|
|
164
|
+
|
|
165
|
+
## Escalation
|
|
166
|
+
|
|
167
|
+
- **When a project uses a non-standard component name (e.g., `PrimaryButton` instead of `Button`).** The export-name resolver won't match. Two paths: (1) the component author adds `@component-type Button` JSDoc, or (2) the project adds the file to DESIGN.md `## Component Registry`. Both unblock the audit without touching the catalog.
|
|
168
|
+
- **When the convention catalog is wrong for an opinionated project.** Add a per-component override block to DESIGN.md `## Component Anatomy Overrides`. The override wins for that component type; other catalogued types behave normally.
|
|
169
|
+
- **When pattern-presence findings have a false-positive rate >5% in a particular project.** Disable the specific pattern via `design.audit.componentAnatomy.patterns: ["-ANAT-P003"]` (negated form). Report the false positive — pattern catalog evolves based on real-world signal.
|
|
170
|
+
- **When harness-accessibility produces a finding this skill should own.** Verify `design.audit.componentAnatomy.enabled = true` and the component IS in the catalog. If both, the a11y deferral is misfiring — file an issue; likely a `getCatalogTypes()` cache staleness.
|
|
171
|
+
- **When `harness validate` runtime exceeds 3 seconds.** Set `design.audit.componentAnatomy.fastMode.maxFiles` to cap the scope, OR set `design.audit.componentAnatomy.fastMode.patterns: false` to skip pattern runs in fast mode (patterns are full-mode only by default).
|
|
172
|
+
- **When a graph operation fails.** Skip graph integration for that run; emit findings to the report only. Log a warning that `VIOLATES_CRAFT` edges were not persisted. The audit's findings are still actionable; the graph is a consumer, not a gate.
|
|
173
|
+
|
|
174
|
+
## Status
|
|
175
|
+
|
|
176
|
+
**v1 — in implementation.** See:
|
|
177
|
+
|
|
178
|
+
- Spec: `docs/changes/design-pipeline/audit-component-anatomy/proposal.md`
|
|
179
|
+
- Plan: `docs/changes/design-pipeline/audit-component-anatomy/plans/2026-05-23-audit-component-anatomy-plan.md`
|
|
180
|
+
- Finding codes: `docs/changes/design-pipeline/audit-component-anatomy/finding-codes.md`
|
|
181
|
+
- Roadmap entry: `design-pipeline sub-project #2` in `docs/roadmap.md`
|