@hegemonart/get-design-done 1.37.1 → 1.38.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.
@@ -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.37.1"
8
+ "version": "1.38.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.37.1",
15
+ "version": "1.38.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.37.1",
4
+ "version": "1.38.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. 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,51 @@ All notable changes to get-design-done are documented here. Versions follow [sem
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.38.0] - 2026-06-01
8
+
9
+ ### Phase 38 — Outcome-Driven Adaptation (A/B Variants + Inbound User-Research Signals)
10
+
11
+ Closes the external-outcome loop. The bandit's reward was **internal** (lint/test/visual pass-fail); it couldn't learn "which design pattern wins with **users**" because user-outcome signals never entered the system. Phase 38 adds two external signal sources — **A/B experiments** + **user research** — feeding a new `design_arms` posterior + the brief/verify loop. **No new runtime dependency** (a pure Beta-posterior store + injectable `fetch`; the platforms are opt-in, read-only).
12
+
13
+ ### Added
14
+
15
+ - **`scripts/lib/ds-arms/design-arms-store.cjs`** — a pure `design_arms` posterior class, **distinct from the routing bandit** (`bandit-router.cjs`). Keyed by `(component_type, variant_pattern_hash)` (inline FNV-1a — no `crypto`), conservative **Beta(2, 8)** prior (mean 0.2 — a pattern earns trust from real outcomes). `variantKey` / `pull` / `observe` / `all`; atomic persist to `.design/telemetry/design-arms.json`. node-builtins only.
16
+ - **`reference/design-variants.md`** + **`design` `--variants N`** — N competing, hypothesis-tagged variants (`<variant id component pattern hypothesis>`, default N=2); the design stage consults the posterior to bias generation — **advisory, never directive (the user always wins, D-03)**. Registered.
17
+ - **`connections/launchdarkly.md` + `statsig.md` + `growthbook.md`** — A/B **experiment-source** connections (read-only; never run experiments — D-04). **`agents/experiment-result-ingester.md`** maps variant→outcome by the primary metric + significance → `observe()` into `design_arms` → `experiment_result` event.
18
+ - **`connections/usertesting.md` + `maze.md` + `hotjar.md`** + **`agents/user-research-synthesizer.md`** — user-research sources (read-only, indexed insights). **PII guard (D-05): every payload routes through `scripts/lib/pseudonymize.cjs` BEFORE any agent context** — enforced by `test/suite/phase-38-pii-guard.test.cjs`. Synthesizes ranked findings (finding · frequency · severity) into the brief **`<prior-research>`** block.
19
+ - **Verify cross-check** — `design-verifier` asserts each `<prior-research>` finding is addressed or explicitly deferred (unaddressed `critical`/`serious` = a gap).
20
+
21
+ ### Notes
22
+
23
+ - **No new runtime dependency, no new egress** — pure Beta store + injectable `fetchImpl` (hermetic tests); the A/B + research platforms are opt-in user-connected MCP/API, read-only.
24
+ - The 6 outcome connections are **Active-table + onboarding entries** (27 → 33 onboarded), NOT pipeline-stage capability-matrix rows — outcome-ingest is post-pipeline (the Notion export-only precedent), so it does not occupy a stage column.
25
+ - 6-manifest lockstep at **v1.38.0** + `OFF_CADENCE_VERSIONS.add('1.38.0')` + the 28 live-pinned `manifests-version.txt` baselines forward-propagated 1.37.2 → 1.38.0.
26
+ - Inventory relock: connection-list 35 → 41 (+6), phase-20 agent-list 50 → 52 (+`experiment-result-ingester` + `user-research-synthesizer`) + both frontmatter-snapshots, registry-diff 151 → 152 (+`design-variants`), tarball golden 680 → 690 (+10). `design-verifier` augmented in place (stays at the 700 cap).
27
+
28
+ ---
29
+
30
+ ## [1.37.2] - 2026-06-01
31
+
32
+ ### Phase 37.2 — Greenfield Design-System Bootstrap (`/gdd:bootstrap-ds`) — completes Phase 37
33
+
34
+ Second and **FINAL** sub-phase of the split **Phase 37** — completing it marks the **parent Phase 37 COMPLETE** (AI-Native Tools Wave 2 + Greenfield DS Bootstrap). Closes the greenfield gap: GDD assumed a design system already existed (in code or Figma) — a brand-new project has none. `/gdd:bootstrap-ds` turns a brand input into a coherent token system + proof components. **No new runtime dependency** — the token math emits native CSS `oklch()` (no color-conversion library).
35
+
36
+ ### Added
37
+
38
+ - **`scripts/lib/ds/token-scale.cjs`** — a pure, dependency-free (zero `require`) token generator. `oklchScale(primary)` → 9 tint/shade stops as native CSS `oklch()`, anchored at the primary, lightness interpolated toward white/black, chroma damped at the extremes (no OKLab→sRGB conversion, no color library); `typeScale` (modular), `spacingScale` (4pt/8pt), `radiusScale`. Deterministic.
39
+ - **`reference/ds-bootstrap-rubric.md`** — greenfield emission rules: primary → 9 tints; **never more than 2 brand colors**; neutrals + semantic colors; type ratios 1.2/1.25/1.333; 4pt/8pt spacing; radius + motion defaults; the 3 variants; role-named CSS-custom-property emission + framework mapping; proof scaffolding. Registered (`type: heuristic`, `phase: 37.2`; 150 → 151).
40
+ - **`agents/ds-generator.md`** — brand-input → token system via `token-scale.cjs` + the rubric + `color-theory.md`. Emits **3 variants** (conservative / balanced / bold), the user picks one, then scaffolds **button / input / card** in the detected framework (web default; native via Phase 34). Never invents a brand; never overwrites an existing DS; proposal→confirm.
41
+ - **`skills/bootstrap-ds/SKILL.md`** (`/gdd:bootstrap-ds`) — brand-input intake (primary + secondary + tone tags + target framework) → `ds-generator`. 43 lines (≤100 Phase-28.5 contract).
42
+
43
+ ### Notes
44
+
45
+ - **No new runtime dependency** (native `oklch()`), **no new egress**.
46
+ - 6-manifest lockstep at **v1.37.2** + `OFF_CADENCE_VERSIONS.add('1.37.2')` + the 27 live-pinned `manifests-version.txt` baselines forward-propagated 1.37.1 → 1.37.2.
47
+ - Inventory relock: skill-list 75 → 76 (+`bootstrap-ds`, both `current/` + `phase-20/`), phase-20 agent-list 49 → 50 (+`ds-generator`) + both frontmatter-snapshots, registry-diff 150 → 151, tarball golden 676 → 680 (+4); root `SKILL.md` command table + `command-count-sync` gate updated.
48
+ - **This completes the parent Phase 37 (AI-Native Tools Wave 2 + Greenfield DS Bootstrap).**
49
+
50
+ ---
51
+
7
52
  ## [1.37.1] - 2026-06-01
