@hegemonart/get-design-done 1.38.5 → 1.39.2

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.
@@ -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.38.5"
8
+ "version": "1.39.2"
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.38.5",
15
+ "version": "1.39.2",
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.38.5",
4
+ "version": "1.39.2",
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. v1.27.7 ships gdd-mcp (Phase 27.7): 12 read-only MCP tools for sub-3s priming. v1.28.0 (Phase 28): Foundational References Tier 2 — 5 new reference files (color-theory, composition, proportion-systems, i18n, contrast-advanced), 2 verifier i18n probes + 1 explore i18n-readiness probe, 12 additive cross-link insertions across 10 existing references, 2 orthogonal audit-scoring lens-tags (composition_alignment + i18n_readiness).",
6
6
  "author": {
7
7
  "name": "hegemonart",
package/CHANGELOG.md CHANGED
@@ -4,6 +4,57 @@ All notable changes to get-design-done are documented here. Versions follow [sem
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.39.2] - 2026-06-01
8
+
9
+ ### Phase 39.2 — Long-Horizon Cost Governance
10
+
11
+ Closes the split Phase 39 (39.1 shipped DS migration). Phase 10.1 per-task caps + Phase 26 per-runtime telemetry track *cost* — none **forecast** it, cap it at the *project* level, or show whether the spend actually *shipped* anything. 39.2 adds a per-cycle spend **forecast**, a **`project_cap`** hard-halt, and an **ROI dashboard**. **No new runtime dependency, no new egress** — three pure helpers + an additive, disabled-by-default branch on the existing budget-enforcer hook.
12
+
13
+ ### Added
14
+
15
+ - **`scripts/lib/budget/cost-forecast.cjs`** — pure, dep-free per-cycle forecast: `forecast()` (best/typical/worst from the mean ± k·σ of historical per-cycle rates) + `cyclesToCap()` ("hit your cap in Y cycles"). Deterministic.
16
+ - **`scripts/lib/budget/roi.cjs`** — pure ROI join: `computeRoi()` (per-cycle cost ⋈ shipped/reverted commits → cost-per-shipped-commit + stick rate) + `roiTableMarkdown()`.
17
+ - **`scripts/lib/budget/project-cap.cjs`** — pure cap classifier: `classifyProjectBudget(spend, cap)` → `ok`/`warn-50`/`warn-80`/`halt`; **disabled when `cap ≤ 0`** (the non-breaking default).
18
+ - **`agents/cost-forecaster.md`** — groups `costs.jsonl` by cycle, runs the model, supports `--scenario best|typical|worst`, emits a `budget_forecast` event. Report-only (sonnet, size_budget M).
19
+ - **`skills/budget/SKILL.md`** (`/gdd:budget [--cycles N] [--scenario …]`) — forecast + "at the current rate you'll hit your $X project cap in Y cycles."
20
+ - **`skills/roi/SKILL.md`** (`/gdd:roi [--since <date>] [--window-days 14]`) — the ROI table; "shipped" = a commit surviving ≥ 14 days (catches revert-after-bug-discovery).
21
+ - **`reference/cost-governance.md`** — the contract (forecast model, `project_cap` semantics, ROI signal, events). Registered.
22
+
23
+ ### Changed
24
+
25
+ - **`hooks/budget-enforcer.ts`** — an **additive** `project_cap` branch (delegates the threshold math to `project-cap.cjs`): warns at 50% + 80%, hard-halts at 100% under `enforce`. **Disabled by default** (`project_cap_usd: 0`) so existing users see zero behavior change. **Graceful** — it blocks the *next* PreToolUse:Agent spawn, letting the current stage finish.
26
+ - **`reference/schemas/budget.schema.json`** — + `project_cap_usd` (≥ 0; 0/absent = disabled) + `project_cap_enforcement_mode` (enforce|warn|log).
27
+ - **`reference/schemas/events.schema.json`** — free-form `type` seed += `budget_forecast` / `project_cap_warning` / `project_cap_halt` (schema-seed only; `KNOWN_EVENT_TYPES` count unchanged).
28
+
29
+ ### Notes
30
+
31
+ - **No new runtime dependency, no new egress** — three pure text/arithmetic helpers + a local `package.json`/`costs.jsonl` read; the hook only ever *blocks*, never spends.
32
+ - 6-manifest lockstep at **v1.39.2** + `OFF_CADENCE_VERSIONS.add('1.39.2')` + the 31 live-pinned `manifests-version.txt` baselines forward-propagated 1.39.1 → 1.39.2.
33
+ - Inventory relock: registry-diff 157 → 158 (+`cost-governance`), skill-list 77 → 79 (+`budget`, +`roi`), agent-list +`cost-forecaster` + both frontmatter-snapshots, event-schema-snapshot sha256 re-locked (the seed-list edit, LF-normalized), tarball golden 700 → 707 (+7). Root `SKILL.md` command table += `budget` + `roi`.
34
+
35
+ ---
36
+
37
+ ## [1.39.1] - 2026-06-01
38
+
39
+ ### Phase 39.1 — DS Migration Workflows
40
+
41
+ Opens the v1.39.x arc and the first half of the split Phase 39. When a design system ships a breaking major (shadcn/ui v1→v2, Tailwind v3→v4, MUI v5→v6, Material 2/3 token rename), GDD can now read the in-repo `package.json`, detect the version skew, consult a curated rule library, and produce an **impact-scored, proposal-only migration plan** with codemod scaffolds. Nothing runs automatically and no codemod engine is bundled — `codemod-gen` emits jscodeshift/ast-grep template **text** the developer reviews and runs themselves. **No new runtime dependency, no new egress.**
42
+
43
+ ### Added
44
+
45
+ - **`reference/migrations/{shadcn-v2,tailwind-v4,mui-v6,material-3-to-4}.md`** — 4 curated rule libraries. Each carries `## Detection` (package.json dep + version), a `## Migration rules` table (Rule ID · Kind · From → To · Note, where Kind ∈ `rename-class`/`rename-prop`/`remove-component`/`token-rename`/`new-default`), and `## Impact notes`. Grounded in the official upstream migration guides (Tailwind v4 browser baseline, MUI Grid2/`experimental_` removal, Material M2→M3 `--mdc-*` → `--md-sys-*` tokens). Registered.
46
+ - **`scripts/lib/migration/codemod-gen.cjs`** — pure, dep-free `emitCodemod(rule, { engine: 'jscodeshift' | 'ast-grep' })` → `{ ruleId, engine, kind, template }`. One template per rule kind; deterministic. Emits template **text only** — it never imports or runs jscodeshift/ast-grep.
47
+ - **`agents/ds-migration-planner.md`** — detects the DS + version from `package.json`, consults `reference/migrations/<ds>.md`, scores each affected component (visual-delta × usage × tests-affected), and emits codemod scaffolds to `.design/migration/` via `codemod-gen`. **Proposal-only**; long-tail DS fall back to a generic template.
48
+ - **`agents/design-verifier.md`** — an in-place note (net-zero, stays at the 700-line cap): when a DS migration is in flight, the verifier also asserts the migration preserved the contract (visual-diff within threshold, component API surface unchanged, tests pass) and treats an unmigrated high-impact rule as a gap.
49
+
50
+ ### Notes
51
+
52
+ - **No new runtime dependency, no new egress** — `codemod-gen` is a pure text emitter; version detection is a local `package.json` read.
53
+ - 6-manifest lockstep at **v1.39.1** + `OFF_CADENCE_VERSIONS.add('1.39.1')` + the 30 live-pinned `manifests-version.txt` baselines forward-propagated 1.38.5 → 1.39.1.
54
+ - Inventory relock: registry-diff 153 → 157 (+4 rule libraries), agent-list +`ds-migration-planner` + both frontmatter-snapshots, tarball golden 694 → 700 (+6: 4 rule libraries + `codemod-gen.cjs` + `ds-migration-planner.md`). No skill/connection deltas.
55
+
56
+ ---
57
+
7
58
  ## [1.38.5] - 2026-06-01
8
59
 
9
60
  ### Phase 38.5 — Deployment Coordination Loop
package/README.md CHANGED
@@ -178,6 +178,14 @@ GDD now learns **which design patterns win with users**, not just which pass lin
178
178
 
179
179
  GDD now tracks a design past "PR merged" to **actually live**. [`/gdd:rollout-status`](skills/rollout-status/SKILL.md) reads the feature-flag service (the Phase 38 LaunchDarkly/Statsig/GrowthBook connections) via [`rollout-coordinator`](agents/rollout-coordinator.md) and classifies each cycle — `unrolled` / `staging-only` / `canary-N%` / `prod-100%` — surfacing **stuck** rollouts (a canary that hasn't advanced in N days). The pure [`rollout-status`](scripts/lib/rollout/rollout-status.cjs) classifier also computes a **deployed-percentage weight** that feeds the `design_arms` posterior via `verify_outcome` events — a variant that only reached 10% of users counts as weak evidence (0.1), a fully-rolled one counts 1.0. **Read-only** (GDD never advances or rolls back) and **no new runtime dependency**.
180
180
 
181
+ ### DS migration workflows (v1.39.1)
182
+
183
+ When a design system ships a breaking major — shadcn/ui v1→v2, Tailwind v3→v4, MUI v5→v6, or the Material 2/3 token rename — GDD detects the skew from the in-repo `package.json`, consults a curated rule library ([`reference/migrations/`](reference/migrations/)), and produces an **impact-scored, proposal-only** migration plan via [`ds-migration-planner`](agents/ds-migration-planner.md). Each affected component is scored by visual-delta × usage × tests-affected, and the planner emits codemod scaffolds to `.design/migration/` through the pure [`codemod-gen`](scripts/lib/migration/codemod-gen.cjs) — which produces jscodeshift/ast-grep template **text only** (it never imports or runs a codemod engine). [`design-verifier`](agents/design-verifier.md) then treats an in-flight migration as a contract: visual-diff within threshold, component API surface unchanged, tests green, and an unmigrated high-impact rule is a gap. **Proposal-only, no new runtime dependency, no new egress.**
184
+
185
+ ### Long-horizon cost governance (v1.39.2)
186
+
187
+ GDD already tracks cost per task and per runtime — now it **forecasts** it, **caps** it at the project level, and shows whether the spend **shipped**. [`/gdd:budget`](skills/budget/SKILL.md) groups `costs.jsonl` by cycle and (via [`cost-forecaster`](agents/cost-forecaster.md) → the pure [`cost-forecast`](scripts/lib/budget/cost-forecast.cjs)) projects the next N cycles in **best / typical / worst** scenarios — "at the current rate you'll hit your $X project cap in Y cycles." A new `budget.json.project_cap_usd` adds a **project-level hard cap**: the [`budget-enforcer`](hooks/budget-enforcer.ts) hook warns at 50% + 80% and **gracefully halts** the next agent spawn at 100% (via the pure [`project-cap`](scripts/lib/budget/project-cap.cjs) classifier) — **disabled by default**, so existing users are unaffected. [`/gdd:roi`](skills/roi/SKILL.md) joins per-cycle cost with commits that shipped (survived ≥ 14 days) vs reverted into a cost-per-shipped-commit table ([`roi`](scripts/lib/budget/roi.cjs)). **No new runtime dependency, no new egress** — the hook only ever blocks, never spends.
188
+
181
189
  ### Previous releases
182
190
 
183
191
  - **v1.26.0** — Headless Model Resolver (per-runtime tier→model map, `resolved_models` router field, per-runtime price tables, `reasoning-class` runtime-neutral alias).
package/SKILL.md CHANGED
@@ -102,6 +102,8 @@ Each stage produces artifacts in `.design/` inside the current project.
102
102
  | `export <cycle> --format html\|pdf\|notion [--pseudonymize] [--pr]` | `get-design-done:gdd-export` | Phase 35.5 — package a finished cycle's design output into a stakeholder-shareable artifact (self-contained HTML / Paged.js-print PDF / Notion page); redacts always, `--pseudonymize` masks identity for external sharing, `--pr` posts the HTML preview via pr-commenter |
103
103
  | `bootstrap-ds [--primary <color>] [--secondary <color>] [--tone <tags>] [--framework <t>]` | `get-design-done:gdd-bootstrap-ds` | Phase 37.2 — bootstrap a design system for a GREENFIELD project (no DS): brand input → OKLCH token system (color tints + modular type + 4pt/8pt spacing + radius/motion) in 3 variants to pick, then button/input/card proof scaffolding via `ds-generator` |
104
104
  | `rollout-status [<cycle>] [--all] [--stuck]` | `get-design-done:gdd-rollout-status` | Phase 38.5 — track a shipped cycle's production rollout (unrolled / staging-only / canary-N% / prod-100%) by reading the feature-flag service via `rollout-coordinator`; surfaces STUCK rollouts; feeds `design_arms` by deployed %. Read-only — never advances or rolls back |
105
+ | `budget [--cycles N] [--scenario best\|typical\|worst]` | `get-design-done:gdd-budget` | Phase 39.2 — forecast design-cycle spend (best/typical/worst from telemetry variance) via `cost-forecaster`; "at the current rate you'll hit your $X project cap in Y cycles." Read-only — never spends, edits `budget.json`, or halts (the budget-enforcer hook halts) |
106
+ | `roi [--since <date>] [--window-days 14]` | `get-design-done:gdd-roi` | Phase 39.2 — ROI table joining per-cycle cost with commits that shipped (survived ≥14d) vs reverted → cost-per-shipped-commit + stick rate. Read-only markdown report |
105
107
 
106
108
  ## Handoff Routing
107
109
 
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: cost-forecaster
3
+ description: Forecasts GDD spend over the next N design cycles. Reads .design/telemetry/costs.jsonl (grouping est_cost_usd by cycle) plus the configured .design/budget.json caps, runs the pure scripts/lib/budget/cost-forecast.cjs model (best/typical/worst from the variance of historical per-cycle rates), and reports "at the current rate you'll hit your project_cap in Y cycles." Supports --scenario best|typical|worst. Report-only — it never writes budget.json, never spends, never halts (the budget-enforcer hook halts). Spawned by /gdd:budget.
4
+ tools: Read, Bash, Grep, Glob
5
+ color: green
6
+ default-tier: sonnet
7
+ tier-rationale: "Groups a JSONL ledger by cycle and runs a pure projection helper, then narrates the result; bounded arithmetic + reporting, no design judgment — sonnet-tier."
8
+ size_budget: M
9
+ size_budget_rationale: "Honest tier sized to the ~95-line body. DELEGATES the projection math to scripts/lib/budget/cost-forecast.cjs and the contract to reference/cost-governance.md — the rollout-coordinator → rollout-status.cjs precedent."
10
+ parallel-safe: false
11
+ typical-duration-seconds: 30
12
+ reads-only: true
13
+ required_reading:
14
+ - "reference/cost-governance.md"
15
+ writes:
16
+ - ".design/telemetry/events.jsonl (a budget_forecast event only — append, no mutation)"
17
+ ---
18
+
19
+ # cost-forecaster
20
+
21
+ You forecast GDD's design-cycle spend so the user sees a cost trajectory **before** the bill arrives.
22
+ You are **report-only**: you read telemetry, run a pure model, and narrate. You never edit
23
+ `budget.json`, never spend, and never block a spawn — the Phase 25 budget-enforcer hook is the only
24
+ thing that halts.
25
+
26
+ **Read `reference/cost-governance.md` first** — it is the contract for the model, the scenarios, and
27
+ the `project_cap` semantics.
28
+
29
+ ## Inputs
30
+
31
+ - **`.design/telemetry/costs.jsonl`** — one row per agent spawn: `{ ts, agent, tier, est_cost_usd,
32
+ cycle, phase, ... }`. The **`cycle`** field is the grouping key.
33
+ - **`.design/budget.json`** — `project_cap_usd` (the ceiling to forecast against; `0`/absent ⇒ no
34
+ project cap configured, so report the trajectory without a "cycles to cap" line).
35
+ - **`--scenario best|typical|worst`** (default `typical`) and **`--cycles N`** (default `5`).
36
+
37
+ ## Procedure
38
+
39
+ 1. **Group spend by cycle.** Read `costs.jsonl`; sum `est_cost_usd` per distinct `cycle` value, in
40
+ chronological order. This yields the array of per-cycle USD totals. If there are 0 cycles, say so
41
+ and stop (nothing to forecast).
42
+ 2. **Run the model.** Call the pure helper — do the math in the lib, never by hand:
43
+
44
+ ```bash
45
+ node -e '
46
+ const { forecast, cyclesToCap } = require("./scripts/lib/budget/cost-forecast.cjs");
47
+ const perCycle = JSON.parse(process.argv[1]); // e.g. [10.2, 12.0, 8.4]
48
+ const f = forecast(perCycle, { nCycles: Number(process.argv[2]||5), scenario: process.argv[3]||"typical" });
49
+ const cap = Number(process.argv[4]||0);
50
+ const toCap = cap > 0 ? cyclesToCap(perCycle.reduce((a,b)=>a+b,0), cap, f.perCycle) : null;
51
+ console.log(JSON.stringify({ ...f, toCap }));
52
+ ' "$PER_CYCLE_JSON" "$N" "$SCENARIO" "$PROJECT_CAP"
53
+ ```
54
+
55
+ 3. **Report.** Print a short markdown summary:
56
+ - the chosen scenario + its per-cycle rate, and the best/typical/worst band (`low`/`high`);
57
+ - the projected total over the next N cycles;
58
+ - if `project_cap_usd > 0`: **"at the `<scenario>` rate (~$X/cycle) you'll reach your
59
+ $`<cap>` project cap in `<toCap>` cycles"** (or "never, spend is trending flat/down" when
60
+ `toCap` is `Infinity`).
61
+ 4. **Emit one event.** Append a `budget_forecast` event to `.design/telemetry/events.jsonl` with
62
+ payload `{ scenario, perCycle, projectedTotal, cyclesToCap }` (PII-free). Append only — never
63
+ rewrite the stream.
64
+
65
+ ## Scenarios (from `cost-forecast.cjs`, D-05)
66
+
67
+ | `--scenario` | per-cycle rate | reads as |
68
+ |---|---|---|
69
+ | `best` | `max(0, mean − k·σ)` | spend trending down / favorable variance |
70
+ | `typical` | `mean` | steady state (default) |
71
+ | `worst` | `mean + k·σ` | spend trending up / unfavorable variance |
72
+
73
+ `k = 1`. The projection is linear on the chosen rate. Always show the band, not just the point —
74
+ a wide best↔worst gap is itself the signal that spend is volatile.
75
+
76
+ ## Record
77
+
78
+ At run-end, print a `## Cost forecast` summary — the scenario, the per-cycle rate + band, the
79
+ projected next-N-cycle total, and the cycles-to-cap line (when a `project_cap_usd` is set). Then
80
+ append one JSONL line to `.design/intel/insights.jsonl` (per `reference/schemas/insight-line.schema.json`)
81
+ recording the forecast `{ scenario, perCycle, projectedTotal, cyclesToCap }`. Close with:
82
+
83
+ ```
84
+ ## COST FORECAST COMPLETE
85
+ ```
86
+
87
+ ## Boundaries
88
+
89
+ - Forecast is **cycle-scoped**, never per-agent-call.
90
+ - You **report**; you do not act. Setting or raising `project_cap_usd` is the user's call.
91
+ - No network. No external services. Pure local telemetry + a pure helper.
@@ -182,7 +182,7 @@ Allow-list seed (skip): `console\.(log|error|warn|info|debug)`, dev-only `/* */`
182
182
 
183
183
  ## Phase 2 — Must-Have Check
184
184
 
185
- Read `.design/STATE.md` `<must_haves>`. Also read must-haves from DESIGN-PLAN.md acceptance criteria, **and the brief's `<prior-research>` findings (Phase 38)** — for each prior-research finding, assert the current design addresses it or note an explicit defer + rationale (an unaddressed `critical`/`serious` finding is a gap). For each M-XX must-have, determine verification method and verify:
185
+ Read `.design/STATE.md` `<must_haves>`. Also read must-haves from DESIGN-PLAN.md acceptance criteria, **and the brief's `<prior-research>` findings (Phase 38)** — for each prior-research finding, assert the current design addresses it or note an explicit defer + rationale (an unaddressed `critical`/`serious` finding is a gap). **When a DS migration is in flight** (`.design/migration/` per Phase 39.1's `ds-migration-planner`), also assert it preserved the contract — visual-diff within threshold, component API surface unchanged, tests pass — and treat an unmigrated high-impact rule as a gap. For each M-XX must-have, determine verification method and verify:
186
186
 
