@hegemonart/get-design-done 1.28.7 → 1.30.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.
Files changed (71) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +116 -0
  4. package/README.de.md +37 -0
  5. package/README.fr.md +37 -0
  6. package/README.it.md +37 -0
  7. package/README.ja.md +37 -0
  8. package/README.ko.md +37 -0
  9. package/README.md +44 -0
  10. package/README.zh-CN.md +37 -0
  11. package/SKILL.md +12 -10
  12. package/agents/design-reflector.md +50 -0
  13. package/package.json +3 -1
  14. package/reference/capability-gap-stage-gate.md +261 -0
  15. package/reference/known-failure-modes.md +185 -0
  16. package/reference/pseudonymization-rules.md +189 -0
  17. package/reference/registry.json +22 -1
  18. package/reference/schemas/events.schema.json +97 -3
  19. package/reference/schemas/generated.d.ts +319 -4
  20. package/scripts/build-distribution-bundles.cjs +549 -0
  21. package/scripts/cli/gdd-events.mjs +35 -2
  22. package/scripts/gsd-cleanup-incubator.cjs +367 -0
  23. package/scripts/install.cjs +61 -0
  24. package/scripts/lib/apply-reflections/incubator-proposals.cjs +448 -0
  25. package/scripts/lib/bandit-router.cjs +92 -9
  26. package/scripts/lib/gsd-health-mirror/index.cjs +37 -1
  27. package/scripts/lib/incubator-author.cjs +845 -0
  28. package/scripts/lib/install/config-dir.cjs +26 -0
  29. package/scripts/lib/install/converters/codex-plugin.cjs +407 -0
  30. package/scripts/lib/install/converters/cursor-marketplace.cjs +309 -0
  31. package/scripts/lib/install/doctor-codex-plugin.cjs +388 -0
  32. package/scripts/lib/install/doctor-cursor-marketplace.cjs +366 -0
  33. package/scripts/lib/install/doctor-tier2.cjs +586 -0
  34. package/scripts/lib/install/runtimes.cjs +48 -0
  35. package/scripts/lib/issue-reporter/cli-flag-report.cjs +153 -0
  36. package/scripts/lib/issue-reporter/consent-prompt.cjs +231 -0
  37. package/scripts/lib/issue-reporter/dedup.cjs +458 -0
  38. package/scripts/lib/issue-reporter/destination.cjs +37 -0
  39. package/scripts/lib/issue-reporter/draft-writer.cjs +157 -0
  40. package/scripts/lib/issue-reporter/gh-absent-fallback.cjs +220 -0
  41. package/scripts/lib/issue-reporter/gh-submit.cjs +114 -0
  42. package/scripts/lib/issue-reporter/kill-switch.cjs +122 -0
  43. package/scripts/lib/issue-reporter/payload-assembly.cjs +367 -0
  44. package/scripts/lib/issue-reporter/privacy-diff.cjs +385 -0
  45. package/scripts/lib/issue-reporter/report-flow.cjs +269 -0
  46. package/scripts/lib/issue-reporter/triage-matcher.cjs +270 -0
  47. package/scripts/lib/pseudonymize.cjs +444 -0
  48. package/scripts/lib/reflections-cycle-writer.cjs +172 -0
  49. package/scripts/lib/reflector/capability-gap-scan.cjs +751 -0
  50. package/scripts/lib/reflector-capability-gap-aggregator.cjs +320 -0
  51. package/scripts/lint-agentskills-spec.cjs +457 -0
  52. package/scripts/release-smoke-test.cjs +33 -2
  53. package/scripts/validate-incubator-scope.cjs +133 -0
  54. package/skills/apply-reflections/SKILL.md +16 -1
  55. package/skills/apply-reflections/apply-reflections-procedure.md +71 -3
  56. package/skills/compare/SKILL.md +2 -2
  57. package/skills/compare/compare-rubric.md +1 -1
  58. package/skills/darkmode/SKILL.md +2 -2
  59. package/skills/darkmode/darkmode-audit-procedure.md +1 -1
  60. package/skills/fast/SKILL.md +46 -0
  61. package/skills/figma-write/SKILL.md +2 -2
  62. package/skills/graphify/SKILL.md +2 -2
  63. package/skills/reflect/SKILL.md +9 -0
  64. package/skills/reflect/procedures/capability-gap-scan.md +120 -0
  65. package/skills/report-issue/SKILL.md +53 -0
  66. package/skills/report-issue/report-issue-procedure.md +120 -0
  67. package/skills/router/SKILL.md +5 -0
  68. package/skills/router/capability-gap-emitter.md +65 -0
  69. package/skills/style/SKILL.md +2 -2
  70. package/skills/style/style-doc-procedure.md +1 -1
  71. package/skills/update/SKILL.md +3 -2