8
53
 
9
54
  ### Phase 37.1 — AI-Native Tools Wave 2 (Framer + Penpot + Webflow + v0.dev + Plasmic + Builder.io)
package/README.md CHANGED
@@ -166,6 +166,14 @@ GDD now opens the motion **exports** a project ships. The pure, dependency-free
166
166
 
167
167
  Six more AI-native design tools join the connection layer (Phase 14's backlog, now scheduled): **Framer**, **Penpot**, and **Webflow** (canvas-category — read frames/boards/structure as design sources) plus **v0.dev**, **Plasmic**, and **Builder.io** (generator-category — prompt→component, wired into [`design-component-generator`](agents/design-component-generator.md) as `<!-- impl: -->` sections). Plasmic is dual (canvas + generator). Onboarding grows 21 → 27. Each is an opt-in MCP/API connection that degrades to code-only when absent — **no new runtime dependency**. First sub-phase of Phase 37 (Greenfield DS Bootstrap follows).
168
168
 
169
+ ### Greenfield design-system bootstrap (v1.37.2)
170
+
171
+ `/gdd:bootstrap-ds` gives a brand-new project a coherent design system from a brand input (a primary color + optional secondary + tone tags + target framework). The pure [`token-scale`](scripts/lib/ds/token-scale.cjs) helper emits a 9-stop OKLCH color scale as native CSS `oklch()` (no color-conversion library), a modular type scale, a 4pt/8pt spacing scale, and radius/motion defaults; [`ds-generator`](agents/ds-generator.md) offers **3 variants** (conservative / balanced / bold) to pick from per [`reference/ds-bootstrap-rubric.md`](reference/ds-bootstrap-rubric.md), then scaffolds button/input/card proof components. Never invents a brand; never overwrites an existing DS. **No new runtime dependency.** This **completes Phase 37** (AI-native Wave 2 + greenfield bootstrap).
172
+
173
+ ### Outcome-driven adaptation (v1.38.0)
174
+
175
+ GDD now learns **which design patterns win with users**, not just which pass lint. `/gdd:design --variants N` emits N competing, hypothesis-tagged variants; a new `design_arms` posterior ([`design-arms-store`](scripts/lib/ds-arms/design-arms-store.cjs) — Beta(2,8), distinct from the routing bandit) consults prior outcomes to bias generation (**advisory, never directive**). Two read-only external signal sources close the loop: **A/B experiments** ([LaunchDarkly / Statsig / GrowthBook](connections/launchdarkly.md) → [`experiment-result-ingester`](agents/experiment-result-ingester.md)) and **user research** ([UserTesting / Maze / Hotjar](connections/usertesting.md) → [`user-research-synthesizer`](agents/user-research-synthesizer.md)), the latter **pseudonymized before any agent context** (a tested PII guard). Findings populate the brief's `<prior-research>` block, which verify cross-checks. **No new runtime dependency.** Onboarding 27 → 33.
176
+
169
177
  ### Previous releases
170
178
 
171
179
  - **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
@@ -100,6 +100,7 @@ Each stage produces artifacts in `.design/` inside the current project.
100
100
  | `benchmark <component\|--wave N\|--list\|--refresh component>` | `get-design-done:gdd-benchmark` | Harvest + synthesize per-component design specs from 18 design systems → `reference/components/<name>.md` |
101
101
  | `benchmark <component\|--wave N\|--list\|--refresh component>` | `get-design-done:gdd-benchmark` | Harvest + synthesize per-component design specs from 18 design systems → `reference/components/<name>.md` |
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
+ | `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` |
103
104
 
104
105
  ## Handoff Routing
105
106
 
@@ -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. 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). 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,74 @@
1
+ ---
2
+ name: ds-generator
3
+ description: Greenfield design-system generator. Turns a brand input (primary color + optional secondary + tone tags + target framework) into a coherent token system — OKLCH color tints/shades, a modular type scale, a 4pt/8pt spacing scale, radius + motion defaults — via the pure scripts/lib/ds/token-scale.cjs and reference/ds-bootstrap-rubric.md. Emits 3 variants (conservative/balanced/bold) for the user to pick, then scaffolds proof components (button/input/card) in the detected framework. Proposal→confirm; never overwrites an existing design system.
4
+ tools: Read, Write, Bash, Glob, Grep
5
+ color: green
6
+ default-tier: opus
7
+ tier-rationale: "Greenfield token-system synthesis is a design-judgment task (OKLCH color relationships, scale selection, tone→ratio mapping grounded in color-theory) — Opus-tier, not a mechanical worker."
8
+ size_budget: LARGE
9
+ size_budget_rationale: "Honest tier sized to the ~120-line body. The agent states the brand-input → token-system → 3-variants → scaffold flow and DELEGATES the deterministic math to scripts/lib/ds/token-scale.cjs and the emission rules to reference/ds-bootstrap-rubric.md (the pdf-executor→validate-print-css precedent)."
10
+ parallel-safe: false
11
+ typical-duration-seconds: 90
12
+ reads-only: false
13
+ writes:
14
+ - ".design/tokens/**"
15
+ - "src/** (proof components, on confirm only)"
16
+ ---
17
+
18
+ @reference/shared-preamble.md
19
+
20
+ # ds-generator
21
+
22
+ ## Role
23
+
24
+ Bootstrap a coherent design system for a **greenfield** project that has none — no Figma, no token file, no component library. Take a brand input and emit a token system + a few proof components, grounded in `reference/ds-bootstrap-rubric.md` + `reference/color-theory.md`, using the deterministic `scripts/lib/ds/token-scale.cjs`. **Never invent a brand** (no logomark, no typeface, no third brand color) and **never overwrite an existing DS** — if `design-context-builder` already mapped one, stop and defer to it.
25
+
26
+ ## When invoked
27
+
28
+ By `/gdd:bootstrap-ds` (the skill collects the brand input). Also reachable when `design-context-builder` detects a greenfield project (no DS signals) and the user opts in.
29
+
30
+ ## Inputs (brand input)
31
+
32
+ - **primary** (required) — a brand color (hex / rgb / `oklch()`). Convert to OKLCH `{l, c, h}`.
33
+ - **secondary** (optional) — a second brand color. Emitted only if supplied (rubric ≤2-colors rule).
34
+ - **tone tags** (optional) — e.g. `calm`, `corporate`, `editorial`, `playful`, `bold` → maps to the type ratio + chroma treatment per the rubric.
35
+ - **target framework** (optional) — `web` (default) / `native-ios` / `native-android` / `flutter` (Phase 34 routing). Detect from the project if absent.
36
+
37
+ ## Step 1 — Resolve the primary to OKLCH
38
+
39
+ Parse the brand primary to `{ l, c, h }`. If given as hex/rgb, convert to OKLCH (state the conversion; do NOT add a color library — use a documented inline conversion or ask the user for the `oklch()` value). Validate `l ∈ 0..1`, `c ≥ 0`, `h ∈ 0..360`.
40
+
41
+ ## Step 2 — Generate the 3 variants
42
+
43
+ For each of **conservative / balanced / bold** (rubric table), run the pure generator and assemble a token set:
44
+
45
+ ```bash
46
+ node -e "const t=require('./scripts/lib/ds/token-scale.cjs'); \
47
+ console.log(JSON.stringify({ \
48
+ color: t.oklchScale({l:L,c:C,h:H}), \
49
+ type: t.typeScale(1, RATIO), \
50
+ space: t.spacingScale(BASE, 8), \
51
+ radius: t.radiusScale(R) }, null, 2))"
52
+ ```
53
+
54
+ Vary chroma (×0.8 / ×1.0 / ×1.15-clamped), type ratio (1.2 / 1.25 / 1.333), and radius (4 / 8 / 12) per the rubric. Emit neutrals (low-chroma ramp) + semantic colors (success/warning/danger/info at fixed hues) the same way. Verify text/surface pairings clear WCAG AA (`reference/color-theory.md`).
55
+
56
+ ## Step 3 — Present + pick (D-02)
57
+
58
+ Show the 3 variants compactly (the `500` primary, the type ratio, the spacing baseline, the radius, a one-line feel). The user picks ONE. Do not scaffold before the pick.
59
+
60
+ ## Step 4 — Emit the chosen token set (proposal → confirm)
61
+
62
+ Emit role-named CSS custom properties (`:root { --color-primary-500: oklch(...); --space-4: 16px; --radius-md: 8px; ... }`) as the canonical artifact, plus the target-framework mapping (web → Tailwind `theme.extend` / shadcn CSS vars; native → `reference/native-platforms.md`). Propose the file(s); write only on confirm.
63
+
64
+ ## Step 5 — First-component scaffolding (proof)
65
+
66
+ Emit **button + input + card** in the target framework, consuming only the emitted tokens — a coherence proof, not a component library. Proposal→confirm; never write to `src/` without it.
67
+
68
+ ## Record
69
+
70
+ Emit a `## Bootstrap summary` for the cycle: the chosen variant, the token counts (9 color stops + neutrals + semantics, N type steps, N spacing steps), the framework, and the scaffolded components. Close with:
71
+
72
+ ```
73
+ ## DS BOOTSTRAP COMPLETE
74
+ ```
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: experiment-result-ingester
3
+ description: Ingests A/B experiment results (LaunchDarkly / Statsig / GrowthBook) and folds them into the design_arms posterior. Reads a finished experiment's payload, maps each variant to a win/lose by the primary metric + significance, calls observe() on scripts/lib/ds-arms/design-arms-store.cjs, and emits an experiment_result typed event. Read-only against the platform (never runs/creates experiments). Injectable fetch — hermetic. Degrades to a noop when no experiment-source is configured.
4
+ tools: Read, Bash, Grep, Glob, ToolSearch
5
+ color: green
6
+ default-tier: sonnet
7
+ tier-rationale: "Mechanical mapping of an experiment payload to win/lose + a posterior update via a pure store; no design judgment — sonnet-tier."
8
+ size_budget: M
9
+ size_budget_rationale: "Honest tier sized to the ~95-line body. The agent states the read→map→observe→emit flow and DELEGATES the posterior math to scripts/lib/ds-arms/design-arms-store.cjs and the per-platform probe to connections/{launchdarkly,statsig,growthbook}.md (the ticket-sync-agent→reference precedent)."
10
+ parallel-safe: false
11
+ typical-duration-seconds: 30
12
+ reads-only: false
13
+ writes:
14
+ - ".design/telemetry/design-arms.json"
15
+ - ".design/intel/insights.jsonl"
16
+ ---
17
+
18
+ @reference/shared-preamble.md
19
+
20
+ # experiment-result-ingester
21
+
22
+ ## Role
23
+
24
+ Close the A/B side of the outcome loop: read a **finished** experiment's results from the configured experiment-source and teach the `design_arms` posterior which design pattern actually won with users. **Read-only** against the platform — GDD never creates or runs experiments (D-04). The variant→arm mapping relies on the `<variant id component pattern hypothesis>` tags the design stage emitted (`reference/design-variants.md`).
25
+
26
+ ## When invoked
27
+
28
+ After an experiment tagged to a GDD cycle reaches a decision, or on demand. Gate on an experiment-source being `available` (per `connections/launchdarkly.md` / `connections/statsig.md` / `connections/growthbook.md`); none → print `experiment ingest: no experiment-source configured — skipped.` and stop (degrade-to-noop).
29
+
30
+ ## Step 1 — Read the experiment payload
31
+
32
+ Probe the configured source (ToolSearch for an MCP, else the platform API key env). Read the experiment's variants + the **primary metric** per variant + the statistical decision (winner / no-significant-difference). Use an **injectable `fetchImpl`** so this is hermetic under test — never hard-code a live HTTP call in a way the test can't stub. Read-only scopes only.
33
+
34
+ ## Step 2 — Map variant → outcome
35
+
36
+ For each variant in the experiment:
37
+
38
+ - Resolve its GDD `component` + `pattern` from the variant tag (or the experiment's metadata mapping).
39
+ - `won` = this variant is the **statistically significant winner** on the primary metric. A no-significant-difference experiment yields NO observation (do not reward noise) — skip, and note it.
40
+
41
+ ## Step 3 — Fold into the posterior
42
+
43
+ ```bash
44
+ node -e "const s=require('./scripts/lib/ds-arms/design-arms-store.cjs'); \
45
+ const k=s.variantKey(COMPONENT, PATTERN); \
46
+ s.observe(COMPONENT, k, { won: WON, source: 'ab', label: PATTERN });"
47
+ ```
48
+
49
+ One `observe` per variant with a decided outcome. `won:true` → `alpha += 1`; the losing variant(s) → `won:false` (`beta += 1`). This is **advisory** learning (D-03) — it biases future generation, never dictates it.
50
+
51
+ ## Step 4 — Emit the event
52
+
53
+ Emit an `experiment_result` typed event into the Phase 22 chain (`.design/intel/insights.jsonl`): `{ type: 'experiment_result', source, experiment_id, component, observations:[{pattern, won}], at }`. No PII (experiment IDs + pattern slugs only).
54
+
55
+ ## Record
56
+
57
+ Emit a `## Experiment ingest` summary: source, experiment, the per-variant win/lose, the posterior means before→after, and any skipped (no-significant-difference) variants. Close with:
58
+
59
+ ```
60
+ ## EXPERIMENT INGEST COMPLETE
61
+ ```
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: user-research-synthesizer
3
+ description: Synthesizes inbound user-research signals (UserTesting / Maze / Hotjar) into brief-grade insights. Reads test reports + session/heatmap aggregates, ALWAYS pseudonymizes them through scripts/lib/pseudonymize.cjs BEFORE any agent context (PII guard), then extracts top findings with frequency + severity for the brief <prior-research> block. Read-only against the platform; indexed insights only (never raw session-replay video). Degrades to a noop when no research source is configured.
4
+ tools: Read, Bash, Grep, Glob, ToolSearch
5
+ color: green
6
+ default-tier: sonnet
7
+ tier-rationale: "Synthesis of pre-collected research reports into ranked findings; bounded extraction, not open design judgment — sonnet-tier."
8
+ size_budget: M
9
+ size_budget_rationale: "Honest tier sized to the ~105-line body. The agent states the read→pseudonymize→synthesize→write-<prior-research> flow and DELEGATES the PII transform to scripts/lib/pseudonymize.cjs and per-platform detail to connections/{usertesting,maze,hotjar}.md."
10
+ parallel-safe: false
11
+ typical-duration-seconds: 45
12
+ reads-only: false
13
+ writes:
14
+ - ".design/BRIEF.md (the <prior-research> block)"
15
+ - ".design/telemetry/design-arms.json (optional qualitative signal)"
16
+ - ".design/intel/insights.jsonl"
17
+ ---
18
+
19
+ @reference/shared-preamble.md
20
+
21
+ # user-research-synthesizer
22
+
23
+ ## Role
24
+
25
+ Close the qualitative side of the outcome loop: turn pre-collected user-research into **brief-grade insights** the next cycle can act on. Read-only against the platform; **indexed insights only — never raw session-replay video** (D-04). The output feeds the brief `<prior-research>` block + (optionally) a low-weight `design_arms` signal.
26
+
27
+ ## PII guard — non-negotiable (D-05)
28
+
29
+ **Every research payload passes through `scripts/lib/pseudonymize.cjs` BEFORE it enters ANY agent context, log, or event.** Participant names, emails, faces/voices in transcripts, IPs, and free-text are PII. The flow is **read → pseudonymize → reason** — never read → reason → redact. There is no path where a raw research payload reaches the model. A static CI test asserts this routing.
30
+
31
+ ## When invoked
32
+
33
+ On demand (`/gdd:research-sync`) or auto-suggested when the verify cross-check finds `<prior-research>` data > 14 days old. Gate on a research source being `available` (per `connections/usertesting.md` / `connections/maze.md` / `connections/hotjar.md`); none → `research synthesis: no research source configured — skipped.` (degrade-to-noop).
34
+
35
+ ## Step 1 — Read (read-only) + pseudonymize
36
+
37
+ Probe the configured source (ToolSearch MCP, else the platform API key env; injectable `fetchImpl` for hermetic tests). Pull indexed insights — test-report findings, task success/time, misclick rates, survey responses, heatmap aggregates. **Immediately** pipe every payload through `pseudonymize.cjs`:
38
+
39
+ ```bash
40
+ node -e "const {pseudonymize}=require('./scripts/lib/pseudonymize.cjs'); process.stdout.write(pseudonymize(require('fs').readFileSync(0,'utf8')))" < raw-payload.json > safe-payload.json
41
+ ```
42
+
43
+ Only `safe-payload.json` is ever read into reasoning.
44
+
45
+ ## Step 2 — Synthesize brief-grade findings
46
+
47
+ From the pseudonymized payload, extract the top findings, each with:
48
+
49
+ - **finding** — a one-line observation in user terms ("users miss the secondary CTA on mobile").
50
+ - **frequency** — how many participants / sessions exhibited it.
51
+ - **severity** — `critical | serious | minor` (blocks the task / slows it / cosmetic).
52
+
53
+ Rank by `severity × frequency`. Keep the top N (default 7) — a brief is a focus list, not a transcript dump.
54
+
55
+ ## Step 3 — Write the `<prior-research>` block + optional signal
56
+
57
+ Write the ranked findings into the brief's `<prior-research>` block (consumed by `skills/brief/SKILL.md` + checked at verify). When a finding maps cleanly to a tested design pattern, optionally fold a **low-weight** qualitative signal into `design_arms` (`observe(component, key, { won, source: 'research', weight: 0.5 })`) — research corroborates A/B, it does not outweigh it.
58
+
59
+ ## Record
60
+
61
+ Emit a `## Research synthesis` summary: source, # reports read, the ranked findings (finding / frequency / severity), and confirmation that pseudonymize ran first. Emit a `research_synthesized` event (no PII). Close with:
62
+
63
+ ```
64
+ ## RESEARCH SYNTHESIS COMPLETE
65
+ ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  This directory contains connection specifications for external tools and MCPs that the get-design-done pipeline integrates with. Each connection has its own spec file. This file is the index.
4
4
 
5
- **Getting started:** run `/gdd:connections` for the interactive onboarding wizard — it probes all 27 connections, recommends setup based on your project type, and walks you through installing each one (auto-run for reversible MCP adds, copy-command for everything else). You can also run `/gdd:connections list` for a read-only status check or `/gdd:connections <name>` to jump to a single connection's setup.
5
+ **Getting started:** run `/gdd:connections` for the interactive onboarding wizard — it probes all 33 connections, recommends setup based on your project type, and walks you through installing each one (auto-run for reversible MCP adds, copy-command for everything else). You can also run `/gdd:connections list` for a read-only status check or `/gdd:connections <name>` to jump to a single connection's setup.
6
6
 
7
7
  ---
8
8
 
@@ -43,6 +43,12 @@ This directory contains connection specifications for external tools and MCPs th
43
43
  | v0.dev | Active | [`connections/v0-dev.md`](connections/v0-dev.md) | **AI-native** (Wave 2, generator) — Vercel v0; MCP-first → REST + `V0_API_KEY`; component-generator `v0` impl (37.1) |
44
44
  | Plasmic | Active | [`connections/plasmic.md`](connections/plasmic.md) | **AI-native** (Wave 2, dual) — canvas read + code emission; component-generator `plasmic` impl (37.1) |
45
45
  | Builder.io | Active | [`connections/builder-io.md`](connections/builder-io.md) | **AI-native** (Wave 2, generator) — Visual Copilot, pull-only this phase; component-generator `builder-io` impl (37.1) |
46
+ | LaunchDarkly | Active | [`connections/launchdarkly.md`](connections/launchdarkly.md) | **Outcome** (experiment-source) — read-only A/B results (`LAUNCHDARKLY_API_KEY`/MCP); `experiment-result-ingester` → `design_arms`; `GDD_DISABLE_LAUNCHDARKLY`; degrade-to-noop (38) |
47
+ | Statsig | Active | [`connections/statsig.md`](connections/statsig.md) | **Outcome** (experiment-source) — read-only experiment/pulse results (`STATSIG_API_KEY`/MCP); → `design_arms`; `GDD_DISABLE_STATSIG`; degrade-to-noop (38) |
48
+ | GrowthBook | Active | [`connections/growthbook.md`](connections/growthbook.md) | **Outcome** (experiment-source) — read-only results (`GROWTHBOOK_API_KEY`, self-hosted/cloud, /MCP); → `design_arms`; `GDD_DISABLE_GROWTHBOOK`; degrade-to-noop (38) |
49
+ | UserTesting | Active | [`connections/usertesting.md`](connections/usertesting.md) | **Outcome** (user-research) — read-only test reports; **pseudonymize-first** → `user-research-synthesizer` → brief `<prior-research>`; `GDD_DISABLE_USERTESTING`; degrade-to-noop (38) |
50
+ | Maze | Active | [`connections/maze.md`](connections/maze.md) | **Outcome** (user-research) — read-only usability metrics; **pseudonymize-first** → `user-research-synthesizer`; `GDD_DISABLE_MAZE`; degrade-to-noop (38) |
51
+ | Hotjar | Active | [`connections/hotjar.md`](connections/hotjar.md) | **Outcome** (user-research) — read-only indexed insights (no raw video); **pseudonymize-first** → `user-research-synthesizer`; `GDD_DISABLE_HOTJAR`; degrade-to-noop (38) |
46
52
 
47
53
  ---
48
54
 
@@ -0,0 +1,110 @@
1
+ # GrowthBook — Connection Specification
2
+
3
+ This file is the connection specification for GrowthBook within the get-design-done pipeline. It lives in `connections/` alongside other connection specs (see [`connections/slack.md`](slack.md) for the structural sibling — an API/env-based connection with a three-value probe and degrade-to-noop).
4
+
5
+ ---
6
+
7
+ GrowthBook is an open-source **experiment-source** for the outcome-learning layer (Phase 38). GDD **reads** A/B experiment results from GrowthBook and feeds each variant→outcome into the `design_arms` posterior, so shipped design decisions get reinforced or discounted by what actually performed in production. GDD never runs, creates, edits, or stops experiments — it is strictly **read-only** (D-04). Reads degrade to a noop when unconfigured or disabled; outcome learning simply pauses and the pipeline never blocks.
8
+
9
+ GrowthBook ships in two deployment shapes, and GDD supports both: a **cloud** account (the hosted GrowthBook service) or a **self-hosted** instance you operate. GDD connects to whichever you point it at; it does not install, bundle, or host GrowthBook (mirrors the self-hosted-or-cloud split in [`connections/slack.md`](slack.md)'s sibling canvas specs).
10
+
11
+ ---
12
+
13
+ ## Setup
14
+
15
+ **Prerequisites:** read-only access to a GrowthBook project's experiment results — either a GrowthBook **API key** (a read-only / viewer-scoped token, not a full-access token) **or** the GrowthBook MCP if it is installed in your runtime.
16
+
17
+ **Token (env, never committed):**
18
+
19
+ ```bash
20
+ export GROWTHBOOK_API_KEY="<read-only-api-key>"
21
+ ```
22
+
23
+ **Optional host (self-hosted only):**
24
+
25
+ ```bash
26
+ export GROWTHBOOK_API_HOST="https://growthbook.internal.example.com"
27
+ ```
28
+
29
+ `GROWTHBOOK_API_HOST` is the **distinguishing signal** between cloud and self-hosted. Leave it unset for the hosted GrowthBook cloud (GDD targets the default cloud host); set it to your instance origin when self-hosting. GDD records the resolved host so downstream stages know which deployment produced a result.
30
+
31
+ Use the narrowest scope GrowthBook offers (read-only / viewer). The key is a credential — never commit it (not in source, not in `.env`, not in config), never log it, rotate if exposed. GDD reads it from env only and never requests a write scope.
32
+
33
+ **Verification:**
34
+
35
+ ```bash
36
+ test -n "${GROWTHBOOK_API_KEY}" && echo "growthbook key present" || echo "growthbook key absent"
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Availability Probe
42
+
43
+ Probe is **MCP-first**, env-fallback, kill-switch-aware:
44
+
45
+ 1. If `GDD_DISABLE_GROWTHBOOK=1` → short-circuit to `not_configured` (treated as disabled; never probe further).
46
+ 2. Run `ToolSearch({ query: "growthbook" })`. If a GrowthBook MCP tool resolves → `growthbook: available`.
47
+ 3. Else check the env key: `test -n "${GROWTHBOOK_API_KEY}"`.
48
+ - Non-empty → `growthbook: available`
49
+ - Empty → `growthbook: not_configured`
50
+ 4. Source present (MCP or key) but a read errored at fetch time → `growthbook: unavailable`.
51
+ 5. When the env path is used, classify the deployment from `GROWTHBOOK_API_HOST`: unset → `deployment=cloud`; any other host → `deployment=self-hosted`. (Via MCP only, with no host exported, `deployment=unknown`.)
52
+
53
+ Write the `growthbook` status to `.design/STATE.md` `<connections>` after probing, using the **three-value schema** plus the deployment marker:
54
+
55
+ ```xml
56
+ <connections>
57
+ growthbook: not_configured
58
+ </connections>
59
+ ```
60
+
61
+ When available, record the resolved deployment alongside the status, e.g. `growthbook: available (deployment=self-hosted)` or `growthbook: available (deployment=cloud)`.
62
+
63
+ | Value | Meaning |
64
+ |---|---|
65
+ | `available` | GrowthBook MCP resolves OR `GROWTHBOOK_API_KEY` set, AND not disabled |
66
+ | `unavailable` | source present but a result read errored |
67
+ | `not_configured` | no MCP and no `GROWTHBOOK_API_KEY`, or `GDD_DISABLE_GROWTHBOOK=1` |
68
+
69
+ | Field | Values | Meaning |
70
+ |---|---|---|
71
+ | `deployment` | `cloud` / `self-hosted` / `unknown` | Derived from `GROWTHBOOK_API_HOST`; `unknown` when only the MCP path is present and no host is exported |
72
+
73
+ The kill-switch `GDD_DISABLE_GROWTHBOOK=1` forces `not_configured` regardless of MCP/key presence (mirrors the Phase 30 / 35.1 disable convention). `gsd-health` surfaces the state.
74
+
75
+ ---
76
+
77
+ ## Pipeline Integration
78
+
79
+ GrowthBook contributes the **experiment-source** capability. The flow is read-only and one-directional (results in, never experiments out):
80
+
81
+ 1. The probe marks `growthbook: available` (and its `deployment`) in `.design/STATE.md`.
82
+ 2. The experiment-result ingester ([`agents/experiment-result-ingester.md`](../agents/experiment-result-ingester.md)) reads completed A/B results from GrowthBook — each experiment's variant identifiers plus their measured metric outcomes (the conversion/lift figures GrowthBook computed for that experiment).
83
+ 3. It maps each variant to the matching `design_arms` arm and records the outcome (win / loss / lift) against that arm's posterior, so the next design decision is informed by production evidence rather than priors alone.
84
+ 4. For each mapped result it emits an `experiment_result` event into the pipeline's event stream for downstream learning and audit.
85
+
86
+ A variant that does not map to a known `design_arms` arm is recorded as unmatched and skipped — it never invents an arm. The ingester reads results only; it issues no experiment-creation, assignment, or mutation calls against GrowthBook (D-04). It surfaces `deployment` so an operator can tell cloud results from self-hosted ones in the event trail, since the two deployments can carry independent experiment sets.
87
+
88
+ **Injectable fetch (hermetic tests):** the ingester takes an injectable `fetchImpl` (defaulting to the resolved MCP tool or global `fetch`). Tests pass a stub `fetchImpl` so `npm test` exercises the variant→outcome mapping with no real egress — no live GrowthBook call in CI, and no dependence on either the cloud host or a self-hosted origin. There is **no bundled GrowthBook SDK and no new dependency**; reads go through the MCP tool or the injectable `fetchImpl`.
89
+
90
+ **Scope — read vs. never:**
91
+
92
+ | GDD reads (read-only) | GDD never does (D-04) |
93
+ |---|---|
94
+ | Completed experiment results: variant ids + metric outcomes | Create, start, stop, or archive an experiment |
95
+ | Per-variant lift / win-loss figures GrowthBook computed | Assign users, change traffic splits, or edit targeting |
96
+ | Experiment / variant identifiers for `design_arms` mapping | Write any flag, feature, or experiment definition back |
97
+
98
+ Everything GDD touches in GrowthBook is a `GET`-equivalent read. There is no GrowthBook code path in GDD that mutates state, which is what lets a reader/viewer-scoped key suffice and keeps the connection safe to leave attached.
99
+
100
+ ---
101
+
102
+ ## Fallback Behavior
103
+
104
+ `not_configured` (no MCP, no key) or disabled (`GDD_DISABLE_GROWTHBOOK=1`) → the experiment-source **degrades to a noop**: the ingester is skipped, no `experiment_result` events are emitted, and the `design_arms` posterior simply does not get the outcome update this cycle. Design decisions still ship — they just rely on prior evidence instead of fresh experiment results.
105
+
106
+ A read failure when a source *is* present (cloud unreachable, self-hosted origin down, or an errored response) → `growthbook: unavailable`; that cycle's ingestion is skipped (no error surfaced to the pipeline) and retried on the next probe. The ingester returns a skipped/empty result and never throws, so outcome learning is best-effort and **never blocks the pipeline** (mirrors the notify degrade-to-noop in [`connections/slack.md`](slack.md)). Always re-probe at stage entry — both the access path and the resolved deployment can change between sessions.
107
+
108
+ ---
109
+
110
+ Do NOT edit the connection index here — the 38 wiring plan adds the Active-Connections row + the experiment-source matrix column.
@@ -0,0 +1,110 @@
1
+ # Hotjar — Connection Specification
2
+
3
+ This file is the connection specification for Hotjar within the get-design-done pipeline. It lives in `connections/` alongside other connection specs. See the connection index for the full connection capability matrix (the hotjar row is added at the Phase 38 wiring closeout).
4
+
5
+ ---
6
+
7
+ Hotjar is a **user-research source** for the discover/plan stages. GDD reads **indexed insights only** — heatmap aggregates, indexed session-insight summaries, and survey results — and feeds findings, as brief-grade prior research, into a phase brief. The connection is strictly **read-only**: GDD never writes to Hotjar, and it **never reads raw session-replay video** (or any per-visitor recording). It pulls only the pre-indexed, aggregate insight surface.
8
+
9
+ Session data is among the most PII-sensitive inputs the pipeline can touch — a single replay or un-aggregated event can carry names, emails, typed form values, IPs, and on-screen personal data. **CRITICAL (D-05): every Hotjar payload MUST pass through `scripts/lib/pseudonymize.cjs` BEFORE it reaches any agent context.** Pseudonymization is mandatory, not optional, and it is the single chokepoint between Hotjar data and any model prompt. This mirrors the redact-before-egress discipline used for the notification surfaces ([`connections/slack.md`](slack.md)), but inverted: redaction guards *outbound*; here pseudonymization guards *inbound* — research data must be scrubbed before it enters a prompt.
10
+
11
+ ---
12
+
13
+ ## Setup
14
+
15
+ **Prerequisites:** a Hotjar account with API access, and a **read-only** API token scoped to insight/aggregate endpoints only (no recording-export scope).
16
+
17
+ **Token (env, never committed):**
18
+
19
+ ```bash
20
+ export HOTJAR_API_KEY="<your-read-only-token>"
21
+ ```
22
+
23
+ Scope the token to **indexed insights / heatmap aggregates / survey results only** — never grant raw-recording or session-export scope, even if Hotjar offers it. GDD has no code path that downloads recordings, and the token must not be able to either. The key is a credential: never commit it (not in source, not in `.env`, not in config), never log it, and rotate it if exposed. GDD reads it from env only.
24
+
25
+ **Verification:**
26
+
27
+ ```bash
28
+ test -n "${HOTJAR_API_KEY}" && echo "hotjar token present" || echo "hotjar token absent"
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Availability Probe
34
+
35
+ Hotjar may be reached either through an MCP (if one is registered) or directly via its HTTP API with the env token. Probe **MCP-first**, then fall back to the env check.
36
+
37
+ **Step H1 — MCP presence (preferred):**
38
+
39
+ ```
40
+ ToolSearch({ query: "hotjar", max_results: 10 })
41
+ ```
42
+
43
+ - Non-empty result → an MCP is registered → `hotjar: available`
44
+ - Empty result → fall through to Step H2
45
+
46
+ **Step H2 — token presence:**
47
+
48
+ ```bash
49
+ test -n "${HOTJAR_API_KEY}"
50
+ ```
51
+
52
+ - Non-empty → `hotjar: available`
53
+ - Empty → `hotjar: not_configured`
54
+ - Present (MCP or token) but a live insight fetch errored → `hotjar: unavailable`
55
+
56
+ **Kill-switch:** Hotjar is forced to a noop when `GDD_DISABLE_HOTJAR=1` (env), regardless of MCP/token presence — the probe short-circuits to `not_configured` and no fetch is attempted. `gdd-health` surfaces the state (mirrors the Phase 30 / 35.x kill-switch pattern).
57
+
58
+ **Write the `hotjar` status to `.design/STATE.md` `<connections>` after probing.** Three-value schema:
59
+
60
+ | Value | Meaning |
61
+ |---|---|
62
+ | `available` | MCP registered, OR `HOTJAR_API_KEY` set — and not disabled |
63
+ | `unavailable` | MCP/token present but a live insight fetch errored |
64
+ | `not_configured` | no MCP and no `HOTJAR_API_KEY` (or `GDD_DISABLE_HOTJAR=1`) |
65
+
66
+ ```xml
67
+ <connections>
68
+ hotjar: not_configured
69
+ </connections>
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Pipeline Integration
75
+
76
+ Hotjar feeds the **user-research** lane of discover/plan. The flow is strictly ordered, and the pseudonymize step is non-negotiable and comes **first**:
77
+
78
+ 1. **Fetch (read-only):** pull heatmap aggregates, indexed session-insight summaries, and survey results for the relevant page/flow. Aggregates only — never a raw recording.
79
+ 2. **Pseudonymize FIRST:** pass every fetched payload through `scripts/lib/pseudonymize.cjs` *before anything else touches it*. The scrubbed `{ payload, replacements }` is the only form allowed downstream. Nothing — no agent, no log, no event — sees the pre-scrub data.
80
+ 3. **Synthesize:** hand the pseudonymized payload to the `user-research-synthesizer` agent, which distills it into brief-grade insights (top friction points, drop-off zones, survey themes) without re-introducing any identifier.
81
+ 4. **Inject:** the synthesized, brief-grade insights land in the phase brief's `<prior-research>` block, where the plan stage reads them as prior evidence for design decisions.
82
+
83
+ Stage flow: `heatmap / insight / survey aggregates → pseudonymize.cjs (FIRST) → user-research-synthesizer → brief-grade insights → brief <prior-research> block`.
84
+
85
+ Adjacent methodology (sample sizing, heatmap/survey interpretation, over-claim guards) lives in the user-research reference doc and governs how the synthesizer reads these aggregates.
86
+
87
+ The fetch path POSTs/GETs via an **injectable `fetchImpl`** (defaulting to the global `fetch`), so the test suite drives it with synthetic insight fixtures hermetically — no live Hotjar, no network. There is **no new dependency**: no `@hotjar/*` package, no SDK; just `fetch` + the existing pseudonymize primitive.
88
+
89
+ ---
90
+
91
+ ## Fallback Behavior
92
+
93
+ Hotjar is an **enhancement, never a hard requirement** (D-03). When `hotjar: not_configured`, `hotjar: unavailable`, or the kill-switch is on, the user-research lane **degrades to a noop**: the `<prior-research>` block is simply built without Hotjar-sourced signals (other research sources, if any, still contribute), and the pipeline continues. Discover/plan never block on Hotjar availability or on a fetch failure.
94
+
95
+ A failed or skipped fetch returns a benign skipped result and never throws. The synthesizer treats absent Hotjar input as "source: missing" and proceeds — same graceful-degradation contract the other optional connections use.
96
+
97
+ ---
98
+
99
+ ## PII + Privacy
100
+
101
+ Session-research data is highly PII-sensitive. This section is binding, not advisory.
102
+
103
+ - **Pseudonymize before context (mandatory, D-05):** every Hotjar payload passes through `scripts/lib/pseudonymize.cjs` **before** it reaches any agent prompt, the synthesizer, or any persisted artifact. There is no bypass path; pseudonymization is the single inbound chokepoint. (Note this is *pseudonymization, not anonymization* — identity correlation is reduced, not eliminated.)
104
+ - **Aggregates, not raw sessions:** GDD reads only indexed insights, heatmap aggregates, and survey results. It **never** fetches, stores, or forwards raw session-replay video or per-visitor recordings — there is no code path that can, and the token must not be scoped to allow it.
105
+ - **No PII in logs or events:** the pseudonymized payload is what flows downstream; the pre-scrub payload is never written to logs, never emitted in pipeline events, and never persisted. The `HOTJAR_API_KEY` is likewise never logged.
106
+ - **Least scope:** prefer the narrowest read-only token Hotjar offers; if recording-export scope cannot be excluded at the token, treat that token as unsafe for this connection.
107
+
108
+ ---
109
+
110
+ Do NOT edit the connection index here — the Phase 38 wiring plan adds the Active-Connections row + the experiment-source matrix column.