187
187
  | Must-have type | Verification method |
188
188
  |---|---|
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: ds-migration-planner
3
+ description: Plans a design-system version migration (shadcn v1→v2, Tailwind v3→v4, MUI v5→v6, Material token migration). Detects the DS + version from package.json, consults the matching reference/migrations/<ds>.md rule library, proposes an impact-scored per-component plan (visual-delta × usage-frequency × tests-affected), and emits jscodeshift/ast-grep codemod templates via scripts/lib/migration/codemod-gen.cjs. Proposal-only — the user reviews + runs each codemod; GDD never auto-applies.
4
+ tools: Read, Bash, Grep, Glob
5
+ color: green
6
+ default-tier: sonnet
7
+ tier-rationale: "Consults a rule library + scores impact + emits codemod scaffolds via a pure helper; bounded planning, not open design judgment — sonnet-tier."
8
+ size_budget: M
9
+ size_budget_rationale: "Honest tier sized to the ~105-line body. DELEGATES the rules to reference/migrations/<ds>.md and the codemod templating to scripts/lib/migration/codemod-gen.cjs (the pdf-executor→validate-print-css precedent)."
10
+ parallel-safe: false
11
+ typical-duration-seconds: 60
12
+ reads-only: false
13
+ writes:
14
+ - ".design/migration/<ds>-<from>-<to>/** (plan + codemod templates, for review)"
15
+ ---
16
+
17
+ @reference/shared-preamble.md
18
+
19
+ # ds-migration-planner
20
+
21
+ ## Role
22
+
23
+ Turn a breaking design-system version bump into a reviewable, impact-ordered migration plan + ready-to-review codemod scaffolds. **Proposal-only (D-01)** — GDD detects, plans, and generates; the user reviews each codemod and runs it with their own tool (jscodeshift / ast-grep). GDD never auto-applies a migration.
24
+
25
+ ## When invoked
26
+
27
+ When the user wants to migrate a DS across a major (or `design-context-builder` detects a dep major behind the installed one). Supported libraries: shadcn (`reference/migrations/shadcn-v2.md`), Tailwind (`tailwind-v4.md`), MUI (`mui-v6.md`), Material tokens (`material-3-to-4.md`).
28
+
29
+ ## Step 1 — Detect DS + version (package.json only, D-03)
30
+
31
+ ```bash
32
+ node -e "const p=require('./package.json'); const d={...p.dependencies,...p.devDependencies}; console.log(JSON.stringify({ tailwind:d.tailwindcss, mui:d['@mui/material'], radix:Object.keys(d).filter(k=>k.startsWith('@radix-ui/')).length, material:d['@material/web']||d['@angular/material'] }))"
33
+ ```
34
+
35
+ Resolve the DS + the from→to version boundary from the dep version. Ambiguous → ask; never guess from source.
36
+
37
+ ## Step 2 — Load the rule library
38
+
39
+ Read `reference/migrations/<ds>.md`. Its `## Migration rules` table is the authoritative rule set (id · kind · from→to · note); `## Impact notes` flags high-visual-delta vs mechanical. **No matching library** (a long-tail DS) → emit a starter rule-library template for the user to author their own (D-05); do not guess rules.
40
+
41
+ ## Step 3 — Impact-scored per-component plan (D-04)
42
+
43
+ For each affected component, score `impact = visual_delta × usage_frequency × tests_affected`:
44
+
45
+ - **visual_delta** — from the rule's Impact notes (high for ring/shadow/color/Grid changes; low for import renames).
46
+ - **usage_frequency** — `grep -rc` the component/class/token across `src/`.
47
+ - **tests_affected** — count touching test files.
48
+
49
+ Order the plan **highest-impact-lowest-risk first** so the user migrates the riskiest surfaces under the most scrutiny. Present the plan as a table (component · rules · impact · manual-review?).
50
+
51
+ ## Step 4 — Emit codemod scaffolds (review before apply)
52
+
53
+ For each mechanical rule, emit a codemod template via the pure generator:
54
+
55
+ ```bash
56
+ node -e "const {emitCodemod}=require('./scripts/lib/migration/codemod-gen.cjs'); \
57
+ console.log(emitCodemod({id:RULE_ID, kind:KIND, from:FROM, to:TO, note:NOTE}, {engine:'jscodeshift'}).template)"
58
+ ```
59
+
60
+ Write each to `.design/migration/<ds>-<from>-<to>/<RULE_ID>.{js,yml}` for the user to review + run. `new-default` rules emit a **manual-review advisory** (no auto-transform). NEVER run the codemod or write into `src/`.
61
+
62
+ ## Step 5 — Hand off to verify
63
+
64
+ After the user applies codemods, `/gdd:verify` (`design-verifier`) checks the migration preserved the contract — visual-diff threshold, component API surface unchanged, tests pass. Note unresolved high-impact rules as gaps.
65
+
66
+ ## Record
67
+
68
+ Emit a `## Migration plan` summary: DS, from→to, the impact-ordered component table, the emitted codemod files, and manual-review items. Close with:
69
+
70
+ ```
71
+ ## MIGRATION PLAN COMPLETE
72
+ ```
@@ -207,6 +207,27 @@ const tierResolverOpenRouter = nodeRequire(
207
207
  '../scripts/lib/tier-resolver-openrouter.cjs',
208
208
  ) as TierResolverOpenRouterModule;
