@harness-engineering/cli 2.6.0 → 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-ORNMAYHB.js → chunk-4EGPFB7V.js} +6196 -125
  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-NX6A24DS.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-HYTRDAHV.js → chunk-WOXR6I3R.js} +3 -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-KZ3PPUYE.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-GZH4EYGD.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,58 @@
1
+ name: security-craft
2
+ version: "0.1.0"
3
+ description: LLM-judgment critique of security posture (TS/JS source). Threat-modeling-as-skill — critiques whether trust boundaries are respected, where implicit privilege escalation lurks, whether the code defends in depth, whether least authority is honored. AST-driven signal detection fires only on files with security-relevant constructs; conservative confidence defaults manage the FP risk inherent in judgment-based security. Sixth non-design craft-pipeline ceiling skill (the final sub-project, #10 of 10).
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 security-craft
19
+ args:
20
+ - name: path
21
+ description: Project root path
22
+ required: false
23
+ - name: files
24
+ description: Optional file scope (overrides discovery)
25
+ required: false
26
+ - name: packages
27
+ description: Restrict to specific packages under packages/
28
+ required: false
29
+ - name: max-files
30
+ description: Cap source-file count (default 100)
31
+ required: false
32
+ - name: max-signals-per-file
33
+ description: Cap per-file signal critique (default 10)
34
+ required: false
35
+ mcp:
36
+ tool: security_craft
37
+ input:
38
+ path: string
39
+ type: rigid
40
+ tier: 2
41
+ phases:
42
+ - name: discover
43
+ description: Walk packages/STAR/src/ recursively; exclude tests + build dirs
44
+ required: true
45
+ - name: signal
46
+ description: AST-driven security construct detection (http handlers, middleware, auth APIs, child_process/eval, fs writes, raw queries, network egress, secret handling)
47
+ required: true
48
+ - name: critique
49
+ description: LLM-rubric loop per (file, signal, rubric) with conservative-confidence bias
50
+ required: true
51
+ - name: report
52
+ description: Aggregate findings + cost telemetry + file/signal counts
53
+ required: true
54
+ state:
55
+ persistent: false
56
+ depends_on:
57
+ - harness-security-scan
58
+ - harness-security-review
@@ -0,0 +1,184 @@
1
+ # Spec Craft
2
+
3
+ > LLM-judgment critique of spec quality (proposals + ADRs) against a curated rubric catalog from the spec-quality canon. Per-section critique with rubric-to-section mapping. Second member of the craft-pipeline initiative; highest-leverage craft skill because spec quality compounds across the entire planning → implementation → review lifecycle below it. Emits 3-axis findings (tier × impact × confidence per ADR 0019).
4
+
5
+ ## When to Use
6
+
7
+ - During PR review on a new or substantially-rewritten spec
8
+ - After authoring a proposal, before circulating it for review
9
+ - When onboarding a new contributor (audit specs they introduced)
10
+ - Periodically (per-sprint or per-release) to catch spec drift
11
+ - As the cross-cutting spec critic for harness-brainstorming (when newly-authored specs land)
12
+ - NOT for spec-structure enforcement (use `harness-soundness-review` — that's the rule-based floor)
13
+ - NOT for README / general-doc critique (use `docs-craft` #2)
14
+ - NOT for autofix / spec rewriting (this is judgment-only; v1.x may add `align-spec` sibling)
15
+ - NOT for source-code comment critique (use `code-craft` #4 or `docs-craft` #2)
16
+ - NOT for RFCs in v1 (v1.x)
17
+
18
+ ## Process
19
+
20
+ ### Phase 1: DISCOVER — Find spec files
21
+
22
+ 1. **Read project configuration.** Check `harness.config.json` for:
23
+ - `craft.spec.enabled` — gate (default `true`)
24
+ - `craft.spec.maxFiles` — doc count cap (default 50)
25
+ - `craft.spec.maxSectionsPerFile` — per-doc section cap (default 10)
26
+
27
+ 2. **Discover spec files:**
28
+ - **proposals:** `docs/changes/*/proposal.md` and `docs/changes/*/<sub>/proposal.md` (one level of nesting, for initiatives with sub-projects)
29
+ - **ADRs:** `docs/knowledge/decisions/*.md` (excluding README)
30
+ - Restrict via `--kinds proposal` or `--kinds adr` for single-kind runs.
31
+
32
+ ### Phase 2: PARSE — Split into named sections
33
+
34
+ For each spec file:
35
+
36
+ 1. **Strip YAML frontmatter** (`--- ... ---` block at top, if present).
37
+ 2. **Split by H2** (`## ...`) into named sections. Each section captures:
38
+ - `heading` — original H2 text (e.g., `"Decisions"`, `"Out-of-scope (v1)"`)
39
+ - `canonical` — normalized form for rubric matching (`"decisions"`, `"out-of-scope-v1"`)
40
+ - `body` — content between this H2 and the next
41
+ - `line` / `endLine` — line range
42
+ 3. Subsections (H3) stay part of the parent H2's body. v1.x may add per-H3 critique for sections like Decisions that have one row per H3.
43
+
44
+ ### Phase 3: CRITIQUE — Per (file, section, rubric) loop
45
+
46
+ 7 seed rubrics:
47
+
48
+ | Rubric | Title | Applies to |
49
+ | ----------- | ---------------------------------------- | ---------------------------------------- |
50
+ | `SPEC-R001` | Sharpness vs vagueness | `*` (all sections) |
51
+ | `SPEC-R002` | Cuts at the joints | `decisions`, `scope`, `technical-design` |
52
+ | `SPEC-R003` | Two readers, same understanding | `decisions`, `success-criteria` |
53
+ | `SPEC-R004` | Load-bearing decision vs ambient context | `decisions`, `overview` |
54
+ | `SPEC-R005` | Honest rationalizations | `rationalizations*` (regex) |
55
+ | `SPEC-R006` | Non-goals are non-goals | `out-of-scope*`, `non-goals*` (regex) |
56
+ | `SPEC-R007` | Stranger in 6 months | `*` (all sections) |
57
+
58
+ For each eligible (section, rubric) pair:
59
+
60
+ 1. Build prompt with rubric description + spec file path + section heading + section body (truncated to 2000 chars if longer for cost control).
61
+ 2. LLM returns fenced JSON: `null` (rubric doesn't apply / section is fine) OR `{ tier, impact, confidence, message }`.
62
+ 3. On non-null: emit a `SpecFinding` with `cite.rubricId` populated for ADR 0020 traceability.
63
+
64
+ ### Phase 4: REPORT — Aggregate + cost telemetry
65
+
66
+ Emit `SpecCraftOutput`:
67
+
68
+ ```ts
69
+ {
70
+ findings: SpecFinding[];
71
+ summary: {
72
+ phaseRun: ['critique'];
73
+ durationMs: number;
74
+ llmCalls: { provider, model, count, costUsd };
75
+ catalog: { rubricsApplied: string[] };
76
+ docsScanned: number;
77
+ sectionsScanned: number;
78
+ runId: string;
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## Harness Integration
84
+
85
+ - **`harness spec-craft`** — CLI entry. `--files <glob>` / `--kinds proposal,adr` / `--sections decisions,scope` / `--max-files <n>` / `--max-sections-per-file <n>` / `--json` / `--verbose`.
86
+ - **`mcp__harness__spec_craft`** — MCP tool. Same input/output. Consumed by agents.
87
+ - **Cross-cutting API:** `critiqueSpecFile(file, opts)` exported from `packages/cli/src/spec-craft/index.ts`. Future craft skills (or `harness-brainstorming`) can call this when they have a doc in hand without re-walking the project.
88
+ - **Shared craft infrastructure (extracted on this PR):** `LlmProvider`, `MockLlmProvider`, `derivePriority`, 3-axis types all live in `packages/cli/src/shared/craft/`. design-craft + naming-craft + spec-craft import from there; design-craft + naming-craft keep their old import paths via re-export shims.
89
+
90
+ ## Success Criteria
91
+
92
+ See `docs/changes/craft-pipeline/spec-craft/proposal.md` for the full 34 success criteria. Highlights:
93
+
94
+ - 7 seed rubrics ship at `catalog/rubrics/<id>.ts` (file-per-rubric, matches naming-craft)
95
+ - 3-axis output preserved (tier × impact × confidence, never collapsed)
96
+ - `cite.rubricId` populated on every finding (ADR 0020)
97
+ - Section parser strips frontmatter; splits by H2 only
98
+ - Rubric-to-section mapping skips silently when rubric doesn't apply
99
+ - `critiqueSpecFile` cross-cutting API works on a single file without project walk
100
+ - All existing design-craft + naming-craft tests still pass after shared/craft extraction (zero behavior change)
101
+
102
+ ## Examples
103
+
104
+ ### Example: Vague Decisions section
105
+
106
+ **Input:** A proposal's `## Decisions` section reads:
107
+
108
+ ```
109
+ | Decision | Why |
110
+ |----------|-----|
111
+ | Use modern stack | scalable and clean |
112
+ | Defer auth | not in scope |
113
+ ```
114
+
115
+ **Output (mock LLM):**
116
+
117
+ ```
118
+ SPEC-R001 [polish/medium/medium] ## Decisions:34
119
+ "Modern stack" and "scalable and clean" are vague — no concrete framework
120
+ named, no metric for "scalable", no operational definition of "clean".
121
+ Sharpen: name the framework, state the scale target (req/sec, team size),
122
+ define what "clean" means in observable terms.
123
+ SPEC-R004 [polish/medium/medium] ## Decisions:34
124
+ The Decisions section pads load-bearing choices with vague qualifiers
125
+ rather than naming the trade-off chosen and the rejected alternative.
126
+ ```
127
+
128
+ ### Example: Strawmanned Rationalizations
129
+
130
+ **Input:** A `## Rationalizations to reject` section reads:
131
+
132
+ ```
133
+ | "Use a different framework" | Other frameworks are worse |
134
+ ```
135
+
136
+ **Output:**
137
+
138
+ ```
139
+ SPEC-R005 [foundational/large/high] ## Rationalizations to reject:88
140
+ "Other frameworks are worse" is a strawman — not stated charitably, not
141
+ paired with a specific reason. Steelman the rejected position: name the
142
+ competing framework, name its strongest feature, then explain the specific
143
+ trade-off that made it unsuitable here.
144
+ ```
145
+
146
+ ### Example: Empty project — no specs
147
+
148
+ **Input:** Project has no `docs/changes/` or `docs/knowledge/decisions/` directory.
149
+
150
+ **Output:**
151
+
152
+ ```
153
+ No spec findings.
154
+
155
+ Summary: 0 findings across 0 docs (0 sections, 7 rubrics, 0 LLM calls, $0.0000, 3ms)
156
+ ```
157
+
158
+ ## Gates
159
+
160
+ - **No autofix.** Sibling `align-spec` deferred until signal warrants safe-to-apply rewrites.
161
+ - **No README / general doc critique.** docs-craft (#2) territory.
162
+ - **No source-code comment critique.** code-craft / docs-craft territory.
163
+ - **No B' bootstrap.** Same posture as naming-craft v1.
164
+ - **No graph persistence.** Phase 1 MVP.
165
+ - **No vision/deep mode.** Specs are text.
166
+ - **No structural floor enforcement.** harness-soundness-review checks the floor; spec-craft assumes the floor is satisfied and critiques the ceiling.
167
+
168
+ ## Escalation
169
+
170
+ - **When LLM cost is too high:** drop `maxSectionsPerFile` to 5 or `maxFiles` to 25. Per-doc cost = sections × rubrics × per-call. Rubric-to-section mapping already prunes most calls; further: use `--sections decisions` to target the highest-value section.
171
+ - **When a rubric produces high false-positive rate:** v1 has no per-rubric disable; v1.x adds `craft.spec.disabledRubrics: ['SPEC-R007']`. Until then: filter findings by `cite.rubricId` in your consumer.
172
+ - **When a spec has intentionally aspirational vagueness (e.g., a manifesto-style Overview):** SPEC-R001 will flag it; low-confidence findings are de-emphasized per ADR 0019. v1.x adds per-section opt-out via `<!-- spec-craft:skip -->` HTML comment.
173
+ - **When you want a doc-level summary instead of per-section findings:** v1 is per-section only; v1.x adds a `--mode doc` opt-in for whole-doc critique.
174
+ - **When you want to critique RFCs:** v1.x. For now, point `--files <rfc.md>` at a single file — the section parser works on any markdown.
175
+
176
+ ## Status
177
+
178
+ **v1 — in implementation.** See:
179
+
180
+ - Spec: `docs/changes/craft-pipeline/spec-craft/proposal.md`
181
+ - Roadmap entry: `craft-pipeline sub-project #6` (the highest-leverage craft skill)
182
+ - Sibling craft skills: `harness-design-craft` (design-pipeline #6), `naming-craft` (craft-pipeline #1)
183
+ - Shared infrastructure: `packages/cli/src/shared/craft/` (extracted on this PR)
184
+ - Future: `align-spec` (FIX side), docs-craft (#2), test-craft (#3), code-craft (#4) — each can call `critiqueSpecFile` if they want spec-level critique for a doc they're already processing.
@@ -0,0 +1,55 @@
1
+ name: spec-craft
2
+ version: "0.1.0"
3
+ description: LLM-judgment critique of spec quality (proposals + ADRs) against a curated rubric catalog. Per-section critique with rubric-to-section mapping. Second craft-pipeline ceiling skill; highest-leverage because spec quality compounds across the lifecycle below it. Triggered the shared craft infrastructure extraction.
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 spec-craft
19
+ args:
20
+ - name: path
21
+ description: Project root path
22
+ required: false
23
+ - name: files
24
+ description: Optional spec file/glob scope
25
+ required: false
26
+ - name: kinds
27
+ description: Restrict to proposal / adr
28
+ required: false
29
+ - name: sections
30
+ description: Restrict to canonical section names
31
+ required: false
32
+ mcp:
33
+ tool: spec_craft
34
+ input:
35
+ path: string
36
+ type: rigid
37
+ tier: 2
38
+ phases:
39
+ - name: discover
40
+ description: Glob docs/changes/*/proposal.md + docs/knowledge/decisions/*.md
41
+ required: true
42
+ - name: parse
43
+ description: Markdown H2 section splitter (frontmatter-aware)
44
+ required: true
45
+ - name: critique
46
+ description: LLM-rubric loop per (file, section, rubric) where rubric applies
47
+ required: true
48
+ - name: report
49
+ description: Aggregate findings + cost telemetry + doc/section counts
50
+ required: true
51
+ state:
52
+ persistent: false
53
+ depends_on:
54
+ - harness-soundness-review
55
+ - harness-design-craft
@@ -0,0 +1,212 @@
1
+ # Test Craft
2
+
3
+ > LLM-judgment critique of test quality across vitest / jest / mocha / playwright. Fourth member of the craft-pipeline initiative. Per-`it`/`test` block critique with best-effort source pairing for contract-vs-implementation rubrics. Tests are often the worst-written code in a codebase precisely because the rule-based floor (coverage threshold) is so easy to clear. 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 tests
8
+ - After ramping up coverage to audit whether new tests actually add signal
9
+ - When onboarding a contributor — audit tests they introduced
10
+ - Periodically to catch accumulated low-signal tests + redundant fixtures
11
+ - NOT for coverage analysis (use `vitest --coverage` or harness-tdd — that's the floor)
12
+ - NOT for autofix / rewriting (v2's `align-test` may add safe renames)
13
+ - NOT for `.test-d.ts` type tests in v1 (v1.x — different rubric vocabulary)
14
+ - NOT for fixture / helper / mock files in v1 (v1.x)
15
+ - NOT for snapshot tests in v1 (different correctness criteria; v1.x)
16
+ - NOT for non-TS/JS languages in v1 (v1.x)
17
+
18
+ ## Process
19
+
20
+ ### Phase 1: DISCOVER — Test files + framework
21
+
22
+ 1. **Read project configuration.** Check `harness.config.json` for:
23
+ - `craft.test.enabled` — gate (default `true`)
24
+ - `craft.test.maxFiles` (default 100), `craft.test.maxTestsPerFile` (default 20)
25
+ - `craft.test.frameworks` — restrict to subset (default: all four)
26
+ - `craft.test.sourcePair` — toggle source-pairing (default true)
27
+
28
+ 2. **Glob test files** under project root: `**/*.{test,spec}.{ts,tsx,js,jsx}`. Skip `node_modules`, `dist`, `build`, `coverage`, dotdirs.
29
+
30
+ 3. **Detect framework per file** via import signatures (order matters; most-specific first):
31
+ - `@playwright/test` → playwright
32
+ - `@jest/globals` → jest
33
+ - `vitest` → vitest
34
+ - `import 'mocha'` → mocha
35
+ - Fallback: vitest (most common; jest-with-globals projects still extract correctly because the AST shape matches)
36
+
37
+ ### Phase 2: EXTRACT — Per-test AST walk
38
+
39
+ Single TS Compiler API walk per file. For each `CallExpression`:
40
+
41
+ - **describe** — push name onto nesting stack, recurse, pop
42
+ - **it / test** — extract `testName` (first string-literal arg), capture current nesting, capture callback body text (truncated to 1500 chars in prompt)
43
+ - Modifiers handled: `.skip`, `.only`, `.todo` (todo excluded from critique — no body to critique)
44
+ - Non-string-literal test names (computed / templates) skip silently
45
+
46
+ ### Phase 3: PAIR — Best-effort source resolution
47
+
48
+ For each test file, try in order:
49
+
50
+ 1. **Sibling** — `foo.test.ts` → `foo.ts` (same dir)
51
+ 2. **Co-located in src** — `tests/foo.test.ts` → `../src/foo.ts`
52
+ 3. **Monorepo-style** — `tests/foo.test.ts` → `../../src/foo.ts`
53
+
54
+ When source resolves, content (truncated to 2000 chars) is added to the LLM prompt under `Source under test:`. This enables `TEST-R007` (contract-not-implementation) to actually compare assertions against the function's public surface. When no source resolves, test-file-only rubrics still fire.
55
+
56
+ ### Phase 4: CRITIQUE — Per (test, rubric) loop
57
+
58
+ 8 seed rubrics:
59
+
60
+ | Rubric | Description |
61
+ | --------------------------------------- | --------------------------------------------------------------------------- |
62
+ | `TEST-R001` contract-not-narrative-name | Test name describes the contract ("returns null when empty"), not narrative |
63
+ | `TEST-R002` meaningful-assertion | Assertion proves something the implementation could plausibly violate |
64
+ | `TEST-R003` arrange-act-assert | Three visually distinct phases, not interleaved |
65
+ | `TEST-R004` fixture-earns-setup-cost | Heavy beforeEach must be justified by what's asserted |
66
+ | `TEST-R005` single-responsibility | One assertion target per `it`; multiple unrelated assertions split poorly |
67
+ | `TEST-R006` deleting-loses-something | Would removing this test lose specific coverage, or is it redundant? |
68
+ | `TEST-R007` contract-not-implementation | Tests the documented behaviour, not the internal structure |
69
+ | `TEST-R008` explicit-failure-mode | Failure message narrates what went wrong without reading the test |
70
+
71
+ For each (test, rubric) pair:
72
+
73
+ 1. Build prompt with rubric + test (name + nesting + body) + optional source + framework label.
74
+ 2. LLM returns fenced JSON: `null` (rubric doesn't apply / test is fine) OR `{ tier, impact, confidence, message }`.
75
+ 3. On non-null: emit `TestFinding` with `cite.rubricId` for ADR 0020 traceability.
76
+
77
+ ### Phase 5: REPORT — Aggregate + cost telemetry
78
+
79
+ Emit `TestCraftOutput`:
80
+
81
+ ```ts
82
+ {
83
+ findings: TestFinding[];
84
+ summary: {
85
+ phaseRun: ['critique'];
86
+ durationMs: number;
87
+ llmCalls: { provider, model, count, costUsd };
88
+ catalog: { rubricsApplied: string[] };
89
+ counts: { filesScanned, testsExtracted, testsSkippedOrTodo, sourcePaired };
90
+ frameworksDetected: Record<TestFramework, number>;
91
+ runId: string;
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Harness Integration
97
+
98
+ - **`harness test-craft`** — CLI entry. `--files` / `--frameworks` / `--max-files` / `--max-tests-per-file` / `--no-source-pair` / `--json` / `--verbose`.
99
+ - **`mcp__harness__test_craft`** — MCP tool. Same input/output. Consumed by agents.
100
+ - **Cross-cutting API:** `critiqueTestsInFile(file, opts)` exported. Works on a single test file without project walk; honours framework filter and source-pairing toggle.
101
+ - **Shared craft infrastructure:** imports `LlmProvider` + 3-axis types + `derivePriority` from `packages/cli/src/shared/craft/`.
102
+
103
+ ## Success Criteria
104
+
105
+ See `docs/changes/craft-pipeline/test-craft/proposal.md` for the full 36 success criteria. Highlights:
106
+
107
+ - 8 seed rubrics ship in `catalog/rubrics/<id>.ts` (file-per-rubric, matches naming/spec/copy-craft)
108
+ - 3-axis output preserved (tier × impact × confidence)
109
+ - `cite.rubricId` populated on every finding (ADR 0020)
110
+ - Framework detection: `@playwright/test` / `@jest/globals` / `vitest` / `mocha` import signatures; vitest fallback
111
+ - Per-test extraction handles `.skip` / `.only` / `.todo` with metadata; describe-chain nesting captured
112
+ - Source pairing best-effort with silent skip when no match
113
+ - Plugin slash-commands pre-generated (avoids CI drift failure pattern)
114
+
115
+ ## Examples
116
+
117
+ ### Example: Narrative test name
118
+
119
+ **Input:** `src/parse.test.ts`:
120
+
121
+ ```ts
122
+ describe('parseTokens', () => {
123
+ it('works correctly', () => {
124
+ expect(parseTokens('a,b,c')).toEqual(['a', 'b', 'c']);
125
+ });
126
+ });
127
+ ```
128
+
129
+ **Output (mock LLM):**
130
+
131
+ ```
132
+ src/parse.test.ts
133
+ TEST-R001 [polish/medium/high] vitest:2
134
+ parseTokens > works correctly
135
+ "works correctly" narrates the test without naming the contract. Try
136
+ "splits comma-separated input into trimmed segments" or "preserves
137
+ order across the split".
138
+ ```
139
+
140
+ ### Example: Tautological assertion
141
+
142
+ **Input:**
143
+
144
+ ```ts
145
+ it('returns a user', () => {
146
+ const user = createUser({ name: 'a' });
147
+ expect(user).toBeDefined();
148
+ });
149
+ ```
150
+
151
+ **Output:**
152
+
153
+ ```
154
+ TEST-R002 [foundational/medium/high] vitest:8
155
+ createUser > returns a user
156
+ `toBeDefined()` after `createUser(...)` cannot fail — the return is
157
+ always defined by the function's signature. Assert the contract:
158
+ `expect(user.name).toBe('a')` or `expect(user).toMatchObject({...})`.
159
+ ```
160
+
161
+ ### Example: Heavy fixture, trivial assertion
162
+
163
+ **Input:**
164
+
165
+ ```ts
166
+ it('handles input', () => {
167
+ // 47 lines of beforeEach setup that mocks 6 collaborators
168
+ const result = handler.handle({});
169
+ expect(result).toBeTruthy();
170
+ });
171
+ ```
172
+
173
+ **Output:**
174
+
175
+ ```
176
+ TEST-R004 [polish/large/medium] vitest:142
177
+ handler > handles input
178
+ 47-line beforeEach + 6 mocks produce a result that's asserted with
179
+ `toBeTruthy`. The fixture's complexity exceeds the test's signal —
180
+ either drop the fixture and use a simpler stub, or assert the
181
+ specific outcome that justified the setup (e.g., which collaborator
182
+ was called with what arguments).
183
+ ```
184
+
185
+ ## Gates
186
+
187
+ - **No autofix.** v2's `align-test`.
188
+ - **No coverage analysis.** That's the floor (`vitest --coverage`).
189
+ - **No `.test-d.ts` type tests** (v1.x).
190
+ - **No fixture / helper / mock file critique** (v1.x).
191
+ - **No snapshot rubrics** (v1.x).
192
+ - **No non-TS/JS language support** (v1.x).
193
+ - **No B' bootstrap.** Same posture as naming/spec/copy-craft.
194
+ - **No graph persistence.** Phase 1 MVP.
195
+
196
+ ## Escalation
197
+
198
+ - **When LLM cost is too high:** drop `maxTestsPerFile` (default 20) or scope to specific frameworks with `--frameworks vitest`. Disable source-pairing with `--no-source-pair` to halve prompt size on every test.
199
+ - **When intentionally narrative test names get flagged (e.g., learning tests, examples):** scope via `--files` to exclude. v1.x adds `// test-craft:skip` annotation.
200
+ - **When source-pairing finds the wrong source for ambiguous names:** the LLM gets misleading context for `TEST-R007`. Use `--no-source-pair` to fall back to test-file-only rubrics, or v1.x's `harness.config.json` test→source mapping.
201
+ - **When jest-with-globals tests aren't detected:** the framework defaults to vitest (AST is compatible); critique still runs. If you need jest tagging specifically, add `import { describe, it } from '@jest/globals'` to make the signature explicit.
202
+ - **When `it.each` / `test.each` blocks get critiqued as a single test:** v1 captures the `each` invocation as one item with a generic name pattern. v1.x adds per-iteration critique.
203
+
204
+ ## Status
205
+
206
+ **v1 — in implementation.** See:
207
+
208
+ - Spec: `docs/changes/craft-pipeline/test-craft/proposal.md`
209
+ - Roadmap entry: `craft-pipeline sub-project #3`
210
+ - Sibling craft skills: `naming-craft` (#1), `spec-craft` (#6), `copy-craft` (#5)
211
+ - Shared infrastructure: `packages/cli/src/shared/craft/`
212
+ - Future: `align-test` (FIX side, v2), docs-craft (#2), code-craft (#4)
@@ -0,0 +1,58 @@
1
+ name: test-craft
2
+ version: "0.1.0"
3
+ description: LLM-judgment critique of test quality across vitest / jest / mocha / playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing for contract-vs-implementation rubrics. Tests are often the worst-written code in a codebase precisely because the rule-based floor is so easy to clear.
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 test-craft
19
+ args:
20
+ - name: path
21
+ description: Project root path
22
+ required: false
23
+ - name: files
24
+ description: Optional test file/glob scope
25
+ required: false
26
+ - name: frameworks
27
+ description: Restrict to vitest/jest/mocha/playwright
28
+ required: false
29
+ - name: no-source-pair
30
+ description: Skip source-pairing resolution
31
+ required: false
32
+ mcp:
33
+ tool: test_craft
34
+ input:
35
+ path: string
36
+ type: rigid
37
+ tier: 2
38
+ phases:
39
+ - name: discover
40
+ description: Glob test files; detect framework per-file via import signatures
41
+ required: true
42
+ - name: extract
43
+ description: Walk TS AST for per-it/test block with nesting, skip/todo/only flags
44
+ required: true
45
+ - name: pair
46
+ description: Best-effort resolve source file under test (sibling / ../src / monorepo)
47
+ required: false
48
+ - name: critique
49
+ description: LLM-rubric loop per (test, rubric); 8 seed rubrics
50
+ required: true
51
+ - name: report
52
+ description: Aggregate findings + frameworks-detected counts + cost telemetry
53
+ required: true
54
+ state:
55
+ persistent: false
56
+ depends_on:
57
+ - harness-tdd
58
+ - harness-design-craft