@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.
- 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-ORNMAYHB.js → chunk-4EGPFB7V.js} +6196 -125
- 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-NX6A24DS.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-HYTRDAHV.js → chunk-WOXR6I3R.js} +3 -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-KZ3PPUYE.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-GZH4EYGD.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,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
|