209
209
 
210
+ // Phase 39.2 D-04: project-level cap classifier (pure). Keeping the threshold
211
+ // math in scripts/lib/budget/project-cap.cjs (out of this hook) mirrors how the
212
+ // hook already delegates cost computation to scripts/lib/budget-enforcer.cjs,
213
+ // and makes the 50/80/100 thresholds unit-testable. The hook only reads the
214
+ // running project spend and asks this module what to do.
215
+ interface ProjectCapClassification {
216
+ enabled: boolean;
217
+ pct: number;
218
+ level: 'ok' | 'warn-50' | 'warn-80' | 'halt';
219
+ cap: number;
220
+ spend: number;
221
+ }
222
+ interface ProjectCapModule {
223
+ classifyProjectBudget(spendUsd: number, capUsd: number): ProjectCapClassification;
224
+ shouldHalt(c: ProjectCapClassification | null, enforcementMode: string): boolean;
225
+ capMessage(c: ProjectCapClassification | null): string | null;
226
+ }
227
+ const projectCap = nodeRequire(
228
+ '../scripts/lib/budget/project-cap.cjs',
229
+ ) as ProjectCapModule;
230
+
210
231
  /**
211
232
  * Plan 33.6-03 (SC#6 opt-in). OpenRouter is consulted ONLY when the user opts
212
233
  * in — either `.design/config.json#openrouter_enabled === true` OR
@@ -380,6 +401,15 @@ const PHASE_TOTALS_PATH = join(
380
401
  'telemetry',
381
402
  'phase-totals.json',
382
403
  );
404
+ // Phase 39.2 D-04: optional fast-path for the running project spend, mirroring
405
+ // PHASE_TOTALS_PATH. When absent the hook replays costs.jsonl (the project cap
406
+ // is opt-in, so this replay only happens for users who set project_cap_usd).
407
+ const PROJECT_TOTALS_PATH = join(
408
+ process.cwd(),
409
+ '.design',
410
+ 'telemetry',
411
+ 'project-totals.json',
412
+ );
383
413
  const STATE_PATH = join(process.cwd(), '.design', 'STATE.md');
384
414
 
385
415
  /** Defaults per D-12 — mirror scripts/bootstrap.sh budget.json bootstrap. */