@@ -0,0 +1,189 @@
1
+ ---
2
+ title: Pseudonymization Rules
3
+ phase: 30
4
+ type: meta-rules
5
+ status: stable
6
+ created: 2026-05-20
7
+ ---
8
+
9
+ # Pseudonymization Rules
10
+
11
+ The 8 substitution rules applied by `scripts/lib/pseudonymize.cjs` to identity-correlatable content in Phase 30 issue payloads.
12
+
13
+ ## Pseudonymization, not anonymization
14
+
15
+ These rules **reduce** identity correlation. They do not **eliminate** it. A determined adversary with side-channel data — writing style, code-style patterns, repository fingerprints, timing — may still re-identify a reporter. Phase 30 explicitly delivers pseudonymization-not-anonymization (CONTEXT D-01); over-promising would be misleading.
16
+
17
+ Pseudonymization here serves three concrete goals:
18
+
19
+ - **(a) Prevent casual identification** by other users who later read the public GitHub issue.
20
+ - **(b) Reduce corporate-DLP false-positives** at submission time on common identifier shapes (usernames in paths, hostnames in stack traces, email patterns in logs). The pseudonymized payload is less likely to be intercepted by enterprise DLP tools that scan for these exact patterns.
21
+ - **(c) Give the user a structural opportunity** to inspect and edit the payload before it leaves their machine. The pseudonymization step makes the payload's identity-correlation surface visible — the user can read it and decide.
22
+
23
+ Submission is always user-initiated and user-reviewed per Plan 30-04 (CONTEXT D-03). Phase 30 does **not** auto-submit, ever.
24
+
25
+ ## Pipeline placement
26
+
27
+ Pseudonymization runs **after** redaction (`scripts/lib/redact.cjs`, Phase 22) in the Plan 30-02 payload assembly. The two are orthogonal:
28
+
29
+ | Layer | Module | What it scrubs |
30
+ |---|---|---|
31
+ | Secrets (high-stakes floor) | `scripts/lib/redact.cjs` | Tokens, API keys, JWTs, PEM blocks, AWS credentials, Slack/Stripe/GitHub tokens — strings that must **never** escape |
32
+ | Identity (privacy) | `scripts/lib/pseudonymize.cjs` | Names, paths, hostnames, repo origins, env-var values, emails, IPs — strings that **may** be published but should not personally identify the reporter |
33
+
34
+ Redaction handles "this string must never escape"; pseudonymization handles "this string is fine to publish but should not personally identify the reporter." The two modules do not import each other; composition lives at the caller (Plan 30-02 payload assembly). See CONTEXT D-01 for the framing rationale and CONTEXT references for the redaction prerequisite.
35
+
36
+ ## The rules
37
+
38
+ Eight rules. Each rule has a stable id (R1..R8) used by `/gdd:update --show-privacy-diff` (Plan 30-07) to enumerate active rules.
39
+
40
+ ### R1 — git-identity
41
+
42
+ **Replaces:** `user.name` and `user.email` from git config when they appear in payload strings (stack traces, log messages, commit-author lines).
43
+
44
+ **Why:** The user's git identity is the most direct re-identification vector inside a payload. Substring matching is used for the name with a word-boundary guard (`\b`) so unrelated words containing the name as a substring (e.g., `alicewonderland` when the name is `alice`) are preserved. Email matching is case-insensitive.
45
+
46
+ **Before/after example:**
47
+ ```
48
+ Author: alice <alice@example.com> committed at 12:34
49
+ Author: <user> <<user>@<domain>> committed at 12:34
50
+ ```
51
+
52
+ **Coverage notes:** Word-boundary regex (`\b<name>\b`) prevents stripping unrelated substrings. Email match is case-insensitive (`alice@example.com` and `Alice@Example.Com` both replaced). Name values shorter than 2 chars and email values shorter than 3 chars are skipped to avoid over-eager matching on common short sequences.
53
+
54
+ ### R2 — absolute-paths
55
+
56
+ **Replaces:** Home-directory absolute paths across all three OS conventions:
57
+
58
+ - Linux: `/home/<user>/...`
59
+ - macOS: `/Users/<user>/...`
60
+ - Windows: `C:\Users\<user>\...` (any drive letter)
61
+
62
+ **Why:** Issue payloads frequently include stack traces with absolute file paths. The path shape alone reveals the user's OS, and the username segment exposes identity. Critically: issue payloads from one OS may be processed on a maintainer's different OS, so the module handles all three shapes regardless of which OS the report was generated on.
63
+
64
+ **Before/after example:**
65
+ ```
66
+ /home/alice/code/proj/file.ts:42 → <home>/code/proj/file.ts:42
67
+ /Users/alice/code/proj/file.ts:42 → <home>/code/proj/file.ts:42
68
+ C:\Users\alice\code\proj\file.ts:42 → <home>\code\proj\file.ts:42
69
+ ```
70
+
71
+ **Coverage notes:** Six regex sweeps — three identity-aware (when the name is known) and three generic (matching `/Users/<any>/`, `/home/<any>/`, `<drive>:\Users\<any>\`). Identity-aware sweeps run first so the identity-aware substitution takes precedence; generic sweeps catch references to teammates or other users that may appear in stack traces.
72
+
73
+ ### R3 — hostname
74
+
75
+ **Replaces:** `os.hostname()` value with `<host>`.
76
+
77
+ **Why:** The machine hostname often encodes the user's name or organization (e.g., `alices-macbook.local`, `acme-corp-laptop-42`). Word-boundary substitution plus a special-case sweep for `@hostname` shapes inside ssh-like strings (where the standard `\b` lookaround does not fire as expected on `@`).
78
+
79
+ **Before/after example:**
80
+ ```
81
+ Connected to alices-macbook.local from alices-macbook
82
+ Connected to <host>.local from <host>
83
+ ```
84
+
85
+ **Coverage notes:** Two sweeps — one for `@hostname` (ssh-shape), one with standard word-boundary. Hostnames shorter than 2 chars are skipped.
86
+
87
+ ### R4 — repo-origin
88
+
89
+ **Replaces:** Git origin URL with `<category>-hash:<sha8>` where:
90
+
91
+ - `<category>` is `public-personal-hash` when the caller's `opts.repoVisibility === 'public-personal'`
92
+ - `<category>` is `private-org-hash` for all other inputs (conservative default)
93
+ - `<sha8>` is `sha256(normalized_origin_url)[:8]` where normalization strips protocol prefixes (`git@github.com:`, `https://github.com/`, `ssh://`, `git://`) and trailing `.git`, then lowercases
94
+
95
+ **Why:** The repo URL identifies both the user and the organization. The hash gives maintainers a deterministic dedup key (same repo → same hash) without exposing the URL. The category prefix tells maintainers whether the reporter's repo is a personal public project or something more sensitive.
96
+
97
+ **Before/after example:**
98
+ ```
99
+ Remote: git@github.com:acme-corp/internal-tools.git
100
+ Remote: private-org-hash:a1b2c3d4
101
+ ```
102
+
103
+ **Coverage notes:** Caller resolves visibility via `gh repo view --json visibility` (or skips if `gh` is absent — module then uses the conservative `private-org-hash` default). The module performs no network calls; visibility is an injected `opts.repoVisibility` value. Owner-is-user vs owner-is-org distinction is the caller's responsibility (the module cannot tell from a URL alone). Both the raw `repoOrigin` substring and the normalized form are substituted so multiple shapes of the same URL in a stack trace all collapse to one placeholder.
104
+
105
+ ### R5 — env-vars
106
+
107
+ **Replaces:** **VALUES** (not key names) of these environment variables wherever they appear in payload strings:
108
+
109
+ - `USER`, `LOGNAME`, `HOSTNAME` (POSIX identity)
110
+ - Any key ending in `_TOKEN`, `_KEY`, `_SECRET` (defense-in-depth alongside Phase 22 redaction)
111
+
112
+ **Why:** Phase 22's `redact.cjs` catches specific token shapes (`sk-ant-...`, `ghp_...`). R5 catches the residual case where an env-var value appears in a payload because the user interpolated it into a log message (e.g., `console.log('TOKEN=' + process.env.MY_KEY)`). The defense-in-depth catches custom org-specific keys that don't match the Phase 22 token-shape regex catalogue.
113
+
114
+ **Before/after example:**
115
+ ```
116
+ Error: GITHUB_TOKEN=ghp_abcDEFghi123 was rejected
117
+ (with process.env.GITHUB_TOKEN === 'ghp_abcDEFghi123')
118
+
119
+ Error: GITHUB_TOKEN=<env:GITHUB_TOKEN> was rejected
120
+ ```
121
+
122
+ **Coverage notes:** Empty / 1-char / 2-char values are skipped (corruption guard — short values would over-match unrelated content). Longer-value entries are processed first (descending by length) so a token that contains another's substring does not get half-replaced. R5 walks the entire payload tree (strings get value substitution, objects/arrays recurse, cycles detected via WeakSet) — value substitution works regardless of nesting depth.
123
+
124
+ ### R6 — email-in-logs
125
+
126
+ **Replaces:** Email addresses that slipped past R1's identity-aware substitution (e.g., third-party emails mentioned in a stack trace or log line) with `<email>`.
127
+
128
+ **Why:** R1 catches the user's own `user.email`. R6 is the generic catch-all for any other email addresses that may appear in a payload — including teammates' emails in error messages, vendor support emails in stack traces, or addresses pulled from data being processed at the time of the error.
129
+
130
+ **Before/after example:**
131
+ ```
132
+ Stack frame: at notify(maintainer@example.org)
133
+ Stack frame: at notify(<email>)
134
+ ```
135
+
136
+ **Coverage notes:** Standard RFC-5322-ish regex (`/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g`). R6 runs **after** R1 in the rule pipeline so identity-aware substitution takes precedence (the user's email becomes `<user>@<domain>`, not `<email>`).
137
+
138
+ ### R7 — ip-addresses
139
+
140
+ **Replaces:** IPv4 and IPv6 addresses, retaining only network class:
141
+
142
+ - IPv4 `a.b.c.d` → `<ipv4:a.b.c.0>` (zero out the last octet)
143
+ - IPv6 → `<ipv6:<prefix>::>` (drop the last segment)
144
+
145
+ **Why:** IP addresses reveal network topology and may identify the user's location, employer, or VPN egress. Retaining the network class (first three IPv4 octets, IPv6 prefix) preserves enough information for maintainer-side triage ("the error occurred over a corporate network") while dropping the specific host identifier.
146
+
147
+ **Before/after example:**
148
+ ```
149
+ Failed to reach 203.0.113.42 → Failed to reach <ipv4:203.0.113.0>
150
+ fe80::1ff:fe23:4567:890a → <ipv6:fe80::1ff:fe23:4567::>
151
+ ```
152
+
153
+ **Coverage notes:** Regex guards prevent false-positives on:
154
+
155
+ - Semver strings (`v1.2.3.4`) — leading `v` blocked by lookbehind
156
+ - Email-adjacent strings (`@1.2.3.4`) — `@` blocked by lookbehind
157
+ - Date strings (`2026-05-20`) — dashes don't match the dotted-octet pattern
158
+ - Longer dotted strings (`1.2.3.4.5`) — trailing `.` blocked by lookahead
159
+
160
+ IPv6 regex requires at least 5 segments to avoid false-positives on time strings (`12:34`, `12:34:56`).
161
+
162
+ ### R8 — stable-pseudonym
163
+
164
+ **Replaces:** Nothing in payload text — R8 is a **separate utility export** (`stablePseudonym(userId, repoOrigin)`) the caller invokes when constructing payload metadata.
165
+
166
+ **Why:** Maintainers want to group reports from the same user-and-repo without ever seeing identity. A deterministic 8-char hex pseudonym = `sha256(userId + ':' + normalized_repo_origin)[:8]` gives them that grouping key. Same user + same repo always hashes to the same 8 chars; different inputs produce different outputs. The URL normalization (strip protocol prefix, strip `.git`, lowercase) makes the hash stable across `git@github.com:foo/bar.git` and `https://github.com/foo/bar` shapes of the same origin.
167
+
168
+ **Before/after example:**
169
+ ```
170
+ userId: 'alice'
171
+ repoOrigin: 'git@github.com:foo/bar.git'
172
+
173
+ pseudonym: 'a1b2c3d4'
174
+ ```
175
+
176
+ **Coverage notes:** Defensive sentinel — if either input is falsy, returns `'00000000'` so call sites never crash on missing inputs. Caller may check for the sentinel if it matters. The `:` separator between userId and repoOrigin prevents collisions between `userId='a' + repoOrigin='bcd'` and `userId='abc' + repoOrigin='d'`.
177
+
178
+ ## Consumed by
179
+
180
+ - **`scripts/lib/pseudonymize.cjs`** — implements R1..R8. The module's `RULES` manifest constant has a 1:1 correspondence with the sections above (R1..R8 ids match `RULES[i].id`).
181
+ - **Plan 30-02 (payload assembly)** — composes Phase 22 redaction + Phase 30 pseudonymization.
182
+ - **Plan 30-04 (consent prompt)** — uses the `replacements` log returned by `pseudonymize()` to display "X replacements made (R1: 3, R2: 5, ...)" summary before the user submits.
183
+ - **Plan 30-07 (`/gdd:update --show-privacy-diff`)** — diffs this document plus `pseudonymize.cjs` between installed and target versions of the plugin. Always-show on first run after upgrade that touched these files; opt-in afterward (CONTEXT D-09).
184
+
185
+ ## See also
186
+
187
+ - `scripts/lib/redact.cjs` (Phase 22) — secrets-stripping prerequisite layer in the payload pipeline.
188
+ - `.planning/phases/30-issue-reporter/CONTEXT.md` — D-01 pseudonymization-not-anonymization framing; D-13 synthetic-fixtures-plus-tmpdir test contract.
189
+ - `reference/registry.json` — registry entry for this document (`name: 'pseudonymization-rules'`, `phase: 30`, `type: 'meta-rules'`).
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./schemas/registry.schema.json",
3
3
  "version": 1,
4
- "generated_at": "2026-05-18T12:47:59.666Z",
4
+ "generated_at": "2026-05-20T09:53:00.000Z",
5
5
  "entries": [
6
6
  {
7
7
  "name": "codex-tools",
@@ -52,6 +52,13 @@
52
52
  "phase": 27,
53
53
  "description": "Phase 27 ACP + ASP protocol cheat sheet for peer-CLI delegation — line-delimited JSON-RPC framing, initialize/prompt/threadStart/turn lifecycle, per-peer ACP entry points, error-path resolution semantics"
54
54
  },
55
+ {
56
+ "name": "pseudonymization-rules",
57
+ "path": "reference/pseudonymization-rules.md",
58
+ "type": "meta-rules",
59
+ "phase": 30,
60
+ "description": "Phase 30 pseudonymization rule catalog (R1..R8) — replaces git identity, paths, hostname, repo origin, env-var values, email, IPs; defines stable per-user pseudonym derivation. Consumed by /gdd:update --show-privacy-diff at 30-07."
61
+ },
55
62
  {
56
63
  "name": "DEPRECATIONS",
57
64
  "path": "reference/DEPRECATIONS.md",
@@ -839,6 +846,20 @@
839
846
  "type": "meta-rules",
840
847
  "phase": 28.5,
841
848
  "description": "Phase 28.5 skill-authoring contract — adapted from mattpocock/skills (MIT). SKILL.md <=100-line cap (warn >=100, block >=250 in CI), 1024-char description cap, <what>. Use when <triggers>. form (lax-mode default per Phase 33 A/B pending), frontmatter required fields, progressive-disclosure one-level-deep rule. Validator at scripts/validate-skill-length.cjs."
849
+ },
850
+ {
851
+ "name": "capability-gap-stage-gate",
852
+ "path": "reference/capability-gap-stage-gate.md",
853
+ "type": "meta-rules",
854
+ "phase": 29,
855
+ "description": "Phase 29 Stage-0 → Stage-1 capability-gap gate specification. Defaults K=3 stable clusters across M=10 cycles with closed-form posterior stddev(Beta(α,β)) < 0.05 (Laplace prior matches Phase 23.5). D-01 lock: gate emits a one-time user-facing prompt on crossing — NEVER an auto-stage-flip. Overridable via .design/config.json capability_gap_gate.{K,M,stddev_threshold}. Test fixtures at tests/reflector-capability-gap-aggregation.test.cjs."
856
+ },
857
+ {
858
+ "name": "known-failure-modes",
859
+ "path": "reference/known-failure-modes.md",
860
+ "type": "meta-rules",
861
+ "phase": 30,
862
+ "description": "Phase 30 triage gate catalogue — locally-fixable failure modes (id/pattern/diagnosis/remedy/severity, with optional propose_report whitelist flag per D-11) consulted by scripts/lib/issue-reporter/triage-matcher.cjs before the report-issue consent prompt (D-07/D-11)."
842
863
  }
843
864
  ]
844
865
  }
@@ -10,7 +10,7 @@
10
10
  "type": {
11
11
  "type": "string",
12
12
  "minLength": 1,
13
- "description": "Free-form event type identifier. Pre-registered seeds: state.mutation, state.transition, stage.entered, stage.exited, hook.fired, error."
13
+ "description": "Free-form event type identifier. Pre-registered seeds: state.mutation, state.transition, stage.entered, stage.exited, hook.fired, error, capability_gap."
14
14
  },
15
15
  "timestamp": {
16
16
  "type": "string",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "payload": {
36
36
  "type": "object",
37
- "description": "Event-type-specific payload. Opaque at the envelope level."
37
+ "description": "Event-type-specific payload. Opaque at the envelope level. When type === 'capability_gap', the payload is narrowed by the conditional schema in allOf[0] to the 7-field CapabilityGapPayload (Phase 29 D-02)."
38
38
  },
39
39
  "_meta": {
40
40
  "type": "object",
@@ -51,5 +51,99 @@
51
51
  "type": "boolean",
52
52
  "description": "Writer-set flag indicating the payload exceeded maxLineBytes and has been replaced by a placeholder."
53
53
  }
54
- }
54
+ },
55
+ "definitions": {
56
+ "TrajectoryRef": {
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["trajectory_path", "byte_start", "byte_end", "content_hash"],
60
+ "properties": {
61
+ "trajectory_path": {
62
+ "type": "string",
63
+ "minLength": 1,
64
+ "description": "Repo-relative or absolute path to the trajectory-JSONL file containing the referenced evidence slice."
65
+ },
66
+ "byte_start": {
67
+ "type": "integer",
68
+ "minimum": 0,
69
+ "description": "Inclusive byte offset into the trajectory file marking the start of the evidence slice."
70
+ },
71
+ "byte_end": {
72
+ "type": "integer",
73
+ "minimum": 0,
74
+ "description": "Exclusive byte offset into the trajectory file marking the end of the evidence slice."
75
+ },
76
+ "content_hash": {
77
+ "type": "string",
78
+ "pattern": "^sha256:[0-9a-f]{64}$",
79
+ "description": "Hash-pinned content fingerprint (Phase 29 D-07). Detects retroactive chain mutation."
80
+ }
81
+ },
82
+ "description": "Pointer to a slice of a trajectory-JSONL file (Phase 29 D-07). Hash-pinned so consumers can detect chain mutation. Used inside CapabilityGapPayload.evidence_refs to keep payloads small while preserving audit-traceability — pointers, NOT duplicated trajectory content."
83
+ },
84
+ "CapabilityGapPayload": {
85
+ "type": "object",
86
+ "additionalProperties": false,
87
+ "required": [
88
+ "event_id",
89
+ "parent_event_id",
90
+ "source",
91
+ "context_hash",
92
+ "intent_summary",
93
+ "suggested_kind",
94
+ "evidence_refs"
95
+ ],
96
+ "properties": {
97
+ "event_id": {
98
+ "type": "string",
99
+ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
100
+ "description": "UUIDv4 identifying this capability_gap event. Stable across emit + read cycles."
101
+ },
102
+ "parent_event_id": {
103
+ "type": ["string", "null"],
104
+ "description": "UUIDv4 of the parent chain event when the gap was surfaced inside a larger trajectory; null when this is a root emit (e.g. /gdd:fast bail-out)."
105
+ },
106
+ "source": {
107
+ "type": "string",
108
+ "enum": ["fast", "router", "reflector_pattern"],
109
+ "description": "Which surface detected the gap. fast = /gdd:fast no-skill-match path; router = gdd-router unmatched-intent path; reflector_pattern = reflector pattern-detection pass (Plan 29-02)."
110
+ },
111
+ "context_hash": {
112
+ "type": "string",
113
+ "minLength": 1,
114
+ "description": "Stable hash of the intent/context that surfaced the gap. Used by 29-03 aggregation to cluster recurring gaps with the same hash."
115
+ },
116
+ "intent_summary": {
117
+ "type": "string",
118
+ "minLength": 1,
119
+ "maxLength": 256,
120
+ "description": "Short human-readable description of the failed lookup. Capped at 256 chars per D-02 — keeps payload small + auditable."
121
+ },
122
+ "suggested_kind": {
123
+ "type": "string",
124
+ "enum": ["agent", "skill"],
125
+ "description": "Drafter hint for 29-04 incubator-author. fast emits 'skill' (narrow primitive); router emits 'agent' (multi-step workflow); reflector_pattern may emit either."
126
+ },
127
+ "evidence_refs": {
128
+ "type": "array",
129
+ "items": { "$ref": "#/definitions/TrajectoryRef" },
130
+ "description": "Pointers to trajectory-JSONL slices that evidence the gap (D-07). Hash-pinned to detect chain mutation. Empty array allowed for low-evidence emits (e.g. /gdd:fast which has no trajectory)."
131
+ }
132
+ },
133
+ "description": "Phase 29 D-02 capability_gap payload — exactly 7 fields, additionalProperties: false. Validated when the envelope's type === 'capability_gap' via the allOf[0] conditional."
134
+ }
135
+ },
136
+ "allOf": [
137
+ {
138
+ "if": {
139
+ "properties": { "type": { "const": "capability_gap" } },
140
+ "required": ["type"]
141
+ },
142
+ "then": {
143
+ "properties": {
144
+ "payload": { "$ref": "#/definitions/CapabilityGapPayload" }
145
+ }
146
+ }
147
+ }
148
+ ]
55
149
  }