@harness-engineering/cli 2.6.1 → 2.7.1
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 +244 -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 +244 -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 +244 -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 +244 -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-FKMKH6ZF.js} +4 -4
- package/dist/{analyzer-VY6DJVOU-4AHIJLNS.js → analyzer-H3AHBFSL-KPOPJYRB.js} +8 -8
- package/dist/{architecture-CXAVNB5X.js → architecture-LUR2I67H.js} +6 -6
- package/dist/{assess-project-M626SSPN.js → assess-project-OJWECOP2.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-HS5KYLRO.js} +5 -5
- package/dist/{chunk-5XLLIYYL.js → chunk-6UHDQP2N.js} +1 -1
- package/dist/{chunk-I6K43QQS.js → chunk-7AF6C5GE.js} +3 -3
- package/dist/{chunk-372MGNTI.js → chunk-7PLIWP7U.js} +6899 -133
- package/dist/{chunk-VUCGB2KH.js → chunk-B2VAYLYI.js} +1 -1
- package/dist/{chunk-HFOB2MPQ.js → chunk-CZPOPMMI.js} +3 -3
- package/dist/{chunk-43M3RLFQ.js → chunk-FC4JPM6W.js} +2 -2
- package/dist/{chunk-6AX6R3LM.js → chunk-FEKBXX2J.js} +9 -9
- package/dist/{chunk-VTTNIZJL.js → chunk-FXZI6NJ2.js} +131 -6
- package/dist/{chunk-5JNSFYZU.js → chunk-GGSNWH7P.js} +6 -6
- package/dist/{chunk-BCFDGOMA.js → chunk-H2ZJOJ7A.js} +81 -1
- package/dist/{chunk-PFZEADQM.js → chunk-INBTDBV2.js} +79 -2
- package/dist/{chunk-Q6ZR2L6Q.js → chunk-KN3EMDZ5.js} +2 -2
- package/dist/{chunk-JFZOWO7D.js → chunk-LECXPXS3.js} +1 -1
- package/dist/{chunk-CQOMVZDC.js → chunk-LGYXR2KZ.js} +2237 -604
- package/dist/{chunk-QOKMDDBJ.js → chunk-LYIPI2SJ.js} +6 -6
- package/dist/{chunk-L5BCZ5XS.js → chunk-N5NSSLMU.js} +1 -1
- package/dist/{chunk-GWE5LL3R.js → chunk-NIVWM7OW.js} +10 -10
- package/dist/{chunk-24KCOJJG.js → chunk-O2ASYKA6.js} +1 -1
- package/dist/{chunk-K246XQ2L.js → chunk-PTHX545Q.js} +1 -1
- package/dist/{chunk-EPUKTTJZ.js → chunk-RC3OI5NK.js} +5 -1
- package/dist/{chunk-YP7I25SL.js → chunk-RJ2WEES5.js} +1 -1
- package/dist/{chunk-5FDBXUFW.js → chunk-TIH53QXY.js} +1 -1
- package/dist/{chunk-OA6SWTU4.js → chunk-VI4IS55S.js} +11 -8
- package/dist/{chunk-GWF2OILZ.js → chunk-VSIRUZQP.js} +1 -1
- package/dist/{chunk-YYRQCQPZ.js → chunk-WZY5U6NS.js} +178 -109
- package/dist/{chunk-SZEFU6XC.js → chunk-Y4T6ENKQ.js} +9 -9
- package/dist/{chunk-HMW3VKUF.js → chunk-Z3TMCCZB.js} +10 -10
- package/dist/{ci-workflow-3JNDZQYD.js → ci-workflow-MDSTHJOY.js} +4 -4
- package/dist/{dist-OHDQBTSO.js → dist-72ID44UE.js} +3 -3
- package/dist/{dist-QPWXPCPL.js → dist-R3TOOPJ7.js} +1 -1
- package/dist/{docs-5RYMDHN3.js → docs-2RFOJ6XY.js} +6 -6
- package/dist/{engine-GKGT5ULT.js → engine-BSFKOGPN.js} +4 -4
- package/dist/{entropy-DQTAPSSE.js → entropy-3UX6644G.js} +5 -5
- package/dist/{feedback-WH6XPQJS.js → feedback-CVCFYND4.js} +2 -2
- package/dist/{generate-agent-definitions-SZA4QIHN.js → generate-agent-definitions-HZ3XENWO.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 +1012 -12
- package/dist/index.js +27 -27
- package/dist/{loader-BNKGM23C.js → loader-6RHWU5FH.js} +4 -4
- package/dist/{mcp-YKKPWUMH.js → mcp-OYR26JDI.js} +17 -17
- package/dist/{performance-54TMJOEU.js → performance-JJMIRXCH.js} +6 -6
- package/dist/{review-pipeline-OTDDVPNY.js → review-pipeline-BHQZIXWZ.js} +4 -4
- package/dist/{runtime-W5THSNAL.js → runtime-DALTAVTN.js} +4 -4
- package/dist/{scan-4PPP6ZXT.js → scan-LYKLM4EQ.js} +1 -1
- package/dist/{security-27HW7CYT.js → security-RMLMYFPB.js} +1 -1
- package/dist/{validate-QRVCD4GP.js → validate-R7DZRDFK.js} +5 -5
- package/dist/{validate-cross-check-BUAGBAPO.js → validate-cross-check-AUUZFNVL.js} +4 -4
- package/package.json +8 -7
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Security Craft
|
|
2
|
+
|
|
3
|
+
> LLM-judgment critique of security posture for TS/JS source — the ceiling counterpart to `harness-security-scan` (CVE/OWASP rule-based floor) and `harness-security-reviewer` (procedural review). Threat-modeling-as-skill rather than pattern-matching. Critiques whether trust boundaries are respected, where implicit privilege escalation lurks, whether the code defends in depth or just at the gate, whether principle of least authority is honored. Sixth non-design member of the craft-pipeline initiative (#10 of 10; the final sub-project). Emits 3-axis findings (tier × impact × confidence per ADR 0019).
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- During PR review on a substantively-changed handler / middleware / privileged op
|
|
8
|
+
- After authoring a new endpoint, before exposing it to traffic
|
|
9
|
+
- When onboarding a new contributor (audit security-relevant code they introduced)
|
|
10
|
+
- Periodically (per-sprint or per-release) to catch security-shape drift
|
|
11
|
+
- For threat-modeling shape questions a CVE scanner doesn't address (trust boundaries, fail-closed, authz ordering)
|
|
12
|
+
- NOT for CVE / dependency scanning (use `harness-security-scan` — rule-based floor)
|
|
13
|
+
- NOT for procedural review checklists (use `harness-security-reviewer`)
|
|
14
|
+
- NOT for IaC critique (v1.x — Dockerfile / k8s / Terraform have different rubric vocabulary)
|
|
15
|
+
- NOT for secret detection (floor concern; existing regex/entropy scanners cover this)
|
|
16
|
+
- NOT for autofix / security rewriting (this is judgment-only; v1.x may add `align-security` with aggressive safeguards)
|
|
17
|
+
- NOT for test files (v1 excludes — test security has a different shape; v1.x with dedicated rubrics)
|
|
18
|
+
|
|
19
|
+
## Process
|
|
20
|
+
|
|
21
|
+
### Phase 1: DISCOVER — Find source files
|
|
22
|
+
|
|
23
|
+
1. Walk `packages/*/src/` recursively.
|
|
24
|
+
2. Include `*.{ts,tsx,js,jsx,mjs,cjs}`; exclude test files (`*.test.*`, `*.spec.*`, `tests/`, `__tests__/`).
|
|
25
|
+
3. Exclude generated / build / coverage dirs (`node_modules`, `dist`, `build`, `coverage`, `.next`, `.turbo`, `__snapshots__`).
|
|
26
|
+
4. Honor `--packages` for explicit package scoping; `--files` overrides discovery.
|
|
27
|
+
|
|
28
|
+
### Phase 2: SIGNAL — AST-driven security construct detection
|
|
29
|
+
|
|
30
|
+
For each source file, the TS Compiler API walks the AST once and emits `SecuritySignal`s. Files with zero signals are **skipped entirely** — this is the FP-management strategy from the spec (no path-heuristic fallback).
|
|
31
|
+
|
|
32
|
+
Detected signal kinds:
|
|
33
|
+
|
|
34
|
+
| Kind | What it matches |
|
|
35
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| `http-handler` | `(req, res)` / `(req, res, next)` shapes; `app.get/post/...`; `@Get/@Post/...` |
|
|
37
|
+
| `middleware` | `(req, res, next) =>` arrow / `(ctx, next)` shapes |
|
|
38
|
+
| `auth-api` | `jwt.{sign,verify}`, `bcrypt.{hash,compare}`, `argon2.*`, `passport.*`, `req.session.*`, `res.cookie` |
|
|
39
|
+
| `privileged-op` | `child_process.{exec,spawn,...}`, `eval`, `new Function`, `vm.runIn*`, `fs.{writeFile,unlink,chmod,...}` |
|
|
40
|
+
| `data-egress` | `fetch`, `axios.*`, `http.request`, `https.request`, `net.connect` |
|
|
41
|
+
| `raw-query` | `*.query(\`...${x}...\`)`, `*.raw(...)`, `$queryRaw`, `$executeRaw` with SQL-shaped argument |
|
|
42
|
+
| `secret-handling` | Secret-named variable (`token`, `password`, `apiKey`, …) flowing into `console.*`, `logger.*`, `JSON.stringify`, template-literal sink |
|
|
43
|
+
|
|
44
|
+
AST awareness (not regex) avoids common false positives: `exec` in a comment, `eval` as a variable name, `token` in a CSS property name.
|
|
45
|
+
|
|
46
|
+
### Phase 3: CRITIQUE — Per (file, signal, rubric) loop
|
|
47
|
+
|
|
48
|
+
8 seed rubrics, each declaring `appliesToSignals` so per-signal pre-filtering minimizes LLM cost:
|
|
49
|
+
|
|
50
|
+
| Rubric | Title | Applies to signals |
|
|
51
|
+
| ---------- | ----------------------------------------------- | --------------------------------------------------- |
|
|
52
|
+
| `SEC-R001` | Trust boundary respected | http-handler, middleware, raw-query, privileged-op |
|
|
53
|
+
| `SEC-R002` | Principle of least authority honored | auth-api, privileged-op, http-handler |
|
|
54
|
+
| `SEC-R003` | Defense in depth (not gate-only) | auth-api, http-handler |
|
|
55
|
+
| `SEC-R004` | Assumed adversary realistic for the deployment | http-handler, middleware, auth-api |
|
|
56
|
+
| `SEC-R005` | Data flow across trust boundaries is visible | http-handler, raw-query, data-egress, privileged-op |
|
|
57
|
+
| `SEC-R006` | Fail closed, not open | auth-api, middleware, http-handler |
|
|
58
|
+
| `SEC-R007` | Secrets carried in a shape that resists leakage | secret-handling |
|
|
59
|
+
| `SEC-R008` | Authorization check happens before the action | http-handler, privileged-op |
|
|
60
|
+
|
|
61
|
+
For each (signal, rubric) pair where the rubric applies:
|
|
62
|
+
|
|
63
|
+
1. Build a prompt with rubric description + file path + signal info + **1500-char window AROUND the signal line** (not the whole file — security-critical context is local).
|
|
64
|
+
2. **Conservative-confidence system prompt** biases the LLM toward `medium` confidence by default; `high` requires a specific, named anti-pattern or visible missing guard.
|
|
65
|
+
3. LLM returns fenced JSON: `null` (rubric doesn't apply / code is fine) OR `{ tier, impact, confidence, message }`.
|
|
66
|
+
4. On non-null: emit a `SecurityFinding` with `cite.rubricId` populated for ADR 0020 traceability.
|
|
67
|
+
|
|
68
|
+
### Phase 4: REPORT — Aggregate + cost telemetry
|
|
69
|
+
|
|
70
|
+
Emit `SecurityCraftOutput`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
{
|
|
74
|
+
findings: SecurityFinding[];
|
|
75
|
+
summary: {
|
|
76
|
+
phaseRun: ['critique'];
|
|
77
|
+
mode: 'fast';
|
|
78
|
+
durationMs: number;
|
|
79
|
+
llmCalls: { provider, model, count, costUsd };
|
|
80
|
+
catalog: { rubricsApplied: string[] };
|
|
81
|
+
counts: { filesScanned, filesSkippedNoSignal, signalsDetected };
|
|
82
|
+
runId: string;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`filesSkippedNoSignal` is tracked separately so report consumers can see how aggressively the AST pre-filter trimmed the corpus.
|
|
88
|
+
|
|
89
|
+
## Harness Integration
|
|
90
|
+
|
|
91
|
+
- **`harness security-craft`** — CLI entry. `--files <glob>` / `--packages <names>` / `--max-files <n>` / `--max-signals-per-file <n>` / `--json` / `--verbose`.
|
|
92
|
+
- **`mcp__harness__security_craft`** — MCP tool. Same input/output. Consumed by agents.
|
|
93
|
+
- **Cross-cutting API:** `critiqueSecurityInFile(file, opts)` exported from `packages/cli/src/security-craft/index.ts`. Returns `[]` for files with no security signals (consistent with the orchestrator's FP-management strategy).
|
|
94
|
+
- **Shared craft infrastructure:** `LlmProvider`, `MockLlmProvider`, `derivePriority`, 3-axis types all live in `packages/cli/src/shared/craft/`.
|
|
95
|
+
|
|
96
|
+
## Success Criteria
|
|
97
|
+
|
|
98
|
+
See `docs/changes/craft-pipeline/security-craft/proposal.md` for the full 29 success criteria. Highlights:
|
|
99
|
+
|
|
100
|
+
- 8 seed rubrics ship at `catalog/rubrics/<id>.ts` (file-per-rubric)
|
|
101
|
+
- AST detector emits signals for all 7 signal kinds; comment / string contents don't fire (AST-aware, not regex)
|
|
102
|
+
- Files with zero signals are skipped (`filesSkippedNoSignal` tracked)
|
|
103
|
+
- Per-rubric `appliesToSignals` pre-filter avoids irrelevant LLM calls
|
|
104
|
+
- 3-axis output preserved; confidence defaults to medium per the spec's Decision #3
|
|
105
|
+
- `cite.rubricId` populated on every finding (ADR 0020)
|
|
106
|
+
- `critiqueSecurityInFile` cross-cutting API works on a single file
|
|
107
|
+
|
|
108
|
+
## Examples
|
|
109
|
+
|
|
110
|
+
### Example: User input flowing into child_process
|
|
111
|
+
|
|
112
|
+
**Input:** `packages/api/src/handlers/run-script.ts`:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { exec } from 'child_process';
|
|
116
|
+
import type { Request, Response } from 'express';
|
|
117
|
+
|
|
118
|
+
export function runScript(req: Request, res: Response): void {
|
|
119
|
+
const userScript = req.body.script;
|
|
120
|
+
exec(`bash -c "${userScript}"`, (err, stdout) => {
|
|
121
|
+
res.json({ output: stdout });
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Output (mock LLM):**
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
SEC-R001 [foundational/large/high] child_process.exec:5
|
|
130
|
+
User-controlled `req.body.script` flows directly into `bash -c "${userScript}"`.
|
|
131
|
+
This is a textbook command-injection sink. Either reject the entire pattern
|
|
132
|
+
(no user-supplied shell strings) or move to `execFile` with an allowlist of
|
|
133
|
+
binaries and pre-validated arg arrays. Never templated into a shell.
|
|
134
|
+
SEC-R005 [foundational/large/high] child_process.exec:5
|
|
135
|
+
Untrusted input (`req.body.script`) crosses the trust boundary into a
|
|
136
|
+
privileged sink without any visible validation or escaping step. The crossing
|
|
137
|
+
is invisible — the variable is named generically and goes straight to exec.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Example: Auth check after action
|
|
141
|
+
|
|
142
|
+
**Input:** `packages/api/src/handlers/get-doc.ts`:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
export async function getDoc(req: Request, res: Response) {
|
|
146
|
+
const doc = await db.docs.findOne({ id: req.params.id });
|
|
147
|
+
if (doc.ownerId !== req.user.id) return res.status(403).send();
|
|
148
|
+
return res.json(doc);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Output:**
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
SEC-R008 [foundational/medium/medium] req,res:1
|
|
156
|
+
The document is loaded BEFORE the authorization check. Even though the
|
|
157
|
+
response is denied, the load has already executed — observable side effects
|
|
158
|
+
(audit logs, rate-limit counters, cache populations) leak existence
|
|
159
|
+
information about documents the caller can't access. Authorize against the
|
|
160
|
+
identifier first (`req.params.id` + `req.user.id`), then load.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Example: File with no security signals
|
|
164
|
+
|
|
165
|
+
**Input:** A pure utility file with no http/auth/exec/fs/network constructs.
|
|
166
|
+
|
|
167
|
+
**Output:**
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
No security findings.
|
|
171
|
+
|
|
172
|
+
Summary: 0 findings across 0 files (12 skipped, 0 signals, 8 rubrics, 0 LLM calls, $0.0000, 4ms)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The 12 files were scanned for signals but skipped because none had security-relevant AST constructs — exactly the FP-management strategy at work.
|
|
176
|
+
|
|
177
|
+
## Gates
|
|
178
|
+
|
|
179
|
+
- **No autofix.** Sibling `align-security` deferred to v2 with aggressive FP safeguards (security rewrites have asymmetric downside).
|
|
180
|
+
- **No IaC critique.** Dockerfile / k8s / Terraform need different rubrics; v1.x.
|
|
181
|
+
- **No multi-file auth-flow tracing.** Cross-file privilege-escalation analysis (handler → middleware → service) needs a graph traversal layer; v1.x once cross-file critique pays for itself elsewhere.
|
|
182
|
+
- **No dependency / CVE scanning.** `harness-security-scan` is the floor.
|
|
183
|
+
- **No secret detection** (floor concern).
|
|
184
|
+
- **No test-file critique.** Test security has a different shape; v1.x.
|
|
185
|
+
- **No path-heuristic fallback.** If AST scan finds zero signals, the file is skipped. Tight scoping is part of the FP-management strategy.
|
|
186
|
+
- **No B' bootstrap.**
|
|
187
|
+
|
|
188
|
+
## Escalation
|
|
189
|
+
|
|
190
|
+
- **When LLM cost is too high:** drop `maxFiles` to 50 or `maxSignalsPerFile` to 5, or scope explicitly with `--packages <name>`. Per-file cost = (signals × applicable rubrics × per-call); typical handler fires ~3-5 rubrics, not 8.
|
|
191
|
+
- **When a specific rubric produces false positives:** v1 has no per-rubric disable; v1.x adds `craft.security.disabledRubrics: ['SEC-R004']`. Until then: filter findings by `cite.rubricId` downstream.
|
|
192
|
+
- **When the AST detector misses a framework you use** (tRPC, Convex, Cloudflare Workers, Hono RPC): v1 ships baseline coverage for Express / Hono / Fastify / Koa / NestJS-decorator. Adding a framework is a 1-line config in `signals.ts`. Track as v1.x.
|
|
193
|
+
- **When findings are too cautious (confidence floor):** the conservative-by-default is deliberate (FP management); v1.x adds `craft.security.confidenceFloor` to tighten further. Loosening below medium is intentionally not exposed.
|
|
194
|
+
- **When you want IaC critique:** v1.x. For v1, scope explicitly to source files and accept the gap.
|
|
195
|
+
- **When a finding is wrong:** dismiss it in your consumer; signal as a `suppressedAt` entry on the rubric for future catalog evolution.
|
|
196
|
+
|
|
197
|
+
## Status
|
|
198
|
+
|
|
199
|
+
**v1 — in implementation.** See:
|
|
200
|
+
|
|
201
|
+
- Spec: `docs/changes/craft-pipeline/security-craft/proposal.md`
|
|
202
|
+
- Roadmap entry: `craft-pipeline sub-project #10` (the final sub-project; the craft-pipeline initiative completes with this PR)
|
|
203
|
+
- Sibling craft skills: `naming-craft` (#1), `spec-craft` (#6), `copy-craft` (#5), `test-craft` (#3), `knowledge-craft` (#9), `harness-design-craft` (design-pipeline #6)
|
|
204
|
+
- Shared infrastructure: `packages/cli/src/shared/craft/`
|
|
205
|
+
- Future: `align-security` (FIX side; aggressive safeguards), IaC critique, multi-file auth-flow tracing, test-file security, framework expansions (tRPC / Convex / Cloudflare Workers / Hono RPC).
|
|
@@ -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
|