@@ -392,6 +422,7 @@ const BUDGET_DEFAULTS: Required<
392
422
  | 'auto_downgrade_on_cap'
393
423
  | 'cache_ttl_seconds'
394
424
  | 'enforcement_mode'
425
+ | 'project_cap_usd'
395
426
  >
396
427
  > = {
397
428
  per_task_cap_usd: 2.0,
@@ -400,6 +431,11 @@ const BUDGET_DEFAULTS: Required<
400
431
  auto_downgrade_on_cap: true,
401
432
  cache_ttl_seconds: 3600,
402
433
  enforcement_mode: 'enforce',
434
+ // Phase 39.2 D-04: project-level cap is DISABLED by default (0). Existing
435
+ // users — who have no project_cap_usd in budget.json — see zero behavior
436
+ // change. project_cap_enforcement_mode stays optional and falls back to
437
+ // enforcement_mode at the use-site.
438
+ project_cap_usd: 0,
403
439
  };
404
440
 
405
441
  /**
@@ -504,6 +540,40 @@ export function currentPhaseSpend(phase: string): number {
504
540
  return sum;
505
541
  }
506
542
 
543
+ // ── cumulative project spend (Phase 39.2 D-04) ───────────────────────────────
544
+
545
+ /**
546
+ * Total project spend = sum of est_cost_usd across the WHOLE costs.jsonl ledger.
547
+ * Fast path: a `project-totals.json` (`{ total: number }`, written by the
548
+ * aggregator) mirrors the WR-02 phase-totals optimization. Falls back to a full
549
+ * ledger replay otherwise. Returns 0 on any error. Only ever consulted when
550
+ * project_cap_usd > 0, so the replay cost is paid only by opt-in users.
551
+ */
552
+ export function currentProjectSpend(): number {
553
+ if (existsSync(PROJECT_TOTALS_PATH)) {
554
+ try {
555
+ const data = JSON.parse(readFileSync(PROJECT_TOTALS_PATH, 'utf8')) as { total?: number };
556
+ return Number(data.total ?? 0);
557
+ } catch {
558
+ // fall through to replay
559
+ }
560
+ }
561
+ if (!existsSync(TELEMETRY_PATH)) return 0;
562
+ const lines = readFileSync(TELEMETRY_PATH, 'utf8')
563
+ .split(/\r?\n/)
564
+ .filter(Boolean);
565
+ let sum = 0;
566
+ for (const line of lines) {
567
+ try {
568
+ const row = JSON.parse(line) as { est_cost_usd?: number };
569
+ sum += Number(row.est_cost_usd ?? 0);
570
+ } catch {
571
+ // tolerant — skip malformed lines
572
+ }
573
+ }
574
+ return sum;
575
+ }
576
+
507
577
  // ── cycle + phase reader (STATE.md frontmatter) ─────────────────────────────
