@hegemonart/get-design-done 1.28.8 → 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +81 -0
- package/README.de.md +23 -0
- package/README.fr.md +23 -0
- package/README.it.md +23 -0
- package/README.ja.md +23 -0
- package/README.ko.md +23 -0
- package/README.md +28 -0
- package/README.zh-CN.md +23 -0
- package/SKILL.md +2 -0
- package/agents/design-reflector.md +50 -0
- package/package.json +1 -1
- package/reference/capability-gap-stage-gate.md +261 -0
- package/reference/known-failure-modes.md +185 -0
- package/reference/pseudonymization-rules.md +189 -0
- package/reference/registry.json +22 -1
- package/reference/schemas/events.schema.json +97 -3
- package/reference/schemas/generated.d.ts +319 -4
- package/scripts/cli/gdd-events.mjs +35 -2
- package/scripts/gsd-cleanup-incubator.cjs +367 -0
- package/scripts/lib/apply-reflections/incubator-proposals.cjs +448 -0
- package/scripts/lib/bandit-router.cjs +92 -9
- package/scripts/lib/gsd-health-mirror/index.cjs +37 -1
- package/scripts/lib/incubator-author.cjs +845 -0
- package/scripts/lib/issue-reporter/cli-flag-report.cjs +153 -0
- package/scripts/lib/issue-reporter/consent-prompt.cjs +231 -0
- package/scripts/lib/issue-reporter/dedup.cjs +458 -0
- package/scripts/lib/issue-reporter/destination.cjs +37 -0
- package/scripts/lib/issue-reporter/draft-writer.cjs +157 -0
- package/scripts/lib/issue-reporter/gh-absent-fallback.cjs +220 -0
- package/scripts/lib/issue-reporter/gh-submit.cjs +114 -0
- package/scripts/lib/issue-reporter/kill-switch.cjs +122 -0
- package/scripts/lib/issue-reporter/payload-assembly.cjs +367 -0
- package/scripts/lib/issue-reporter/privacy-diff.cjs +385 -0
- package/scripts/lib/issue-reporter/report-flow.cjs +269 -0
- package/scripts/lib/issue-reporter/triage-matcher.cjs +270 -0
- package/scripts/lib/pseudonymize.cjs +444 -0
- package/scripts/lib/reflections-cycle-writer.cjs +172 -0
- package/scripts/lib/reflector/capability-gap-scan.cjs +751 -0
- package/scripts/lib/reflector-capability-gap-aggregator.cjs +320 -0
- package/scripts/release-smoke-test.cjs +33 -2
- package/scripts/validate-incubator-scope.cjs +133 -0
- package/skills/apply-reflections/SKILL.md +16 -1
- package/skills/apply-reflections/apply-reflections-procedure.md +71 -3
- package/skills/fast/SKILL.md +46 -0
- package/skills/reflect/SKILL.md +9 -0
- package/skills/reflect/procedures/capability-gap-scan.md +120 -0
- package/skills/report-issue/SKILL.md +53 -0
- package/skills/report-issue/report-issue-procedure.md +120 -0
- package/skills/router/SKILL.md +5 -0
- package/skills/router/capability-gap-emitter.md +65 -0
- package/skills/update/SKILL.md +3 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Capability-Gap Stage-0 → Stage-1 Gate Specification
|
|
2
|
+
|
|
3
|
+
> Phase 29 reference doc. Specifies the deterministic gate that decides when
|
|
4
|
+
> the reflector has gathered enough `capability_gap` signal to surface a
|
|
5
|
+
> one-time opt-in prompt for Stage-1 (incubator authoring of new agents /
|
|
6
|
+
> skills). **No code path in this repository auto-flips the stage** —
|
|
7
|
+
> D-01 is the discipline.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Overview
|
|
12
|
+
|
|
13
|
+
Phase 29 ships in two stages:
|
|
14
|
+
|
|
15
|
+
- **Stage 0** (Wave A, v1.29.0) emits `capability_gap` events from
|
|
16
|
+
`/gdd:fast` no-skill-match (Plan 29-01), `gdd-router` unmatched-intent
|
|
17
|
+
(Plan 29-01), and the reflector pattern-detection pass (Plan 29-02).
|
|
18
|
+
- **Stage 1** (Wave B) layers incubator authoring of agents and skills on
|
|
19
|
+
top of the Stage-0 signal (Plans 29-04 / 29-05 / 29-06).
|
|
20
|
+
|
|
21
|
+
The transition Stage 0 → Stage 1 is **gated on data**, not on a calendar
|
|
22
|
+
date or a release. The reflector aggregates events into per-cycle
|
|
23
|
+
clusters (`scripts/lib/reflector-capability-gap-aggregator.cjs`) and
|
|
24
|
+
evaluates a deterministic stability function against the project's
|
|
25
|
+
cycle history. When the gate is crossed, `/gdd:apply-reflections`
|
|
26
|
+
emits a **one-time user-facing prompt** in the cycle markdown — never
|
|
27
|
+
an auto-stage-flip. The user opting in is a separate explicit action,
|
|
28
|
+
out of scope for this gate spec.
|
|
29
|
+
|
|
30
|
+
Reference: **Phase 29 CONTEXT.md decision D-01** (two-stage approach;
|
|
31
|
+
user opts in per a one-time prompt; if data is thin, Stage 1 never
|
|
32
|
+
auto-enables).
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Default thresholds
|
|
37
|
+
|
|
38
|
+
The gate has three knobs, all set in `.design/config.json`
|
|
39
|
+
under the `capability_gap_gate` key. Defaults:
|
|
40
|
+
|
|
41
|
+
| Knob | Default | Meaning |
|
|
42
|
+
|------|---------|---------|
|
|
43
|
+
| `K` | `3` | Minimum number of **stable clusters** required to cross the gate. |
|
|
44
|
+
| `M` | `10` | Minimum number of **consecutive cycles** a cluster must appear in to be considered for stability. |
|
|
45
|
+
| `stddev_threshold` | `0.05` | Maximum allowed posterior `stddev(Beta(α, β))` for a cluster to be considered stable. |
|
|
46
|
+
|
|
47
|
+
All three are overridable via `.design/config.json`:
|
|
48
|
+
|
|
49
|
+
```jsonc
|
|
50
|
+
{
|
|
51
|
+
"capability_gap_gate": {
|
|
52
|
+
"K": 3,
|
|
53
|
+
"M": 10,
|
|
54
|
+
"stddev_threshold": 0.05
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Reference: **Phase 29 CONTEXT.md decision D-03** (defaults are starting
|
|
60
|
+
points; first-N-users data should refine).
|
|
61
|
+
|
|
62
|
+
Validation: `K` must be a positive integer, `M` a positive integer,
|
|
63
|
+
`stddev_threshold` a number in `(0, 1]`. The evaluator silently falls
|
|
64
|
+
back to defaults if any value is invalid (T-29.03-02 mitigation in the
|
|
65
|
+
plan's threat model).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Stability formula
|
|
70
|
+
|
|
71
|
+
A cluster `c` is **stable** iff both conditions hold:
|
|
72
|
+
|
|
73
|
+
1. **Consecutive presence.** `c` appears in `≥ M` consecutive cycles
|
|
74
|
+
somewhere within the observed history. (The most recent unbroken
|
|
75
|
+
run is what matters — if a cluster missed a cycle, the run resets
|
|
76
|
+
and only the longest streak counts.)
|
|
77
|
+
2. **Narrow posterior.** The closed-form posterior standard deviation
|
|
78
|
+
of the Beta distribution satisfies:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
stddev(Beta(α, β)) = sqrt( (α · β) / ((α + β)² · (α + β + 1)) )
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
with the Laplace prior (matches Phase 23.5's bandit-router posterior
|
|
85
|
+
store):
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
α = appearances + 1
|
|
89
|
+
β = (cycles_observed − appearances) + 1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
where `appearances` is the total number of cycles in which `c` was
|
|
93
|
+
present (across the full history, not just the M-window) and
|
|
94
|
+
`cycles_observed = history.length`.
|
|
95
|
+
|
|
96
|
+
**Worked example.** A cluster present in 25 of the 30 observed cycles
|
|
97
|
+
with a longest consecutive run of 18 has `α = 26`, `β = 6`, so
|
|
98
|
+
`stddev ≈ sqrt((26·6)/(32²·33)) ≈ sqrt(0.0154) ≈ 0.124`. That fails
|
|
99
|
+
the default `stddev_threshold = 0.05`. To clear `0.05`, that same
|
|
100
|
+
cluster would need closer to 30/30 presence or a longer history.
|
|
101
|
+
|
|
102
|
+
**Cross-link.** The closed-form Beta-stddev is shared with Phase 23.5
|
|
103
|
+
(see `scripts/lib/bandit-arbitrage.cjs` and the
|
|
104
|
+
"Bandit-arbitrage analysis" section in `agents/design-reflector.md`).
|
|
105
|
+
The same posterior is what gates frontmatter-tier corrections; here it
|
|
106
|
+
gates stage transition.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 4. Evaluation cadence
|
|
111
|
+
|
|
112
|
+
Gate evaluation runs every time `/gdd:apply-reflections` is invoked.
|
|
113
|
+
Inputs:
|
|
114
|
+
|
|
115
|
+
1. The reflector pass collects all cycle markdown files in
|
|
116
|
+
`.design/reflections/` and parses the `## Capability gaps observed`
|
|
117
|
+
sections (emitted by `renderGapsSection` in
|
|
118
|
+
`scripts/lib/reflector-capability-gap-aggregator.cjs`).
|
|
119
|
+
2. The per-cycle cluster lists are folded into a history array of
|
|
120
|
+
`{ cycle_slug, clusters }` entries.
|
|
121
|
+
3. `evaluateStageGate(history, config)` is called once and returns:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
{ crossed: boolean, stable_cluster_ids: string[], cycles_observed: number }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
4. If `crossed === true` and the project has not previously opted in
|
|
128
|
+
(see § 6), the prompt in § 5 is emitted into the cycle markdown.
|
|
129
|
+
|
|
130
|
+
The evaluation is **deterministic** (no randomness), **idempotent**
|
|
131
|
+
(no side-effects in the evaluator), and **read-only** with respect to
|
|
132
|
+
`.design/config.json` — that file is only updated by the user's
|
|
133
|
+
explicit opt-in action, never by the reflector.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 5. Gate-crossed prompt
|
|
138
|
+
|
|
139
|
+
When the gate crosses for the first time, `/gdd:apply-reflections`
|
|
140
|
+
appends the following verbatim block to the cycle markdown:
|
|
141
|
+
|
|
142
|
+
> ```markdown
|
|
143
|
+
> ## Stage-0 → Stage-1 gate crossed — opt-in required
|
|
144
|
+
>
|
|
145
|
+
> Capability-gap detection has accumulated enough signal across recent
|
|
146
|
+
> cycles to consider enabling Stage-1 (incubator authoring of new
|
|
147
|
+
> agents / skills). The gate is informational only — **nothing has
|
|
148
|
+
> changed in the runtime**, and Stage-1 will NOT auto-enable. Per
|
|
149
|
+
> Phase 29 CONTEXT.md decision D-01, the user opts in explicitly.
|
|
150
|
+
>
|
|
151
|
+
> - Stable clusters observed: **<N>** (≥K = <K>)
|
|
152
|
+
> - Cycles observed: **<cycles_observed>** (≥M = <M>)
|
|
153
|
+
> - Stable cluster IDs (truncated):
|
|
154
|
+
> - `<cluster_id_1>`
|
|
155
|
+
> - `<cluster_id_2>`
|
|
156
|
+
> - `<cluster_id_3>`
|
|
157
|
+
>
|
|
158
|
+
> If you want to enable Stage-1 incubator authoring (Plans 29-04 / 29-05),
|
|
159
|
+
> opt in with the project-local command below. You can always opt out
|
|
160
|
+
> later by deleting the timestamps from `.design/config.json` (§ 7).
|
|
161
|
+
>
|
|
162
|
+
> <!-- TODO: confirm opt-in command — likely
|
|
163
|
+
> `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config set capability_gap_gate.opted_in_at "$(date -Iseconds)"`
|
|
164
|
+
> or a project-local equivalent. Plan 29-05 (apply-reflections
|
|
165
|
+
> extension) will land the canonical command. -->
|
|
166
|
+
>
|
|
167
|
+
> This prompt is emitted at most once per project. If you ignore it,
|
|
168
|
+
> the gate continues to evaluate every cycle but does not re-prompt
|
|
169
|
+
> (the `user_prompted_at` timestamp in `.design/config.json` suppresses
|
|
170
|
+
> it). To re-trigger the prompt for a fresh round of evaluation, delete
|
|
171
|
+
> `capability_gap_gate.user_prompted_at` from `.design/config.json`.
|
|
172
|
+
> ```
|
|
173
|
+
|
|
174
|
+
The wiring side of this — actually writing the `user_prompted_at`
|
|
175
|
+
timestamp and routing the opt-in confirmation — is deferred to
|
|
176
|
+
**Plan 29-05** (`/gdd:apply-reflections` extension). This document
|
|
177
|
+
specifies the prompt text and behavior; 29-05 implements the
|
|
178
|
+
state-machine that consumes it.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 6. Opt-in semantics
|
|
183
|
+
|
|
184
|
+
Two timestamps in `.design/config.json` track the project's gate state:
|
|
185
|
+
|
|
186
|
+
```jsonc
|
|
187
|
+
{
|
|
188
|
+
"capability_gap_gate": {
|
|
189
|
+
"K": 3,
|
|
190
|
+
"M": 10,
|
|
191
|
+
"stddev_threshold": 0.05,
|
|
192
|
+
"user_prompted_at": "2026-05-19T22:00:00.000Z", // set when § 5 emitted
|
|
193
|
+
"opted_in_at": "2026-05-19T22:05:00.000Z" // set when user opts in
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- **`user_prompted_at`** is set the first time the gate crosses and the
|
|
199
|
+
prompt block is rendered. The prompt is not re-emitted while this
|
|
200
|
+
timestamp is present.
|
|
201
|
+
- **`opted_in_at`** is set when the user explicitly opts into Stage-1.
|
|
202
|
+
Stage-1 incubator authoring (Plans 29-04+) becomes active once this
|
|
203
|
+
timestamp is present. **Stage 1 is NEVER enabled by the reflector
|
|
204
|
+
setting this timestamp itself** — D-01 lock.
|
|
205
|
+
|
|
206
|
+
Once `opted_in_at` is set, the gate stops emitting prompts entirely
|
|
207
|
+
(it's a one-shot mechanism, not a continuous nudge).
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 7. Reset / override
|
|
212
|
+
|
|
213
|
+
Operators can manually reset the gate by editing `.design/config.json`:
|
|
214
|
+
|
|
215
|
+
| Effect desired | Action |
|
|
216
|
+
|----------------|--------|
|
|
217
|
+
| Re-prompt on next gate cross | Delete `capability_gap_gate.user_prompted_at`. |
|
|
218
|
+
| Revert from Stage-1 to Stage-0 | Delete `capability_gap_gate.opted_in_at`. (Stage-1 artifacts in `.design/reflections/incubator/` are NOT removed by this; deletion is the user's call.) |
|
|
219
|
+
| Tighten / loosen thresholds | Edit `K` / `M` / `stddev_threshold` directly. Out-of-range values silently fall back to defaults (§ 2). |
|
|
220
|
+
|
|
221
|
+
Reset is **explicit** and **idempotent**. The reflector never writes
|
|
222
|
+
to these fields on its own — the only writers are (a) the
|
|
223
|
+
`/gdd:apply-reflections` opt-in path (Plan 29-05) and (b) the human
|
|
224
|
+
operator editing the file by hand.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 8. Test fixtures
|
|
229
|
+
|
|
230
|
+
Executable examples that exercise the gate live in
|
|
231
|
+
`tests/reflector-capability-gap-aggregation.test.cjs`:
|
|
232
|
+
|
|
233
|
+
- **T3** — 30 cycles × 3 always-present clusters → gate crosses with
|
|
234
|
+
default K=3 / M=10 / stddev_threshold=0.05.
|
|
235
|
+
- **T3b** — 10 cycles × 1 always-present cluster → gate does NOT
|
|
236
|
+
cross (posterior stddev ≈ 0.077 with α=11, β=1 is above the 0.05
|
|
237
|
+
threshold; M=10 is the lower bound on observations, not a
|
|
238
|
+
sufficient condition for stability).
|
|
239
|
+
- **T4** — 30 cycles, 2 always-present clusters + 1 "noisy" cluster
|
|
240
|
+
present in only the first 4 cycles → gate does NOT cross
|
|
241
|
+
(`stable_cluster_ids.length === 2 < K=3`).
|
|
242
|
+
- **T4b** — 5 cycles total → gate does NOT cross (`cycles_observed < M`).
|
|
243
|
+
- **T7** — Confirms `K` and `stddev_threshold` overrides flow through
|
|
244
|
+
`normalizeConfig` and reach the evaluation.
|
|
245
|
+
|
|
246
|
+
These fixtures are synthetic and inline (D-11). The gate evaluator
|
|
247
|
+
never reads `.design/gep/events.jsonl` directly in CI — fixtures
|
|
248
|
+
seed the cluster lists by hand.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Decisions referenced
|
|
253
|
+
|
|
254
|
+
- **D-01** — Two-stage approach: Stage 0 telemetry-only ships first;
|
|
255
|
+
Stage 1 authoring gated on data; user opts in per a one-time prompt;
|
|
256
|
+
no auto-flip.
|
|
257
|
+
- **D-03** — Default `K=3` / `M=10` / `stddev_threshold=0.05`,
|
|
258
|
+
overridable via `.design/config.json`.
|
|
259
|
+
- **D-11** — Tests use synthetic fixtures (no live event chain reads).
|
|
260
|
+
- **Phase 23.5** — Posterior `stddev(Beta(α, β))` closed form and
|
|
261
|
+
Laplace prior convention reused here.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Known Failure Modes (Phase 30 Triage Gate)
|
|
2
|
+
|
|
3
|
+
This file is the **catalogue of locally-fixable failure modes** that the
|
|
4
|
+
Phase 30 issue-reporter consults *before* prompting the user to file a
|
|
5
|
+
GitHub issue. The triage gate runs first (D-07): when an entry matches
|
|
6
|
+
the user's error, the gate surfaces `this looks like X — try Y` and
|
|
7
|
+
exits the report flow without prompting. `--force-report` bypasses the
|
|
8
|
+
gate but still requires consent (D-11).
|
|
9
|
+
|
|
10
|
+
## Schema
|
|
11
|
+
|
|
12
|
+
Each entry is a single fenced ```yaml block with this flat key:value shape:
|
|
13
|
+
|
|
14
|
+
- `id` — stable identifier (kebab-case or `KFM-NNN` numeric). Required.
|
|
15
|
+
- `pattern` — JavaScript regex string. Matched against
|
|
16
|
+
`[error.message, error.stack].filter(Boolean).join("\n")`. Required.
|
|
17
|
+
- `diagnosis` — one-sentence plain-English root cause. Required.
|
|
18
|
+
- `remedy` — one-sentence user-runnable action. Required.
|
|
19
|
+
- `severity` — advisory only, one of `low` / `medium` / `high`. Required.
|
|
20
|
+
- `propose_report` — boolean. If `true`, this mode is on the D-11
|
|
21
|
+
whitelist: 30-04 may *propose* `--report` at error time for this
|
|
22
|
+
class. Defaults to `false`. Advisory; the matcher does not act on it.
|
|
23
|
+
|
|
24
|
+
## Matching policy
|
|
25
|
+
|
|
26
|
+
- **First match wins.** Entries are evaluated in file order. The matcher
|
|
27
|
+
returns the first entry whose regex tests true against the haystack.
|
|
28
|
+
No severity ranking, no aggregation, no blending.
|
|
29
|
+
- **Invalid regex is non-fatal.** An entry whose `pattern` fails to
|
|
30
|
+
compile is skipped with a `console.warn`, never crashes the matcher.
|
|
31
|
+
- **Missing catalogue is non-fatal.** If this file is absent or
|
|
32
|
+
unparseable, `matchKnownFailure` returns `{ matched: false }` and
|
|
33
|
+
warns once.
|
|
34
|
+
|
|
35
|
+
Consumed by `scripts/lib/issue-reporter/triage-matcher.cjs`
|
|
36
|
+
(`matchKnownFailure(errorContext)`). Wired into `skills/report-issue`
|
|
37
|
+
(Plan 30-04) before the consent prompt.
|
|
38
|
+
|
|
39
|
+
## Entries
|
|
40
|
+
|
|
41
|
+
### KFM-001 — EACCES on `.design/` write
|
|
42
|
+
|
|
43
|
+
Permission failure when the plugin writes into `.design/`. Common after
|
|
44
|
+
`sudo`-cloning a repo or running CI as a user without write access to
|
|
45
|
+
the project root.
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
id: KFM-001
|
|
49
|
+
pattern: 'EACCES.*\.design'
|
|
50
|
+
diagnosis: 'Permission denied writing to .design/ — the plugin cannot persist its work-product directory.'
|
|
51
|
+
remedy: 'Run `chown -R "$USER" .design` (or recreate the directory as your normal user) and retry the command.'
|
|
52
|
+
severity: medium
|
|
53
|
+
propose_report: false
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### KFM-002 — `gh` CLI not on PATH
|
|
57
|
+
|
|
58
|
+
The Phase 30 outbound submission path requires the user's `gh` CLI
|
|
59
|
+
(D-05). If it's missing, Plan 30-06 falls back to clipboard + URL.
|
|
60
|
+
This entry catches the typical shell-spawn error.
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
id: KFM-002
|
|
64
|
+
pattern: '(gh: command not found|spawn gh ENOENT|''gh'' is not recognized)'
|
|
65
|
+
diagnosis: 'GitHub CLI (`gh`) is not installed or not on PATH; the issue reporter''s outbound path relies on it.'
|
|
66
|
+
remedy: 'Install gh from https://cli.github.com and run `gh auth login`, then retry. (Or use the clipboard fallback: the payload is already on disk under .design/issue-drafts/.)'
|
|
67
|
+
severity: low
|
|
68
|
+
propose_report: false
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### KFM-003 — Node.js version mismatch
|
|
72
|
+
|
|
73
|
+
`package.json` declares `engines.node: ">=22"`. Older Node versions
|
|
74
|
+
crash on the `--experimental-strip-types` test runner, or fail subtle
|
|
75
|
+
TypeScript-import behaviour.
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
id: KFM-003
|
|
79
|
+
pattern: '(engine "node" is incompatible|Unsupported engine|SyntaxError.*Unexpected token.*satisfies|--experimental-strip-types)'
|
|
80
|
+
diagnosis: 'Active Node.js version is below the plugin''s required >=22; modern syntax features and the strip-types test runner are unavailable.'
|
|
81
|
+
remedy: 'Upgrade Node to >=22 (e.g. `nvm install 22 && nvm use 22`) and rerun.'
|
|
82
|
+
severity: high
|
|
83
|
+
propose_report: false
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### KFM-004 — Figma token missing
|
|
87
|
+
|
|
88
|
+
Figma-aware flows expect `FIGMA_TOKEN` (or the documented env-var alias)
|
|
89
|
+
to be present. The 401/missing-env error class is recognisable.
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
id: KFM-004
|
|
93
|
+
pattern: '(FIGMA_TOKEN.*(not set|missing|undefined)|Figma.*401|figma.*unauthor)'
|
|
94
|
+
diagnosis: 'FIGMA_TOKEN environment variable is missing or invalid; Figma-dependent commands cannot authenticate.'
|
|
95
|
+
remedy: 'Generate a personal access token at https://www.figma.com/developers/api#access-tokens and `export FIGMA_TOKEN=<token>` in your shell profile.'
|
|
96
|
+
severity: medium
|
|
97
|
+
propose_report: false
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### KFM-005 — Git working tree dirty
|
|
101
|
+
|
|
102
|
+
Several phase-tooling commands assume a clean working tree (clean
|
|
103
|
+
checkpoints between cycles). A dirty tree surfaces as a stderr line
|
|
104
|
+
the matcher can recognise.
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
id: KFM-005
|
|
108
|
+
pattern: '(working tree (is )?(not clean|dirty)|uncommitted changes|Changes not staged for commit)'
|
|
109
|
+
diagnosis: 'Git working tree has uncommitted changes; the command requires a clean checkpoint before proceeding.'
|
|
110
|
+
remedy: 'Commit, stash (`git stash -u`), or discard your local changes, then rerun the command.'
|
|
111
|
+
severity: low
|
|
112
|
+
propose_report: false
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### KFM-006 — `.planning/` directory missing
|
|
116
|
+
|
|
117
|
+
GSD/GDD project commands assume `.planning/` has been initialised by
|
|
118
|
+
`/gsd:new-project`. A bare ENOENT on that path is a clear self-fix.
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
id: KFM-006
|
|
122
|
+
pattern: 'ENOENT.*\.planning'
|
|
123
|
+
diagnosis: '.planning/ directory does not exist; the project has not been initialised yet.'
|
|
124
|
+
remedy: 'Run `/gsd:new-project` to bootstrap the planning structure, then retry.'
|
|
125
|
+
severity: medium
|
|
126
|
+
propose_report: false
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### KFM-007 — `reference/registry.json` invalid JSON
|
|
130
|
+
|
|
131
|
+
The Phase 14.5 registry is hand-edited; a stray trailing comma or
|
|
132
|
+
unquoted key surfaces here. Self-fixable, not a maintainer issue.
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
id: KFM-007
|
|
136
|
+
pattern: '(reference/registry\.json.*(SyntaxError|JSON|Unexpected token)|Unexpected token.*registry\.json)'
|
|
137
|
+
diagnosis: 'reference/registry.json failed to parse as JSON — likely a trailing comma or unbalanced brace from a recent edit.'
|
|
138
|
+
remedy: 'Open reference/registry.json in your editor; the JSON parser error message will pinpoint the line. Fix the syntax and retry.'
|
|
139
|
+
severity: medium
|
|
140
|
+
propose_report: false
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### KFM-008 — MCP server unreachable
|
|
144
|
+
|
|
145
|
+
When the Figma / GDD MCP servers are not running, commands depending
|
|
146
|
+
on them fail with a clear connection-refused class of error.
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
id: KFM-008
|
|
150
|
+
pattern: '(MCP.*(unreachable|ECONNREFUSED|not connected)|mcp.*server.*not.*running|connection refused.*ws://)'
|
|
151
|
+
diagnosis: 'An MCP server (Figma, GDD-state, or GDD-tools) is not reachable; the plugin cannot route tool calls through it.'
|
|
152
|
+
remedy: 'Start the relevant MCP server (see scripts/mcp-servers/) and confirm `claude mcp list` shows it as connected.'
|
|
153
|
+
severity: medium
|
|
154
|
+
propose_report: true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### KFM-009 — Plugin file accidentally deleted
|
|
158
|
+
|
|
159
|
+
A user-side `git clean -fdx` or aggressive editor refactor can remove
|
|
160
|
+
plugin files. This is a re-install path, not a bug report path — but
|
|
161
|
+
it's on the whitelist because users typically can't tell it apart from
|
|
162
|
+
an upstream regression.
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
id: KFM-009
|
|
166
|
+
pattern: 'Cannot find module.*(scripts/lib/|skills/.*SKILL\.md|reference/.*\.md)'
|
|
167
|
+
diagnosis: 'A plugin file is missing — most often the result of a local `git clean` or a partial install.'
|
|
168
|
+
remedy: 'Reinstall the plugin: `npm install -g @hegemonart/get-design-done` (or pull the repo fresh in dev). If the file should exist, the error message gives its path.'
|
|
169
|
+
severity: medium
|
|
170
|
+
propose_report: true
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### KFM-010 — Disk full / ENOSPC
|
|
174
|
+
|
|
175
|
+
Out-of-space failures masquerade as obscure write errors. Self-fixable
|
|
176
|
+
by freeing space; not a maintainer report path.
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
id: KFM-010
|
|
180
|
+
pattern: '(ENOSPC|no space left on device|disk full)'
|
|
181
|
+
diagnosis: 'Disk is full — no space left on the device the plugin is writing to.'
|
|
182
|
+
remedy: 'Free space (e.g. clear `.design/cache/`, prune old worktrees, empty trash) and retry.'
|
|
183
|
+
severity: high
|
|
184
|
+
propose_report: false
|
|
185
|
+
```
|
|
@@ -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'`).
|
package/reference/registry.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./schemas/registry.schema.json",
|
|
3
3
|
"version": 1,
|
|
4
|
-
"generated_at": "2026-05-
|
|
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
|
}
|