@hegemonart/get-design-done 1.21.0 → 1.23.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +184 -0
- package/hooks/_hook-emit.js +81 -0
- package/hooks/gdd-bash-guard.js +8 -0
- package/hooks/gdd-decision-injector.js +2 -0
- package/hooks/gdd-protected-paths.js +8 -0
- package/hooks/gdd-trajectory-capture.js +64 -0
- package/hooks/hooks.json +9 -0
- package/package.json +7 -2
- package/reference/output-contracts/planner-decision.schema.json +94 -0
- package/reference/output-contracts/verifier-decision.schema.json +66 -0
- package/scripts/cli/gdd-events.mjs +283 -0
- package/scripts/lib/audit-aggregator/index.cjs +219 -0
- package/scripts/lib/connection-probe/index.cjs +263 -0
- package/scripts/lib/design-solidify.mjs +265 -0
- package/scripts/lib/design-tokens/_js-harness.cjs +66 -0
- package/scripts/lib/design-tokens/css-vars.cjs +55 -0
- package/scripts/lib/design-tokens/figma.cjs +121 -0
- package/scripts/lib/design-tokens/index.cjs +100 -0
- package/scripts/lib/design-tokens/js-const.cjs +107 -0
- package/scripts/lib/design-tokens/tailwind.cjs +98 -0
- package/scripts/lib/domain-primitives/anti-patterns.cjs +66 -0
- package/scripts/lib/domain-primitives/nng.cjs +136 -0
- package/scripts/lib/domain-primitives/wcag.cjs +166 -0
- package/scripts/lib/event-chain.cjs +177 -0
- package/scripts/lib/event-stream/index.ts +20 -0
- package/scripts/lib/event-stream/reader.ts +139 -0
- package/scripts/lib/event-stream/types.ts +155 -1
- package/scripts/lib/event-stream/writer.ts +65 -8
- package/scripts/lib/parse-contract.cjs +168 -0
- package/scripts/lib/redact.cjs +122 -0
- package/scripts/lib/reference-resolver.cjs +184 -0
- package/scripts/lib/touches-analyzer/index.cjs +201 -0
- package/scripts/lib/touches-pattern-miner.cjs +195 -0
- package/scripts/lib/trajectory/index.cjs +126 -0
- package/scripts/lib/transports/ws.cjs +179 -0
- package/scripts/lib/visual-baseline/diff.cjs +137 -0
- package/scripts/lib/visual-baseline/index.cjs +139 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "Get Design Done — 5-stage agent-orchestrated design pipeline with 9 connections, handoff-first workflow, bidirectional Figma write-back, 22+ specialized agents, queryable knowledge layer (intel store, dependency analysis, learnings extraction), and a self-improvement loop (reflector, frontmatter + budget feedback, global-skills layer). v1.20.0 ships the SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream, and resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) for rate-limit + 429 + context-overflow recovery. Full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation (auto-tag + GitHub Release + release-time smoke test).",
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.23.0"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "get-design-done",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 22+ specialized agents, 9 connections (Figma, Refero, Preview, Storybook, Chromatic, Figma Writer, Graphify, Pinterest, Claude Design), Claude Design handoff, bidirectional Figma write-back, and a queryable intel store (.design/intel/) for dependency and learnings queries. Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation. Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain.",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.23.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "hegemonart"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "get-design-done",
|
|
3
3
|
"short_name": "gdd",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.23.0",
|
|
5
5
|
"description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 22+ specialized agents, 9 connections (Figma, Refero, Preview, Storybook, Chromatic, Figma Writer, Graphify, Pinterest, Claude Design), handoff-first workflow via Claude Design bundles, bidirectional Figma write-back (annotations, Code Connect), queryable intel store (`.design/intel/`) for O(1) design surface lookups, and self-improvement loop (reflector agent, frontmatter + budget feedback, global-skills layer at `~/.claude/gdd/global-skills/`). Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings, reflect, apply-reflections. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows, lint + schema + frontmatter + stale-ref + shellcheck + gitleaks + injection-scan + blocking size-budget) and release automation (auto-tag + GitHub Release + release-time smoke test). Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "hegemonart",
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,190 @@ All notable changes to get-design-done are documented here. Versions follow [sem
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [1.23.0] — 2026-04-25
|
|
8
|
+
|
|
9
|
+
Phase 23 GDD SDK Domain Primitives milestone — lands the highest-leverage code primitives from the ROADMAP "GDD SDK Domain Primitives" entry as typed Node modules with tests. 10 atomic plans (23-01 through 23-10), additive — every Phase 20/21/22 consumer keeps working unchanged. Distribution as separate `@hegemonart/gdd-sdk` npm package and screenshot-capture orchestration are explicitly deferred to follow-up phases.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **JSON output contracts** — `reference/output-contracts/planner-decision.schema.json` + `verifier-decision.schema.json` (Draft-07). `scripts/lib/parse-contract.cjs` gains `parsePlannerDecision()` and `parseVerifierDecision()` riding the same extract→parse→validate pipeline as the existing `parseMotionMap`. Lets `/gdd:synthesize` consume planner output without regex-parsing markdown headings, and lets executor↔verifier ping-pong on a typed envelope. (Plan 23-01)
|
|
14
|
+
|
|
15
|
+
- **Solidify-with-rollback gate** — `scripts/lib/design-solidify.mjs` runs the typecheck/build/targeted-test triplet for a task and, on any failure, rolls the working tree back via `git stash` (configurable: `stash` | `hard` | `none`) and emits a `solidify.rollback` event onto the Phase 22 causal chain. Optional `emit()` callback for event-stream telemetry sink. Authored as `.mjs` to sidestep the Phase 22 Node 24 + Windows + .mjs↔.ts loader bug. (Plan 23-02)
|
|
16
|
+
|
|
17
|
+
- **Touches: analyzer + parallelism decision engine** — `scripts/lib/touches-analyzer/index.cjs` parses `Touches:` lines from task markdown into glob lists and produces a pairwise verdict (`parallel` | `sequential`) for any two tasks. Encodes today's prompt-only heuristic into auditable code. Verdict rules (first match wins): empty → unknown-touches; literal equality → shared-glob; shared component dir → shared-component-dir; resolved-file overlap → shared-file; otherwise → disjoint. (Plan 23-03)
|
|
18
|
+
|
|
19
|
+
- **Audit aggregator** — `scripts/lib/audit-aggregator/index.cjs` takes `Array<Finding>` from N audit-agents, dedups by `{file, line, rule_id}`, scores via severity-weighted formula (P0:8/P1:4/P2:2/P3:1), and returns sorted top-N + tally summary. Default merge picks higher-confidence → higher-severity → lex-earliest agent → first-seen. Confidence outside `[0, 1]` clamped with one `process.emitWarning` per call. Cross-platform path normalization. (Plan 23-04)
|
|
20
|
+
|
|
21
|
+
- **Reference resolver** — `scripts/lib/reference-resolver.cjs` adds the resolution direction on top of the Phase 14.5 reference-registry. `resolve('forms')` / `resolve('type:forms')` → `{name, path, type, excerpt}` with a 200-char excerpt suitable for inlining into agent prompts. Lookup order: exact name → slug match → singularize fuzzy → type-only-when-unique. Ambiguous match throws `RangeError` with candidates. `resolveAll(keys, {ignoreMissing})` for bulk. `excerptOf(path, {maxChars})` strips frontmatter / fenced code / HTML comments / markdown headers. (Plan 23-05)
|
|
22
|
+
|
|
23
|
+
- **Touches pattern miner** — `scripts/lib/touches-pattern-miner.cjs` scans `.design/archive/cycle-*/tasks/*.md` after `/gdd:complete-cycle`, canonicalizes signatures (lowercase + backslash-normalize + cycle-slug strip + dedup + sort), and proposes crystallization candidates when a signature recurs in ≥3 tasks across ≥2 cycles. Writes `.design/learnings/touches-patterns.json` atomically (`.tmp` + rename). **NEVER auto-applies** — `/gdd:apply-reflections` is the materialization gate. (Plan 23-06)
|
|
24
|
+
|
|
25
|
+
- **Image diff + visual baseline manager** — `scripts/lib/visual-baseline/diff.cjs` compares two PNG buffers. With `pngjs` installed (probeOptional), decodes both and counts pixels whose R/G/B/A channels differ beyond the tolerance (default 4). Without `pngjs`, falls back to bytewise SHA-256 equality. `scripts/lib/visual-baseline/index.cjs` exposes `compareToBaseline(key, pngBuffer)` and `applyBaseline(key, pngBuffer)`; reads/writes `.design/baselines/<key>.png`; rejects path-traversal keys. `pngjs@7` declared as optional dep. Defers Playwright/Preview MCP screenshot capture orchestration to a later phase. (Plan 23-07)
|
|
26
|
+
|
|
27
|
+
- **Design-token reader (multi-source)** — `scripts/lib/design-tokens/index.cjs` facades over four pure-JS readers producing the uniform `{tokens, source, format, warnings}` shape:
|
|
28
|
+
- `css-vars.cjs` — extracts `--token: value;` from CSS/SCSS, last-write-wins, strips block comments, warns on `$scss-vars`
|
|
29
|
+
- `js-const.cjs` — spawn-node harness evaluates CJS/ESM exports, recognises `{tokens: …}` / default / direct bag, flattens nested with `.` separator
|
|
30
|
+
- `tailwind.cjs` — same harness, walks `theme` + `theme.extend` per scale (extend overrides base)
|
|
31
|
+
- `figma.cjs` — parses `{variableCollections}` shape OR already-flattened bag; emits `rgb(R, G, B)` for color values, per-mode tokens for multi-mode variables
|
|
32
|
+
Auto-detection by extension + content sniff. (Plan 23-08)
|
|
33
|
+
|
|
34
|
+
- **Domain primitives bundle** — three checkers sharing a single hit shape (`{rule_id, severity P0-P3, summary, evidence?, line?, file}`):
|
|
35
|
+
- `domain-primitives/nng.cjs` — runs grep-style heuristic rules loaded from `reference/heuristics.md` fenced yaml blocks; caller may inject `opts.rules` to bypass file-load
|
|
36
|
+
- `domain-primitives/anti-patterns.cjs` — same yaml extractor against `reference/anti-patterns.md`
|
|
37
|
+
- `domain-primitives/wcag.cjs` (no axe-core dep) — `contrastRatio()` (WCAG 1.4.3 luminance), `checkContrast({fg, bg, level: AA|AAA})`, `checkTapTarget({width, height, level})` (AA 24×24, AAA 44×44), `checkAriaLabels({content})` (interactive elements without text + aria-label)
|
|
38
|
+
Both NNG + anti-pattern files allow no parseable yaml today (treated as empty registry); robust to gradual rule population. (Plan 23-09)
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- `tests/semver-compare.test.cjs` `OFF_CADENCE_VERSIONS` gains `1.23.0`.
|
|
43
|
+
- `test-fixture/baselines/phase-20/resilience-primitives.txt` gains `reference-resolver.cjs` (alphabetical between `reference-registry.cjs` and `relevance-counter.cjs`) and `touches-pattern-miner.cjs` (alphabetical at end).
|
|
44
|
+
|
|
45
|
+
### Tests
|
|
46
|
+
|
|
47
|
+
- `tests/output-contracts-23-01.test.cjs` — planner + verifier contracts (14 tests)
|
|
48
|
+
- `tests/design-solidify.test.cjs` — solidify gate, all rollback modes (6)
|
|
49
|
+
- `tests/touches-analyzer.test.cjs` — parser + verdict + matrix (17)
|
|
50
|
+
- `tests/audit-aggregator.test.cjs` — dedup + score + tallies (15)
|
|
51
|
+
- `tests/reference-resolver.test.cjs` — resolution rules + excerpts (12)
|
|
52
|
+
- `tests/touches-pattern-miner.test.cjs` — canonicalize + thresholds + atomic write (10)
|
|
53
|
+
- `tests/visual-baseline.test.cjs` — diff modes + baseline round-trip (14, pngjs-optional path skipped when absent)
|
|
54
|
+
- `tests/design-tokens.test.cjs` — 4 readers + facade auto-detect (15)
|
|
55
|
+
- `tests/domain-primitives.test.cjs` — NNG + anti-pattern + WCAG checkers (18)
|
|
56
|
+
- `tests/phase-23-baseline.test.cjs` — Phase 23 regression baseline (12)
|
|
57
|
+
|
|
58
|
+
Total: 133 new tests. All Phase 20/21/22 tests still green.
|
|
59
|
+
|
|
60
|
+
### Deferred
|
|
61
|
+
|
|
62
|
+
- **`@hegemonart/gdd-sdk` separate npm package** — out of scope; build/packaging project of its own.
|
|
63
|
+
- **Screenshot capture orchestration** (Playwright + Claude Preview MCP wrapper) — needs live MCP infra to validate.
|
|
64
|
+
- **Spec↔code↔visual triangulation verifier** — depends on Phase 16/17 component specs being fleshed out.
|
|
65
|
+
- **Knowledge-graph typed query layer** + cycle/workstream model SDK + pause/resume context serializer SDK + per-stage budget allocator SDK + git operations primitive + handoff bundle parser + intel store typed reader/writer — each is roadmap-text-sized and warrants its own phase scope.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## [1.22.0] — 2026-04-25
|
|
70
|
+
|
|
71
|
+
Phase 22 GDD SDK Observability milestone — the single-typed `BaseEvent` envelope from Phase 20 grows into a queryable, redacted, transport-able observability layer with tail/grep/WebSocket consumers and a causal event chain. 10 plans (22-01 through 22-10), additive — every Phase 20 + Phase 21 consumer keeps working unchanged.
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- **Expanded event-type registry** — 17 new pre-registered subtypes alongside
|
|
76
|
+
the 6 from Phase 20: `wave.started`, `wave.completed`, `blocker.added`,
|
|
77
|
+
`decision.added`, `must_have.added`, `parallelism.verdict`, `cost.update`,
|
|
78
|
+
`rate_limit`, `api.retry`, `compact.boundary`, `mcp.probe`,
|
|
79
|
+
`reflection.proposed`, `connection.status_change`, `tool_call.started`,
|
|
80
|
+
`tool_call.completed`, `agent.spawn`, `agent.outcome`. The envelope
|
|
81
|
+
remains open (`type: string`); the `KnownEvent` union is an additive
|
|
82
|
+
overlay for typed `switch` consumers. Runtime `KNOWN_EVENT_TYPES` list
|
|
83
|
+
exported for the CLI's `list-types` subcommand. (Plan 22-01)
|
|
84
|
+
|
|
85
|
+
- **Secret scrubber** — `scripts/lib/redact.cjs` deep-walks every event
|
|
86
|
+
payload and replaces secret-shaped strings with `[REDACTED:<type>]`
|
|
87
|
+
placeholders. Patterns: `sk-ant-…` (anthropic), `sk_live_…` (stripe),
|
|
88
|
+
`xox[baprs]-…` (slack), `ghp_…` (github_pat), `AKIA…` (aws), generic
|
|
89
|
+
`sk-…`, `eyJ…` JWTs, and full `-----BEGIN/END-----` PEM blocks.
|
|
90
|
+
Wired into `EventWriter.serialize()` so every event hitting disk and
|
|
91
|
+
every bus subscriber sees the scrubbed form — single rule everywhere,
|
|
92
|
+
no escape hatch. (Plan 22-02)
|
|
93
|
+
|
|
94
|
+
- **Per-tool-call trajectory stream** — `.design/telemetry/trajectories/<cycle>.jsonl`
|
|
95
|
+
receives one line per agent tool-use:
|
|
96
|
+
`{ts, agent, tool, args_hash, result_hash, latency_ms, status}`.
|
|
97
|
+
Args + result are sha256-prefix-hashed (16 chars) — line size stays
|
|
98
|
+
bounded; prompt content stays de-identified. Captured by the new
|
|
99
|
+
PostToolUse:Agent hook `hooks/gdd-trajectory-capture.js`. (Plan 22-03)
|
|
100
|
+
|
|
101
|
+
- **Append-only event chain** — `.design/gep/events.jsonl` (gep =
|
|
102
|
+
"GDD Event Provenance"). One line per causal hop: `{event_id,
|
|
103
|
+
parent_event_id, ts, agent, decision_refs, outcome, rollback_reason?}`.
|
|
104
|
+
`appendChainEvent()` auto-generates UUIDs; `walkParents()` traces a
|
|
105
|
+
row up to its root for `/gdd:audit --retroactive`. Cycle-safe; skips
|
|
106
|
+
invalid lines; lives separately from the firehose so audits don't
|
|
107
|
+
scan unrelated rows. (Plan 22-04)
|
|
108
|
+
|
|
109
|
+
- **Typed event reader + aggregator** — `readEvents({path, type,
|
|
110
|
+
predicate, since, until})` returns an async streaming iterator over
|
|
111
|
+
`events.jsonl` (readline + createReadStream — no full-file load,
|
|
112
|
+
GB-scale logs OK). `aggregate()` rolls events up by type, stage,
|
|
113
|
+
cycle, agent + totals (count, error_count, truncated_count). Exposed
|
|
114
|
+
from the public event-stream API. (Plan 22-05)
|
|
115
|
+
|
|
116
|
+
- **CLI transport** — `gdd-events` bin entry, subcommands:
|
|
117
|
+
`tail [--follow]` (250ms-poll follow mode, no native inotify dep);
|
|
118
|
+
`grep <terms…>` with hand-rolled filter language
|
|
119
|
+
(`type=foo`, `payload.x.y=bar`, `!type=baz`, multiple = AND);
|
|
120
|
+
`cat` pretty-print with timestamp+type prefix; `list-types` runtime
|
|
121
|
+
registry dump; `serve` for the WebSocket transport. (Plan 22-06)
|
|
122
|
+
|
|
123
|
+
- **WebSocket transport** — `scripts/lib/transports/ws.cjs` exposes
|
|
124
|
+
`startServer({port, token, tailFrom?, subscribe?})`. Loaded via
|
|
125
|
+
`probeOptional('ws')` — clear install hint when absent. Auth:
|
|
126
|
+
`Authorization: Bearer <token>` on the upgrade; mismatched →
|
|
127
|
+
HTTP 401 close. Tail replay sends `tailFrom`'s contents to new
|
|
128
|
+
clients before subscribing them to the live bus. Fire-and-forget
|
|
129
|
+
delivery; closed sockets dropped silently. `ws@8` declared as
|
|
130
|
+
optional dep. (Plan 22-07)
|
|
131
|
+
|
|
132
|
+
- **Connection probe primitive** — `scripts/lib/connection-probe/`
|
|
133
|
+
exposes `probe({name, cmd, timeout, retries, fallback})` →
|
|
134
|
+
`{status: 'ok'|'degraded'|'down', latency_ms, attempts,
|
|
135
|
+
fallback_used, error?}`. Backoff between retries via
|
|
136
|
+
`jittered-backoff.cjs`. State persisted at
|
|
137
|
+
`.design/telemetry/connection-state.json` (atomic .tmp+rename).
|
|
138
|
+
Status transitions emit `connection.status_change` events through a
|
|
139
|
+
caller-supplied `emit` callback — transitions only, not every probe.
|
|
140
|
+
Replaces ad-hoc bash probe snippets in `connections/`. (Plan 22-08)
|
|
141
|
+
|
|
142
|
+
- **Hook → event-stream wire-in** — new `hooks/_hook-emit.js` shared
|
|
143
|
+
helper wraps `appendEvent()` in try/catch (hooks must NEVER throw on
|
|
144
|
+
telemetry failure). Wired hooks emit `hook.fired` events on every
|
|
145
|
+
decision: `gdd-bash-guard` (allow/block + severity + pattern),
|
|
146
|
+
`gdd-protected-paths` (allow/block + matched glob),
|
|
147
|
+
`gdd-decision-injector` (inject/no-hits + backend label).
|
|
148
|
+
Trajectory-capture (Plan 22-03) already emits via its own path.
|
|
149
|
+
Additive — zero behavior change to existing hook contracts. (Plan 22-09)
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
|
|
153
|
+
- `EventWriter.serialize()` now runs `redact()` on the event before
|
|
154
|
+
JSON-stringifying. Persisted form is the canonical scrubbed form.
|
|
155
|
+
All 27 Phase 20 event-stream tests still pass. (Plan 22-02 wire-in)
|
|
156
|
+
|
|
157
|
+
- `hooks.json` registers a new `PostToolUse:Agent` matcher pointing at
|
|
158
|
+
`gdd-trajectory-capture.js`. The existing `PreToolUse:Agent`
|
|
159
|
+
budget-enforcer is unaffected. (Plan 22-03 registration)
|
|
160
|
+
|
|
161
|
+
### Tests
|
|
162
|
+
|
|
163
|
+
- `tests/event-types-registry.test.ts` — registry expansion (4 tests)
|
|
164
|
+
- `tests/redact.test.cjs` — secret scrubber (12 tests)
|
|
165
|
+
- `tests/event-stream-redact-integration.test.ts` — write-time wire-in (2 tests)
|
|
166
|
+
- `tests/trajectory-capture.test.cjs` — module + hook subprocess (7 tests)
|
|
167
|
+
- `tests/event-chain.test.cjs` — chain + walk + cycle defense (10 tests)
|
|
168
|
+
- `tests/event-reader.test.ts` — reader + aggregator (8 tests)
|
|
169
|
+
- `tests/cli-events.test.cjs` — CLI subcommands + filter language (10 tests)
|
|
170
|
+
- `tests/ws-transport.test.cjs` — WebSocket auth + replay + live (5 + 1 skip)
|
|
171
|
+
- `tests/connection-probe-primitive.test.cjs` — retry + fallback + emit (9 tests)
|
|
172
|
+
- `tests/hook-emit-wire.test.cjs` — bash-guard + protected-paths emission (4 tests)
|
|
173
|
+
- `tests/phase-22-baseline.test.cjs` — Phase 22 regression baseline (12 tests)
|
|
174
|
+
|
|
175
|
+
Total: 84 new tests. All Phase 20/21 tests still green.
|
|
176
|
+
|
|
177
|
+
### Deferred
|
|
178
|
+
|
|
179
|
+
- Grafana / Prometheus exporter (out of scope; code-primitive
|
|
180
|
+
observability shipped first).
|
|
181
|
+
- Event-stream compaction / retention (`events.jsonl` grows unbounded;
|
|
182
|
+
Phase 22.x if needed).
|
|
183
|
+
- Replay-on-subscribe semantics (bus stays live-only; transports that
|
|
184
|
+
want replay read `events.jsonl` directly, then subscribe live).
|
|
185
|
+
- Wire-in for `gdd-mcp-circuit-breaker.js`, `budget-enforcer.ts`,
|
|
186
|
+
`context-exhaustion.ts`, `gdd-read-injection-scanner.ts` — flow is
|
|
187
|
+
more intricate; follow-up phase.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
7
191
|
## [1.21.0] — 2026-04-24
|
|
8
192
|
|
|
9
193
|
Phase 21 GDD SDK Headless milestone — the plugin now runs unchanged on Claude Code, OpenAI Codex CLI, and Gemini CLI, and ships a full `gdd-sdk` CLI that executes the design pipeline without a harness. 12 plans (21-01 through 21-12), 50+ commits, 936 tests.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hooks/_hook-emit.js — shared `hook.fired` emitter for Phase 22 wire-in
|
|
3
|
+
* (Plan 22-09).
|
|
4
|
+
*
|
|
5
|
+
* Hooks must NEVER throw on telemetry failure — a broken event stream
|
|
6
|
+
* cannot block a tool call. This helper wraps appendEvent in try/catch
|
|
7
|
+
* and silently swallows.
|
|
8
|
+
*
|
|
9
|
+
* Why a wrapper instead of importing directly:
|
|
10
|
+
* * Centralizes the try/catch so each hook stays terse.
|
|
11
|
+
* * Loads the .ts event-stream lazily — hooks invoked via plain `node`
|
|
12
|
+
* (no --experimental-strip-types) just no-op on telemetry instead
|
|
13
|
+
* of crashing. The hooks.json registrations vary on whether they
|
|
14
|
+
* pass --experimental-strip-types, and we don't want to forbid
|
|
15
|
+
* plain-node invocation paths.
|
|
16
|
+
* * Single place to add structured event sinks later (e.g. mirror to
|
|
17
|
+
* CLI transport) without touching every hook file.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* const { emitHookFired } = require('./_hook-emit.js');
|
|
21
|
+
* // …decision computed…
|
|
22
|
+
* emitHookFired('budget-enforcer', 'allow');
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
let cachedAppendEvent = null;
|
|
28
|
+
let resolutionAttempted = false;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Lazy-resolve `appendEvent` — only loads the event-stream module the
|
|
32
|
+
* first time a hook fires. Falls back to a no-op if the module is not
|
|
33
|
+
* loadable in the current runtime (e.g. plain `node` without
|
|
34
|
+
* --experimental-strip-types).
|
|
35
|
+
*
|
|
36
|
+
* @returns {(ev: unknown) => void}
|
|
37
|
+
*/
|
|
38
|
+
function getAppendEvent() {
|
|
39
|
+
if (cachedAppendEvent !== null || resolutionAttempted) {
|
|
40
|
+
return cachedAppendEvent || (() => {});
|
|
41
|
+
}
|
|
42
|
+
resolutionAttempted = true;
|
|
43
|
+
try {
|
|
44
|
+
// event-stream/index.ts requires --experimental-strip-types. Try
|
|
45
|
+
// require()'ing — if Node refuses to parse `.ts`, we silently fall
|
|
46
|
+
// back to no-op.
|
|
47
|
+
// eslint-disable-next-line node/no-missing-require, global-require
|
|
48
|
+
cachedAppendEvent = require('../scripts/lib/event-stream/index.ts').appendEvent;
|
|
49
|
+
return cachedAppendEvent;
|
|
50
|
+
} catch {
|
|
51
|
+
cachedAppendEvent = null;
|
|
52
|
+
return () => {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Emit a `hook.fired` event. Silent on every failure mode.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} hookName
|
|
60
|
+
* @param {string} decision
|
|
61
|
+
* @param {Record<string, unknown>} [extras] — opaque additional payload fields
|
|
62
|
+
*/
|
|
63
|
+
function emitHookFired(hookName, decision, extras) {
|
|
64
|
+
try {
|
|
65
|
+
const appendEvent = getAppendEvent();
|
|
66
|
+
const payload = { hook: hookName, decision };
|
|
67
|
+
if (extras && typeof extras === 'object') {
|
|
68
|
+
Object.assign(payload, extras);
|
|
69
|
+
}
|
|
70
|
+
appendEvent({
|
|
71
|
+
type: 'hook.fired',
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
sessionId: process.env.GDD_SESSION_ID || 'hook',
|
|
74
|
+
payload,
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
/* hooks must never throw on telemetry */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { emitHookFired };
|
package/hooks/gdd-bash-guard.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const { match } = require(path.join(__dirname, '..', 'scripts', 'lib', 'dangerous-patterns.cjs'));
|
|
18
|
+
const { emitHookFired } = require('./_hook-emit.js'); // Plan 22-09 wire-in
|
|
18
19
|
|
|
19
20
|
async function main() {
|
|
20
21
|
let buf = '';
|
|
@@ -22,11 +23,13 @@ async function main() {
|
|
|
22
23
|
|
|
23
24
|
let payload;
|
|
24
25
|
try { payload = JSON.parse(buf || '{}'); } catch {
|
|
26
|
+
emitHookFired('gdd-bash-guard', 'allow', { reason: 'parse-error' });
|
|
25
27
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
if (payload?.tool_name && payload.tool_name !== 'Bash') {
|
|
32
|
+
emitHookFired('gdd-bash-guard', 'allow', { reason: 'non-bash-tool' });
|
|
30
33
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
31
34
|
return;
|
|
32
35
|
}
|
|
@@ -34,6 +37,10 @@ async function main() {
|
|
|
34
37
|
const command = payload?.tool_input?.command ?? '';
|
|
35
38
|
const r = match(command);
|
|
36
39
|
if (r.matched) {
|
|
40
|
+
emitHookFired('gdd-bash-guard', 'block', {
|
|
41
|
+
severity: r.severity,
|
|
42
|
+
pattern: r.pattern,
|
|
43
|
+
});
|
|
37
44
|
process.stdout.write(JSON.stringify({
|
|
38
45
|
continue: false,
|
|
39
46
|
stopReason: `gdd-bash-guard: dangerous command blocked (${r.severity}): ${r.description} [${r.pattern}]`,
|
|
@@ -41,6 +48,7 @@ async function main() {
|
|
|
41
48
|
return;
|
|
42
49
|
}
|
|
43
50
|
|
|
51
|
+
emitHookFired('gdd-bash-guard', 'allow', { reason: 'no-match' });
|
|
44
52
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -203,10 +203,12 @@ async function main() {
|
|
|
203
203
|
const backendLabel = BACKEND || (useRgGlobal ? 'ripgrep' : 'node-grep');
|
|
204
204
|
const block = buildRecallBlock(hits, basename, backendLabel);
|
|
205
205
|
if (!block) {
|
|
206
|
+
try { require('./_hook-emit.js').emitHookFired('gdd-decision-injector', 'no-hits', { backend: backendLabel }); } catch { /* swallow */ }
|
|
206
207
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
207
208
|
return;
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
try { require('./_hook-emit.js').emitHookFired('gdd-decision-injector', 'inject', { backend: backendLabel, hit_count: hits.length }); } catch { /* swallow */ }
|
|
210
212
|
process.stdout.write(JSON.stringify({
|
|
211
213
|
continue: true,
|
|
212
214
|
hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: block },
|
|
@@ -98,6 +98,11 @@ async function main() {
|
|
|
98
98
|
: cand.replace(/\\/g, '/');
|
|
99
99
|
const r = matches(rel, protectedPaths);
|
|
100
100
|
if (r.matched) {
|
|
101
|
+
try {
|
|
102
|
+
require('./_hook-emit.js').emitHookFired('gdd-protected-paths', 'block', {
|
|
103
|
+
path: rel, pattern: r.pattern,
|
|
104
|
+
});
|
|
105
|
+
} catch { /* swallow */ }
|
|
101
106
|
process.stdout.write(JSON.stringify({
|
|
102
107
|
continue: false,
|
|
103
108
|
stopReason: `gdd-protected-paths: '${rel}' is a protected path (matched '${r.pattern}'). To override, lift the path from the default glob list or explicitly edit via an approved workflow (e.g., /gdd:update, plan execution).`,
|
|
@@ -106,6 +111,9 @@ async function main() {
|
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
try {
|
|
115
|
+
require('./_hook-emit.js').emitHookFired('gdd-protected-paths', 'allow');
|
|
116
|
+
} catch { /* swallow */ }
|
|
109
117
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
110
118
|
}
|
|
111
119
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* gdd-trajectory-capture.js — PostToolUse:Agent hook (Plan 22-03).
|
|
4
|
+
*
|
|
5
|
+
* Reads the standard Claude Code hook JSON from stdin:
|
|
6
|
+
* { tool_name, tool_input, tool_response, session_id, ... }
|
|
7
|
+
*
|
|
8
|
+
* Writes one JSONL line to `.design/telemetry/trajectories/<cycle>.jsonl`
|
|
9
|
+
* via `scripts/lib/trajectory/index.cjs`. Silent-on-failure: telemetry
|
|
10
|
+
* never blocks the parent agent's tool call.
|
|
11
|
+
*
|
|
12
|
+
* Cycle resolution:
|
|
13
|
+
* * env var GDD_CYCLE wins (used by Phase 21 pipeline runner)
|
|
14
|
+
* * fallback: 'current'
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { recordCall } = require('../scripts/lib/trajectory/index.cjs');
|
|
20
|
+
|
|
21
|
+
let raw = '';
|
|
22
|
+
process.stdin.setEncoding('utf8');
|
|
23
|
+
process.stdin.on('data', (chunk) => {
|
|
24
|
+
raw += chunk;
|
|
25
|
+
});
|
|
26
|
+
process.stdin.on('end', () => {
|
|
27
|
+
try {
|
|
28
|
+
const input = raw.trim() ? JSON.parse(raw) : {};
|
|
29
|
+
const toolName = input.tool_name || input.toolName || 'unknown';
|
|
30
|
+
const toolInput = input.tool_input ?? input.toolInput ?? null;
|
|
31
|
+
const toolResponse = input.tool_response ?? input.toolResponse ?? null;
|
|
32
|
+
const sessionId = input.session_id ?? input.sessionId ?? null;
|
|
33
|
+
const status =
|
|
34
|
+
toolResponse && typeof toolResponse === 'object' && toolResponse.is_error === true
|
|
35
|
+
? 'error'
|
|
36
|
+
: 'ok';
|
|
37
|
+
const latency = typeof input.latency_ms === 'number' ? input.latency_ms : 0;
|
|
38
|
+
|
|
39
|
+
recordCall({
|
|
40
|
+
cycle: process.env.GDD_CYCLE || 'current',
|
|
41
|
+
session_id: sessionId,
|
|
42
|
+
agent: input.agent || process.env.GDD_AGENT || 'unknown',
|
|
43
|
+
tool: toolName,
|
|
44
|
+
args: toolInput,
|
|
45
|
+
result: toolResponse,
|
|
46
|
+
latency_ms: latency,
|
|
47
|
+
status,
|
|
48
|
+
});
|
|
49
|
+
} catch (err) {
|
|
50
|
+
try {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
`[gdd-trajectory] hook failed: ${err && err.message ? err.message : String(err)}\n`,
|
|
53
|
+
);
|
|
54
|
+
} catch {
|
|
55
|
+
/* swallow */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Always emit a non-blocking continue verdict.
|
|
59
|
+
try {
|
|
60
|
+
process.stdout.write(JSON.stringify({ continue: true, suppressOutput: true }));
|
|
61
|
+
} catch {
|
|
62
|
+
/* swallow */
|
|
63
|
+
}
|
|
64
|
+
});
|
package/hooks/hooks.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hegemonart/get-design-done",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "A Claude Code plugin for systematic design improvement",
|
|
5
5
|
"author": "Hegemon",
|
|
6
6
|
"homepage": "https://github.com/hegemonart/get-design-done",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"bin": {
|
|
30
30
|
"get-design-done": "./scripts/install.cjs",
|
|
31
31
|
"gdd-state-mcp": "./scripts/mcp-servers/gdd-state/server.ts",
|
|
32
|
-
"gdd-sdk": "./bin/gdd-sdk"
|
|
32
|
+
"gdd-sdk": "./bin/gdd-sdk",
|
|
33
|
+
"gdd-events": "./scripts/cli/gdd-events.mjs"
|
|
33
34
|
},
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"access": "public",
|
|
@@ -84,5 +85,9 @@
|
|
|
84
85
|
"dependencies": {
|
|
85
86
|
"@anthropic-ai/claude-agent-sdk": "^0.2.119",
|
|
86
87
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
88
|
+
},
|
|
89
|
+
"optionalDependencies": {
|
|
90
|
+
"pngjs": "^7.0.0",
|
|
91
|
+
"ws": "^8.20.0"
|
|
87
92
|
}
|
|
88
93
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "planner-decision.schema.json",
|
|
4
|
+
"title": "Planner Decision Output Contract",
|
|
5
|
+
"description": "Schema for the structured JSON block emitted by design-planner. Lets /gdd:synthesize and downstream consumers (executor, audit aggregator) read planner output without regex-parsing markdown.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "plan_id", "tasks", "waves"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"const": "1.0.0"
|
|
13
|
+
},
|
|
14
|
+
"plan_id": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Stable identifier — e.g. '23-04' or 'PLAN.md'.",
|
|
17
|
+
"minLength": 1
|
|
18
|
+
},
|
|
19
|
+
"generated_at": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"format": "date-time"
|
|
22
|
+
},
|
|
23
|
+
"tasks": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["task_id", "summary", "touches"],
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"properties": {
|
|
31
|
+
"task_id": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"description": "Stable per-plan task identifier (e.g. T-01, 23-04-T-1)."
|
|
35
|
+
},
|
|
36
|
+
"summary": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 3
|
|
39
|
+
},
|
|
40
|
+
"touches": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": { "type": "string" },
|
|
43
|
+
"description": "File globs the task is expected to read or write."
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "type": "string" },
|
|
48
|
+
"description": "Other task_ids that must complete first.",
|
|
49
|
+
"default": []
|
|
50
|
+
},
|
|
51
|
+
"parallel_safe": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "Hint from the planner — the parallelism decision engine confirms via Touches: analysis.",
|
|
54
|
+
"default": false
|
|
55
|
+
},
|
|
56
|
+
"estimated_minutes": {
|
|
57
|
+
"type": "number",
|
|
58
|
+
"minimum": 0,
|
|
59
|
+
"description": "Rough wall-clock estimate; consumed by budget allocator."
|
|
60
|
+
},
|
|
61
|
+
"acceptance": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "Free-form acceptance criteria copy."
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"waves": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["wave", "task_ids"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"wave": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"minLength": 1,
|
|
79
|
+
"description": "Wave label (e.g. 'A', 'B', 'C')."
|
|
80
|
+
},
|
|
81
|
+
"task_ids": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": { "type": "string" },
|
|
84
|
+
"minItems": 1
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"rationale": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "Free-form planner-side rationale — not consumed by code."
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "verifier-decision.schema.json",
|
|
4
|
+
"title": "Verifier Decision Output Contract",
|
|
5
|
+
"description": "Schema for the structured JSON block emitted by design-verifier. Drives executor↔verifier ping-pong with a typed envelope rather than free-form prose.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "verdict", "gaps", "must_fix_before_ship", "confidence"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"const": "1.0.0"
|
|
13
|
+
},
|
|
14
|
+
"generated_at": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"format": "date-time"
|
|
17
|
+
},
|
|
18
|
+
"verdict": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": ["pass", "fail", "gap"],
|
|
21
|
+
"description": "pass = ship-ready, gap = remediable, fail = re-plan."
|
|
22
|
+
},
|
|
23
|
+
"gaps": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["id", "severity", "area", "summary"],
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"properties": {
|
|
30
|
+
"id": { "type": "string", "minLength": 1 },
|
|
31
|
+
"severity": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["P0", "P1", "P2", "P3"]
|
|
34
|
+
},
|
|
35
|
+
"area": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Free-form domain tag — e.g. 'a11y', 'motion', 'tokens'."
|
|
38
|
+
},
|
|
39
|
+
"summary": { "type": "string", "minLength": 3 },
|
|
40
|
+
"evidence": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Citation: file:line reference or audit excerpt."
|
|
43
|
+
},
|
|
44
|
+
"remediation": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "One-line proposed fix."
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"must_fix_before_ship": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "type": "string" },
|
|
54
|
+
"description": "Subset of gap.id values that block ship — typically the P0/P1 ones."
|
|
55
|
+
},
|
|
56
|
+
"confidence": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["high", "med", "low"],
|
|
59
|
+
"description": "Verifier's self-rated confidence — drives whether to escalate to a second pass."
|
|
60
|
+
},
|
|
61
|
+
"rationale": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "Free-form notes — not code-consumed."
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|