508
578
 
509
579
  /**
@@ -985,6 +1055,82 @@ export async function main(): Promise<void> {
985
1055
  const estCost = Number(toolInput._est_cost_usd ?? 0);
986
1056
  const phaseSpend = currentPhaseSpend(phase);
987
1057
 
1058
+ // ── Phase 39.2 D-04: project-level cap ─────────────────────────────────────
1059
+ //
1060
+ // Independent of enforcement_mode: the 50%/80% warnings + the 100% halt are
1061
+ // governed by project_cap_enforcement_mode (falling back to enforcement_mode).
1062
+ // No-op when project_cap_usd <= 0 (the opt-in default), so existing users see
1063
+ // zero change. Checked here, before the per-task/per-phase branches, so a
1064
+ // project-level breach halts the NEXT spawn regardless of the per-scope caps —
1065
+ // the graceful halt (the current stage's in-flight spawns already ran).
1066
+ if (budget.project_cap_usd > 0) {
1067
+ const projectSpend = currentProjectSpend();
1068
+ const projClass = projectCap.classifyProjectBudget(
1069
+ projectSpend + estCost,
1070
+ budget.project_cap_usd,
1071
+ );
1072
+ const projMode = budget.project_cap_enforcement_mode ?? budget.enforcement_mode;
1073
+ if (projClass.level === 'warn-50' || projClass.level === 'warn-80') {
1074
+ try {
1075
+ appendEvent({
1076
+ type: 'project_cap_warning',
1077
+ timestamp: new Date().toISOString(),
1078
+ sessionId: getSessionId(),
1079
+ ...(cycle !== undefined && cycle !== 'unknown' ? { cycle } : {}),
1080
+ payload: {
1081
+ pct: projClass.pct,
1082
+ spend: projClass.spend,
1083
+ cap: projClass.cap,
1084
+ level: projClass.level,
1085
+ },
1086
+ } as unknown as HookFiredEvent);
1087
+ } catch {
1088
+ // fail-open — event-stream errors never block the hook.
1089
+ }
1090
+ process.stderr.write(`gdd-budget-enforcer WARN: ${projectCap.capMessage(projClass)}\n`);
1091
+ } else if (projClass.level === 'halt') {
1092
+ try {
1093
+ appendEvent({
1094
+ type: 'project_cap_halt',
1095
+ timestamp: new Date().toISOString(),
1096
+ sessionId: getSessionId(),
1097
+ ...(cycle !== undefined && cycle !== 'unknown' ? { cycle } : {}),
1098
+ payload: {
1099
+ pct: projClass.pct,
1100
+ spend: projClass.spend,
1101
+ cap: projClass.cap,
1102
+ enforcementMode: projMode,
1103
+ },
1104
+ } as unknown as HookFiredEvent);
1105
+ } catch {
1106
+ // fail-open.
1107
+ }
1108
+ if (projectCap.shouldHalt(projClass, projMode)) {
1109
+ writeTelemetry({
1110
+ agent,
1111
+ tier: toolInput._tier_override ?? toolInput._default_tier ?? 'sonnet',
1112
+ tokens_in: Number(toolInput._tokens_in_est ?? 0),
1113
+ tokens_out: Number(toolInput._tokens_out_est ?? 0),
1114
+ cache_hit: false,
1115
+ est_cost_usd: estCost,
1116
+ enforcement_mode: projMode,
1117
+ block_reason: 'project_cap',
1118
+ _cyclePhase: cyclePhase,
1119
+ });
1120
+ emitHookFired('block', cycle);
1121
+ const response: ToolOutput = {
1122
+ continue: false,
1123
+ suppressOutput: false,
1124
+ message: `Project budget cap reached: $${projClass.spend.toFixed(2)} of $${budget.project_cap_usd.toFixed(2)} (${projClass.pct.toFixed(0)}%). Raise project_cap_usd in .design/budget.json, or set project_cap_enforcement_mode to "warn" to keep going. (Graceful halt — the current stage's earlier spawns already completed; this blocks the next one.)`,
1125
+ };
1126
+ process.stdout.write(JSON.stringify(response));
1127
+ return;
1128
+ }
1129
+ // warn / log mode: surface the 100% breach but allow the spawn.
1130
+ process.stderr.write(`gdd-budget-enforcer WARN: ${projectCap.capMessage(projClass)}\n`);
1131
+ }
1132
+ }
1133
+
988
1134
  // Phase 25 / D-05: per-spawn cap is class-specific when
989
1135
  // complexity_class is present and class_caps_usd[class] is defined.
990
1136
  // Falls back to per_task_cap_usd for backwards compatibility — when
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hegemonart/get-design-done",
3
- "version": "1.38.5",
3
+ "version": "1.39.2",
4
4
  "description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
5
5
  "author": "Hegemon",
6
6
  "homepage": "https://github.com/hegemonart/get-design-done",
@@ -0,0 +1,93 @@
1
+ # Cost Governance — Forecast, Project Cap, and ROI
2
+
3
+ Phase 39.2 contract. GDD already tracks cost (Phase 10.1 per-task caps, Phase 26 per-runtime
4
+ telemetry, Phase 27.5 bandit cost-arbitrage) — but it never *forecasts* spend, never imposes a
5
+ *project-level* hard cap, and never shows whether the spend actually *shipped* anything. This file is
6
+ the contract for the three pieces that close those gaps: the **forecast model**, the **`project_cap`
7
+ hard-halt**, and the **ROI dashboard**. All three are read-only/report-only except the hook, which
8
+ only ever *blocks* a spawn — it never spends, edits config, or mutates telemetry.
9
+
10
+ ## Telemetry inputs
11
+
12
+ - **`.design/telemetry/costs.jsonl`** (OPT-09) — one row per agent spawn:
13
+ `{ ts, agent, tier, tokens_in, tokens_out, cache_hit, est_cost_usd, cycle, phase }`.
14
+ The **`cycle`** field is the join key: grouping `est_cost_usd` by `cycle` gives per-cycle USD totals.
15
+ - **`.design/telemetry/events.jsonl`** — the event stream; this phase appends three new `type`s
16
+ (below).
17
+ - **Cycle identity** — `.design/STATE.md` frontmatter `cycle:`. There is no `CYCLES.md`; per-cycle
18
+ commit counts are computed on demand from `git log` (the `/gdd:stats` precedent).
19
+
20
+ ## Forecast model (`scripts/lib/budget/cost-forecast.cjs`, pure)
21
+
22
+ Group `costs.jsonl` by `cycle` → an array of per-cycle USD totals. From the **mean** `m` and
23
+ **population standard deviation** `σ` of those rates, the three scenarios are:
24
+
25
+ | Scenario | Per-cycle rate | Meaning |
26
+ |---|---|---|
27
+ | `best` | `max(0, m − k·σ)` | spend trends down / variance favorable |
28
+ | `typical` | `m` | steady state |
29
+ | `worst` | `m + k·σ` | spend trends up / variance unfavorable |
30
+
31
+ `k = 1` by default. The projection over the next `N` cycles is linear: `projectedTotal = rate · N`.
32
+ `cyclesToCap(currentSpend, cap, rate)` returns the integer number of cycles until `currentSpend`
33
+ reaches `cap` at that rate — `Infinity` when `rate ≤ 0`, `0` when already at/over the cap. This powers
34
+ the `/gdd:budget` warning **"at the current rate you'll hit cap $X in Y cycles."**
35
+
36
+ The math is a pure, dep-free, deterministic core (no fs, no clock, no randomness) — `agents/cost-forecaster.md`
37
+ and `/gdd:budget` read the telemetry and hand the grouped totals in. `--scenario best|typical|worst`
38
+ selects the rate.
39
+
40
+ ## Project cap (`scripts/lib/budget/project-cap.cjs` + `hooks/budget-enforcer.ts`)
41
+
42
+ A **project-level** hard cap, distinct from the existing per-task and per-phase caps. Config lives in
43
+ `.design/budget.json`:
44
+
45
+ | Key | Type | Default | Meaning |
46
+ |---|---|---|---|
47
+ | `project_cap_usd` | number ≥ 0 | `0` (disabled) | Total project spend ceiling (USD). |
48
+ | `project_cap_enforcement_mode` | `enforce` \| `warn` \| `log` | falls back to `enforcement_mode` | How a breach is handled. |
49
+
50
+ **Disabled by default.** A cap of `0` (or absent / non-finite) means *no project cap* — existing
51
+ users see zero behavior change. The classifier `classifyProjectBudget(spend, cap)` returns a level:
52
+
53
+ | Running spend vs cap | Level | Hook behavior |
54
+ |---|---|---|
55
+ | `< 50%` | `ok` | nothing |
56
+ | `≥ 50%` | `warn-50` | emit `project_cap_warning`, print, allow |
57
+ | `≥ 80%` | `warn-80` | emit `project_cap_warning`, print, allow |
58
+ | `≥ 100%` | `halt` | emit `project_cap_halt`; under `enforce`, block the spawn |
59
+
60
+ The cap is enforced in the **PreToolUse:Agent** hook, so the halt is **graceful**: it blocks the
61
+ *next* agent spawn, letting the current pipeline stage finish. Under `warn`/`log` mode a `halt`-level
62
+ breach prints/records but still allows the spawn (advisory). Running project spend is the sum of
63
+ `est_cost_usd` across all `costs.jsonl` rows (a `project-totals.json` fast-path mirrors the Phase 10.1
64
+ `phase-totals.json` optimization).
65
+
66
+ ## ROI dashboard (`scripts/lib/budget/roi.cjs`, pure + `/gdd:roi`)
67
+
68
+ Joins per-cycle cost with what actually shipped. **"Shipped"** = a commit that **survived ≥ 14 days**
69
+ in `main` (the ROADMAP default — a longer window catches revert-after-bug-discovery); a commit
70
+ reverted inside that window counts as `reverted`. `/gdd:roi` shells `git log` per cycle for the
71
+ shipped/reverted counts and reads per-cycle cost from `costs.jsonl`; `roi.cjs` computes:
72
+
73
+ - `costPerShipped = costUsd / max(shipped, 1)` — USD per commit that stuck.
74
+ - `stickRate = shipped / max(shipped + reverted, 1)` — fraction of commits that survived.
75
+
76
+ Output is a markdown table (cycle · cost · shipped · reverted · $/shipped · stick rate) plus a TOTAL
77
+ row. Markdown only — no GUI.
78
+
79
+ ## Events
80
+
81
+ Three new free-form `type`s on `.design/telemetry/events.jsonl`:
82
+
83
+ | Type | Emitted by | Payload (PII-free) |
84
+ |---|---|---|
85
+ | `budget_forecast` | `cost-forecaster` / `/gdd:budget` | `{ scenario, perCycle, projectedTotal, cyclesToCap }` |
86
+ | `project_cap_warning` | budget-enforcer hook | `{ pct, spend, cap, level }` at `warn-50` / `warn-80` |
87
+ | `project_cap_halt` | budget-enforcer hook | `{ pct, spend, cap, enforcementMode }` at `halt` |
88
+
89
+ ## Boundaries
90
+
91
+ Forecast is **cycle-scoped** (not per-agent-call). The cap **halts**, it never spends or auto-tunes.
92
+ ROI is **markdown**, not a GUI. Nothing here writes `budget.json` — the user sets the cap; GDD only
93
+ reads, forecasts, warns, and (at 100% under `enforce`) blocks the next spawn.