@hegemonart/get-design-done 1.28.8 → 1.30.5
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 +116 -0
- package/README.de.md +25 -0
- package/README.fr.md +25 -0
- package/README.it.md +25 -0
- package/README.ja.md +25 -0
- package/README.ko.md +25 -0
- package/README.md +30 -0
- package/README.zh-CN.md +25 -0
- package/SKILL.md +2 -0
- package/agents/design-authority-watcher.md +42 -1
- 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 +521 -0
- package/reference/pseudonymization-rules.md +189 -0
- package/reference/registry.json +22 -1
- package/reference/schemas/events.schema.json +158 -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 +455 -0
- package/scripts/lib/authority-watcher/index.cjs +201 -0
- package/scripts/lib/bandit-router.cjs +92 -9
- package/scripts/lib/failure-mode-matcher.cjs +460 -0
- package/scripts/lib/gsd-health-mirror/index.cjs +37 -1
- package/scripts/lib/incubator-author.cjs +845 -0
- package/scripts/lib/install/interactive.cjs +27 -2
- 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 +352 -0
- package/scripts/lib/reflector-kfm-proposer.cjs +468 -0
- package/scripts/release-smoke-test.cjs +33 -2
- package/scripts/validate-incubator-scope.cjs +133 -0
- package/skills/apply-reflections/SKILL.md +20 -1
- package/skills/apply-reflections/apply-reflections-procedure.md +106 -4
- 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,521 @@
|
|
|
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
|
+
### Schema v1 (Phase 30, matcher-consumed) — original 6 fields
|
|
15
|
+
|
|
16
|
+
These six fields are consumed by `scripts/lib/issue-reporter/triage-matcher.cjs`
|
|
17
|
+
(`matchKnownFailure(errorContext)`). The matcher reads ONLY these fields and
|
|
18
|
+
ignores everything else gracefully (D-04 backward-compat).
|
|
19
|
+
|
|
20
|
+
- `id` — stable identifier (kebab-case or `KFM-NNN` numeric). Required.
|
|
21
|
+
- `pattern` — JavaScript regex string. Matched against
|
|
22
|
+
`[error.message, error.stack].filter(Boolean).join("\n")`. Required.
|
|
23
|
+
- `diagnosis` — one-sentence plain-English root cause. Required.
|
|
24
|
+
- `remedy` — one-sentence user-runnable action. Required.
|
|
25
|
+
- `severity` — advisory only, one of `low` / `medium` / `high`. Required.
|
|
26
|
+
- `propose_report` — boolean. If `true`, this mode is on the D-11
|
|
27
|
+
whitelist: 30-04 may *propose* `--report` at error time for this
|
|
28
|
+
class. Defaults to `false`. Advisory; the matcher does not act on it.
|
|
29
|
+
|
|
30
|
+
### Schema v2 (Phase 30.5 D-02) — additive fields
|
|
31
|
+
|
|
32
|
+
These five fields are required on every entry from Phase 30.5 onward.
|
|
33
|
+
They are NOT consumed by the Phase 30 matcher (D-04 backward-compat
|
|
34
|
+
invariant); they exist for human authors, retrospective harvesting, and
|
|
35
|
+
future tooling (e.g. the Phase 30.5-02 fuzzy matcher, the Phase 30.5-03
|
|
36
|
+
reflector incubator). Adding them does not change the matcher's
|
|
37
|
+
behaviour for the original 6 fields.
|
|
38
|
+
|
|
39
|
+
- `symptom` — string, 1–3 sentences. Plain-English description of what
|
|
40
|
+
the user sees when this failure mode hits. Required.
|
|
41
|
+
*Example:* `'Build fails with EUSAGE about a missing or stale lockfile after a package.json edit.'`
|
|
42
|
+
- `root_cause` — string, 1–2 sentences. Technical explanation of why
|
|
43
|
+
the failure happens. Required.
|
|
44
|
+
*Example:* `'npm ci enforces lockfile parity with package.json; manually editing one without the other breaks parity.'`
|
|
45
|
+
- `fix` — string (single line; multi-step encoded as `1) … 2) … 3) …`).
|
|
46
|
+
Step-by-step user-runnable remedy. The original `remedy` field stays
|
|
47
|
+
as the short matcher-consumed one-liner; `fix` is the fuller version
|
|
48
|
+
with prerequisites and verification steps. The two MAY differ. Required.
|
|
49
|
+
*Example:* `1) Run npm install once locally. 2) Stage the updated package-lock.json. 3) Commit and re-run npm ci.`
|
|
50
|
+
- `related_phases` — number[] (YAML flow style: `[12, 24]`). Phase numbers
|
|
51
|
+
this mode touches. Empty array `[]` is allowed when the mode is
|
|
52
|
+
cross-cutting and not tied to a specific phase. Required.
|
|
53
|
+
*Example:* `[12, 14.6, 24]`
|
|
54
|
+
- `first_observed_cycle` — string. Cycle slug like `cycle-2026-05`, or
|
|
55
|
+
`pre-30.5` for entries harvested from before this catalogue formalised
|
|
56
|
+
the schema. Required.
|
|
57
|
+
*Example:* `'cycle-2026-05'`
|
|
58
|
+
|
|
59
|
+
The Phase 30 matcher (`scripts/lib/issue-reporter/triage-matcher.cjs`)
|
|
60
|
+
consumes ONLY the original 6 fields per D-04. The 5 additive fields are
|
|
61
|
+
for human authors, retrospective harvesting, and future tooling.
|
|
62
|
+
|
|
63
|
+
## Matching policy
|
|
64
|
+
|
|
65
|
+
- **First match wins.** Entries are evaluated in file order. The matcher
|
|
66
|
+
returns the first entry whose regex tests true against the haystack.
|
|
67
|
+
No severity ranking, no aggregation, no blending.
|
|
68
|
+
- **Invalid regex is non-fatal.** An entry whose `pattern` fails to
|
|
69
|
+
compile is skipped with a `console.warn`, never crashes the matcher.
|
|
70
|
+
- **Missing catalogue is non-fatal.** If this file is absent or
|
|
71
|
+
unparseable, `matchKnownFailure` returns `{ matched: false }` and
|
|
72
|
+
warns once.
|
|
73
|
+
|
|
74
|
+
Consumed by `scripts/lib/issue-reporter/triage-matcher.cjs`
|
|
75
|
+
(`matchKnownFailure(errorContext)`). Wired into `skills/report-issue`
|
|
76
|
+
(Plan 30-04) before the consent prompt.
|
|
77
|
+
|
|
78
|
+
## Entries
|
|
79
|
+
|
|
80
|
+
### KFM-001 — EACCES on `.design/` write
|
|
81
|
+
|
|
82
|
+
Permission failure when the plugin writes into `.design/`. Common after
|
|
83
|
+
`sudo`-cloning a repo or running CI as a user without write access to
|
|
84
|
+
the project root.
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
id: KFM-001
|
|
88
|
+
pattern: 'EACCES.*\.design'
|
|
89
|
+
diagnosis: 'Permission denied writing to .design/ — the plugin cannot persist its work-product directory.'
|
|
90
|
+
remedy: 'Run `chown -R "$USER" .design` (or recreate the directory as your normal user) and retry the command.'
|
|
91
|
+
severity: medium
|
|
92
|
+
propose_report: false
|
|
93
|
+
symptom: 'Commands that write into `.design/` (state snapshots, reflection drafts, issue drafts) fail with `EACCES: permission denied`. The plugin cannot persist its work-product directory.'
|
|
94
|
+
root_cause: 'The `.design/` directory was created by a different user (often root after a `sudo`-clone) or its permissions were tightened so the current user lacks write access.'
|
|
95
|
+
fix: '1) Identify the owner with `ls -ld .design` and `id -u`. 2) Run `sudo chown -R "$USER" .design` to reclaim ownership. 3) Alternatively, `rm -rf .design && mkdir .design` if the directory contains no state you need. 4) Re-run the command that failed.'
|
|
96
|
+
related_phases: [11, 22, 29]
|
|
97
|
+
first_observed_cycle: 'pre-30.5'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### KFM-002 — `gh` CLI not on PATH
|
|
101
|
+
|
|
102
|
+
The Phase 30 outbound submission path requires the user's `gh` CLI
|
|
103
|
+
(D-05). If it's missing, Plan 30-06 falls back to clipboard + URL.
|
|
104
|
+
This entry catches the typical shell-spawn error.
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
id: KFM-002
|
|
108
|
+
pattern: '(gh: command not found|spawn gh ENOENT|''gh'' is not recognized)'
|
|
109
|
+
diagnosis: 'GitHub CLI (`gh`) is not installed or not on PATH; the issue reporter''s outbound path relies on it.'
|
|
110
|
+
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/.)'
|
|
111
|
+
severity: low
|
|
112
|
+
propose_report: false
|
|
113
|
+
symptom: 'The `/gdd:report-issue` flow exits during the outbound submission step with `gh: command not found`, `spawn gh ENOENT`, or (on Windows) `''gh'' is not recognized as an internal or external command`.'
|
|
114
|
+
root_cause: 'Phase 30 D-05 routes the outbound submission through the user''s `gh` CLI; when `gh` is absent or off `PATH`, the spawn fails before any network call.'
|
|
115
|
+
fix: '1) Install `gh` from https://cli.github.com (`brew install gh`, `winget install GitHub.cli`, or distro package). 2) Run `gh auth login` and pick GitHub.com + your preferred protocol. 3) Verify with `gh auth status`. 4) Re-run `/gdd:report-issue`. As a fallback, the issue draft is already saved under `.design/issue-drafts/` — open it and file manually via the GitHub web UI.'
|
|
116
|
+
related_phases: [30]
|
|
117
|
+
first_observed_cycle: 'pre-30.5'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### KFM-003 — Node.js version mismatch
|
|
121
|
+
|
|
122
|
+
`package.json` declares `engines.node: ">=22"`. Older Node versions
|
|
123
|
+
crash on the `--experimental-strip-types` test runner, or fail subtle
|
|
124
|
+
TypeScript-import behaviour.
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
id: KFM-003
|
|
128
|
+
pattern: '(engine "node" is incompatible|Unsupported engine|SyntaxError.*Unexpected token.*satisfies|--experimental-strip-types)'
|
|
129
|
+
diagnosis: 'Active Node.js version is below the plugin''s required >=22; modern syntax features and the strip-types test runner are unavailable.'
|
|
130
|
+
remedy: 'Upgrade Node to >=22 (e.g. `nvm install 22 && nvm use 22`) and rerun.'
|
|
131
|
+
severity: high
|
|
132
|
+
propose_report: false
|
|
133
|
+
symptom: 'npm warns `engine "node" is incompatible` / `Unsupported engine`, or Node throws `SyntaxError: Unexpected token "satisfies"`, or the test runner fails to recognise `--experimental-strip-types`.'
|
|
134
|
+
root_cause: 'The plugin declares `engines.node: ">=22"` in `package.json` and uses Node 22-only syntax (e.g. `satisfies` operator in TypeScript-stripped tests). Older Node runtimes cannot parse the source.'
|
|
135
|
+
fix: '1) Check active version with `node -v`. 2) If <22, run `nvm install 22 && nvm use 22` (Linux/macOS) or `nvm-windows install 22` / `winget install OpenJS.NodeJS`. 3) Reopen the shell or re-source `~/.nvmrc`. 4) Verify with `node -v` then re-run the command.'
|
|
136
|
+
related_phases: [12, 14.6, 24]
|
|
137
|
+
first_observed_cycle: 'pre-30.5'
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### KFM-004 — Figma token missing
|
|
141
|
+
|
|
142
|
+
Figma-aware flows expect `FIGMA_TOKEN` (or the documented env-var alias)
|
|
143
|
+
to be present. The 401/missing-env error class is recognisable.
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
id: KFM-004
|
|
147
|
+
pattern: '(FIGMA_TOKEN.*(not set|missing|undefined)|Figma.*401|figma.*unauthor)'
|
|
148
|
+
diagnosis: 'FIGMA_TOKEN environment variable is missing or invalid; Figma-dependent commands cannot authenticate.'
|
|
149
|
+
remedy: 'Generate a personal access token at https://www.figma.com/developers/api#access-tokens and `export FIGMA_TOKEN=<token>` in your shell profile.'
|
|
150
|
+
severity: medium
|
|
151
|
+
propose_report: false
|
|
152
|
+
symptom: 'Figma-aware commands abort with `FIGMA_TOKEN not set`, return HTTP 401 from `api.figma.com`, or emit `figma: unauthorized`.'
|
|
153
|
+
root_cause: 'Figma flows authenticate via a personal access token in `FIGMA_TOKEN`. When the env var is unset, expired, or revoked, the API rejects every request.'
|
|
154
|
+
fix: '1) Visit https://www.figma.com/developers/api#access-tokens and generate a new token. 2) `export FIGMA_TOKEN=<token>` in `~/.zshrc` / `~/.bashrc` (or `$Env:FIGMA_TOKEN` in PowerShell). 3) Open a new shell and verify with `echo $FIGMA_TOKEN`. 4) Re-run the failing command.'
|
|
155
|
+
related_phases: [13.2, 18, 19.6]
|
|
156
|
+
first_observed_cycle: 'pre-30.5'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### KFM-005 — Git working tree dirty
|
|
160
|
+
|
|
161
|
+
Several phase-tooling commands assume a clean working tree (clean
|
|
162
|
+
checkpoints between cycles). A dirty tree surfaces as a stderr line
|
|
163
|
+
the matcher can recognise.
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
id: KFM-005
|
|
167
|
+
pattern: '(working tree (is )?(not clean|dirty)|uncommitted changes|Changes not staged for commit)'
|
|
168
|
+
diagnosis: 'Git working tree has uncommitted changes; the command requires a clean checkpoint before proceeding.'
|
|
169
|
+
remedy: 'Commit, stash (`git stash -u`), or discard your local changes, then rerun the command.'
|
|
170
|
+
severity: low
|
|
171
|
+
propose_report: false
|
|
172
|
+
symptom: 'A phase-tooling command (e.g. `/gsd:execute-phase`, milestone closeout) aborts with `working tree is not clean`, `uncommitted changes`, or `Changes not staged for commit`.'
|
|
173
|
+
root_cause: 'Several GSD/GDD tooling commands assume a clean git checkpoint between cycles so commits remain atomic and easy to revert. A dirty working tree breaks that invariant.'
|
|
174
|
+
fix: '1) Run `git status` to see which files are dirty. 2) If the changes are wanted, stage them individually with `git add <path>` and `git commit -m "..."`. 3) If they''re scratch work, `git stash -u` (preserves them) or `git checkout -- <path>` (discards them). 4) Re-run the original command.'
|
|
175
|
+
related_phases: [22, 23.5, 25]
|
|
176
|
+
first_observed_cycle: 'pre-30.5'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### KFM-006 — `.planning/` directory missing
|
|
180
|
+
|
|
181
|
+
GSD/GDD project commands assume `.planning/` has been initialised by
|
|
182
|
+
`/gsd:new-project`. A bare ENOENT on that path is a clear self-fix.
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
id: KFM-006
|
|
186
|
+
pattern: 'ENOENT.*\.planning'
|
|
187
|
+
diagnosis: '.planning/ directory does not exist; the project has not been initialised yet.'
|
|
188
|
+
remedy: 'Run `/gsd:new-project` to bootstrap the planning structure, then retry.'
|
|
189
|
+
severity: medium
|
|
190
|
+
propose_report: false
|
|
191
|
+
symptom: 'A GSD/GDD command fails with `ENOENT: no such file or directory, open ''.planning/STATE.md''` or a similar `ENOENT` referencing the `.planning/` tree.'
|
|
192
|
+
root_cause: 'GSD/GDD commands read project state from `.planning/` (STATE.md, ROADMAP.md, REQUIREMENTS.md, phases/). The directory must be bootstrapped by `/gsd:new-project` before any other workflow command will work.'
|
|
193
|
+
fix: '1) Verify you''re in the project root with `pwd` / `ls`. 2) If `.planning/` is genuinely missing, run `/gsd:new-project` to scaffold it. 3) If you expected `.planning/` to exist (cloned repo), check whether it was excluded by `.gitignore` — `.planning/` is local-only in some projects. 4) Re-run the failing command.'
|
|
194
|
+
related_phases: [22, 23.5]
|
|
195
|
+
first_observed_cycle: 'pre-30.5'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### KFM-007 — `reference/registry.json` invalid JSON
|
|
199
|
+
|
|
200
|
+
The Phase 14.5 registry is hand-edited; a stray trailing comma or
|
|
201
|
+
unquoted key surfaces here. Self-fixable, not a maintainer issue.
|
|
202
|
+
|
|
203
|
+
```yaml
|
|
204
|
+
id: KFM-007
|
|
205
|
+
pattern: '(reference/registry\.json.*(SyntaxError|JSON|Unexpected token)|Unexpected token.*registry\.json)'
|
|
206
|
+
diagnosis: 'reference/registry.json failed to parse as JSON — likely a trailing comma or unbalanced brace from a recent edit.'
|
|
207
|
+
remedy: 'Open reference/registry.json in your editor; the JSON parser error message will pinpoint the line. Fix the syntax and retry.'
|
|
208
|
+
severity: medium
|
|
209
|
+
propose_report: false
|
|
210
|
+
symptom: 'A tool that loads the Phase 14.5 reference registry crashes with `SyntaxError: Unexpected token` or `JSON.parse` failure referencing `reference/registry.json`.'
|
|
211
|
+
root_cause: 'reference/registry.json is hand-edited as part of new-phase work; a trailing comma, unquoted key, or unbalanced brace from a recent edit breaks JSON.parse on the next consumer.'
|
|
212
|
+
fix: '1) Open `reference/registry.json` in your editor — the JSON parser error message includes the offending line/column. 2) Look for trailing commas (JSON forbids them), unquoted keys, or unbalanced braces/brackets. 3) Validate with `node -e "JSON.parse(require(''fs'').readFileSync(''reference/registry.json''))"` — silent exit means success. 4) Re-run the failing command.'
|
|
213
|
+
related_phases: [14.5, 25]
|
|
214
|
+
first_observed_cycle: 'pre-30.5'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### KFM-008 — MCP server unreachable
|
|
218
|
+
|
|
219
|
+
When the Figma / GDD MCP servers are not running, commands depending
|
|
220
|
+
on them fail with a clear connection-refused class of error.
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
id: KFM-008
|
|
224
|
+
pattern: '(MCP.*(unreachable|ECONNREFUSED|not connected)|mcp.*server.*not.*running|connection refused.*ws://)'
|
|
225
|
+
diagnosis: 'An MCP server (Figma, GDD-state, or GDD-tools) is not reachable; the plugin cannot route tool calls through it.'
|
|
226
|
+
remedy: 'Start the relevant MCP server (see scripts/mcp-servers/) and confirm `claude mcp list` shows it as connected.'
|
|
227
|
+
severity: medium
|
|
228
|
+
propose_report: true
|
|
229
|
+
symptom: 'A command depending on an MCP server fails with `MCP unreachable`, `ECONNREFUSED`, `mcp server not running`, or `connection refused ws://...`. Tool calls routed through MCP never reach their target.'
|
|
230
|
+
root_cause: 'The local MCP transport (WebSocket or stdio bridge) is not bound. The MCP server process is either not started, crashed silently, or is listening on a different port than the client expects.'
|
|
231
|
+
fix: '1) Run `claude mcp list` and check the status column for the failing server. 2) If it shows `disconnected`, start it: `scripts/mcp-servers/<name>.cjs` or the launcher script for that server. 3) Confirm the port matches the value in `mcp.json` or `.mcp.json`. 4) Re-run the failing command. If the server crashes on start, this is a maintainer report path (propose_report:true).'
|
|
232
|
+
related_phases: [27.7, 33.6]
|
|
233
|
+
first_observed_cycle: 'pre-30.5'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### KFM-009 — Plugin file accidentally deleted
|
|
237
|
+
|
|
238
|
+
A user-side `git clean -fdx` or aggressive editor refactor can remove
|
|
239
|
+
plugin files. This is a re-install path, not a bug report path — but
|
|
240
|
+
it's on the whitelist because users typically can't tell it apart from
|
|
241
|
+
an upstream regression.
|
|
242
|
+
|
|
243
|
+
```yaml
|
|
244
|
+
id: KFM-009
|
|
245
|
+
pattern: 'Cannot find module.*(scripts/lib/|skills/.*SKILL\.md|reference/.*\.md)'
|
|
246
|
+
diagnosis: 'A plugin file is missing — most often the result of a local `git clean` or a partial install.'
|
|
247
|
+
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.'
|
|
248
|
+
severity: medium
|
|
249
|
+
propose_report: true
|
|
250
|
+
symptom: 'A command errors out with `Cannot find module` referencing a path under `scripts/lib/`, `skills/.../SKILL.md`, or `reference/*.md`. From the user''s perspective, a file that should ship with the plugin is gone.'
|
|
251
|
+
root_cause: 'The file was removed locally — most often by an aggressive `git clean -fdx`, a worktree teardown that swept up tracked files, or an incomplete reinstall. The error path tells you exactly which file is missing.'
|
|
252
|
+
fix: '1) Note the missing path from the error. 2) `git status` to confirm it''s gone (not just renamed). 3) `git checkout HEAD -- <path>` to restore from the current commit. 4) If the file was never committed locally, run `npm install -g @hegemonart/get-design-done` (or pull the repo fresh in dev mode). 5) Re-run the failing command. (propose_report:true because a missing plugin file can also indicate an upstream packaging bug.)'
|
|
253
|
+
related_phases: [24, 25]
|
|
254
|
+
first_observed_cycle: 'pre-30.5'
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### KFM-010 — Disk full / ENOSPC
|
|
258
|
+
|
|
259
|
+
Out-of-space failures masquerade as obscure write errors. Self-fixable
|
|
260
|
+
by freeing space; not a maintainer report path.
|
|
261
|
+
|
|
262
|
+
```yaml
|
|
263
|
+
id: KFM-010
|
|
264
|
+
pattern: '(ENOSPC|no space left on device|disk full)'
|
|
265
|
+
diagnosis: 'Disk is full — no space left on the device the plugin is writing to.'
|
|
266
|
+
remedy: 'Free space (e.g. clear `.design/cache/`, prune old worktrees, empty trash) and retry.'
|
|
267
|
+
severity: high
|
|
268
|
+
propose_report: false
|
|
269
|
+
symptom: 'A write operation fails with `ENOSPC`, `no space left on device`, or `disk full`. The error often surfaces from a seemingly unrelated path (cache write, log append, snapshot save).'
|
|
270
|
+
root_cause: 'The filesystem holding `.design/`, the repo, or the npm cache has run out of free blocks or inodes. Node''s `fs.writeFile` (and downstream tools) propagate the kernel''s `ENOSPC` directly.'
|
|
271
|
+
fix: '1) Check free space with `df -h` (Linux/macOS) or `Get-PSDrive` (PowerShell). 2) Clear local caches: `rm -rf .design/cache/`, `npm cache clean --force`, prune stale `git worktree list` entries. 3) Empty system trash / Recycle Bin. 4) If the issue is inodes (not blocks), check with `df -i` and clean small-file-heavy dirs (`node_modules`, build artifacts). 5) Re-run the command.'
|
|
272
|
+
related_phases: [22, 24, 29]
|
|
273
|
+
first_observed_cycle: 'pre-30.5'
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### KFM-011 — `validate-skill-length` pre-commit hook fails
|
|
277
|
+
|
|
278
|
+
The local pre-commit hook `scripts/validate-skill-length.cjs` rejects
|
|
279
|
+
SKILL.md files exceeding the agentskills.io length budget. A commit is
|
|
280
|
+
blocked until the offending skill is trimmed or the `--fix` autoformatter
|
|
281
|
+
is run.
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
id: KFM-011
|
|
285
|
+
pattern: '(validate-skill-length|skill.*exceeds.*(line|character)|SKILL\.md.*(too long|over.*budget))'
|
|
286
|
+
diagnosis: 'A SKILL.md file exceeds the agentskills.io length budget; the validate-skill-length pre-commit hook is blocking the commit.'
|
|
287
|
+
remedy: 'Run `node scripts/validate-skill-length.cjs --fix` to auto-trim, then re-stage and commit.'
|
|
288
|
+
severity: low
|
|
289
|
+
propose_report: true
|
|
290
|
+
symptom: 'A `git commit` aborts with `validate-skill-length` warnings: one or more `SKILL.md` files exceed the per-skill line or character budget. The commit never lands.'
|
|
291
|
+
root_cause: 'The pre-commit hook enforces the agentskills.io length contract (Phase 28.5). When a recent edit pushes a skill over budget, the hook fails fast to keep skills shippable.'
|
|
292
|
+
fix: '1) Inspect the hook output — it names each offending file and its measured size. 2) Run `node scripts/validate-skill-length.cjs --fix` to autoformat any auto-trimmable bloat. 3) If the file still exceeds budget, manually shorten descriptions or move detail into a `reference/` sidecar. 4) `git add` the trimmed file and re-run `git commit`. This is on the propose_report whitelist because recurring trips often indicate the budget itself needs reconsideration.'
|
|
293
|
+
related_phases: [28.5, 28.6]
|
|
294
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### KFM-012 — `npm ci` lockfile drift
|
|
298
|
+
|
|
299
|
+
`npm ci` enforces strict parity between `package.json` and
|
|
300
|
+
`package-lock.json`. A manual edit to one without the other surfaces as
|
|
301
|
+
an `EUSAGE` error with the lockfile path called out.
|
|
302
|
+
|
|
303
|
+
```yaml
|
|
304
|
+
id: KFM-012
|
|
305
|
+
pattern: '(npm error code EUSAGE|can only install (packages )?with an existing package-lock\.json|npm ci.*lockfile)'
|
|
306
|
+
diagnosis: 'package-lock.json is out of sync with package.json; npm ci refuses to install when the lockfile is missing or drifted.'
|
|
307
|
+
remedy: 'Run `npm install` once locally to regenerate the lockfile, commit the updated package-lock.json, then re-run `npm ci`.'
|
|
308
|
+
severity: medium
|
|
309
|
+
propose_report: false
|
|
310
|
+
symptom: 'CI or a clean install fails with `npm error code EUSAGE` and a message about `npm ci` only working with an existing, up-to-date `package-lock.json`.'
|
|
311
|
+
root_cause: 'A manual edit to `package.json` (e.g. bumping a version) without a follow-up `npm install` leaves the lockfile drifted. `npm ci` rejects drift to keep installs reproducible.'
|
|
312
|
+
fix: '1) Pull the latest main if needed. 2) Run `npm install` (NOT `npm ci`) to regenerate the lockfile from `package.json`. 3) Inspect the diff with `git diff package-lock.json` — should match the package.json change. 4) Stage and commit the updated lockfile. 5) Re-run `npm ci`.'
|
|
313
|
+
related_phases: [25]
|
|
314
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### KFM-013 — lychee transient SSL failure on whitelisted hosts
|
|
318
|
+
|
|
319
|
+
The link-check job (lychee) reports intermittent TLS errors on a small
|
|
320
|
+
set of well-known authority hosts (`heydonworks.com`,
|
|
321
|
+
`ryanmulligan.dev`, `adamwathan.me`). These are usually transient and
|
|
322
|
+
clear on re-run; persistent failures warrant an allowlist entry.
|
|
323
|
+
|
|
324
|
+
```yaml
|
|
325
|
+
id: KFM-013
|
|
326
|
+
pattern: '(lychee.*\[ERROR\].*(heydonworks\.com|ryanmulligan\.dev|adamwathan\.me)|SSL.*handshake.*(heydonworks|ryanmulligan|adamwathan))'
|
|
327
|
+
diagnosis: 'lychee link-check hit a transient TLS/SSL failure on one of the whitelisted authority hosts; likely a flaky CDN handshake rather than a real broken link.'
|
|
328
|
+
remedy: 'Rerun the link-check job; if 3 consecutive runs fail, add the host to lychee allowlist in .lycheeignore or workflow config.'
|
|
329
|
+
severity: low
|
|
330
|
+
propose_report: false
|
|
331
|
+
symptom: 'The `link-check` workflow fails with `lychee [ERROR]` lines naming `heydonworks.com`, `ryanmulligan.dev`, or `adamwathan.me`, often with a TLS handshake or `SSL` error in the message.'
|
|
332
|
+
root_cause: 'These hosts sit behind CDNs that occasionally renegotiate TLS sessions mid-flight from CI runners. lychee surfaces the failure as a hard error even when the URL is reachable on retry.'
|
|
333
|
+
fix: '1) Open the failed Actions run and re-run only failed jobs. 2) If the same host fails three times in a row, edit `.lycheeignore` (or the equivalent allowlist in the workflow) and add the URL or hostname. 3) Re-run the workflow.'
|
|
334
|
+
related_phases: [13.2, 18]
|
|
335
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### KFM-014 — `gh pr merge` fast-forward warning (cosmetic)
|
|
339
|
+
|
|
340
|
+
GitHub's CLI surfaces a "Not possible to fast-forward" warning after a
|
|
341
|
+
successful server-side merge in some workflow combinations. The merge
|
|
342
|
+
already landed; the warning is purely cosmetic.
|
|
343
|
+
|
|
344
|
+
```yaml
|
|
345
|
+
id: KFM-014
|
|
346
|
+
pattern: '(Not possible to fast-forward, do you want to continue|gh pr merge.*warning.*fast-forward)'
|
|
347
|
+
diagnosis: 'gh pr merge surfaced a fast-forward warning, but the server-side merge already succeeded; the local branch is just lagging.'
|
|
348
|
+
remedy: 'Verify the PR is merged on GitHub (gh pr view --json state); the warning is cosmetic and can be ignored. Pull main to sync your local branch.'
|
|
349
|
+
severity: low
|
|
350
|
+
propose_report: false
|
|
351
|
+
symptom: 'After `gh pr merge`, the CLI prints `Not possible to fast-forward, do you want to continue?` even though the PR shows as merged on github.com. The user is unsure whether the merge happened.'
|
|
352
|
+
root_cause: 'The local main branch has diverged from origin/main between the start of `gh pr merge` and its post-merge sync step. The server-side merge succeeded; the local refs just need a `git pull` to catch up.'
|
|
353
|
+
fix: '1) Confirm the merge with `gh pr view <number> --json state,mergedAt`. 2) If `state` is `MERGED`, the warning is cosmetic. 3) Run `git checkout main && git pull origin main` to sync local refs. 4) Continue with the next workflow step.'
|
|
354
|
+
related_phases: [25]
|
|
355
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### KFM-015 — CodeQL `js/incomplete-sanitization` false positive
|
|
359
|
+
|
|
360
|
+
CodeQL flags `js/incomplete-sanitization` on regex-based input that
|
|
361
|
+
operates on an enum-constrained value. The alert is a false positive in
|
|
362
|
+
that narrow case but cannot be silenced without an inline justification.
|
|
363
|
+
|
|
364
|
+
```yaml
|
|
365
|
+
id: KFM-015
|
|
366
|
+
pattern: '(js/incomplete-sanitization|CodeQL.*incomplete[- ]sanitization)'
|
|
367
|
+
diagnosis: 'CodeQL flagged js/incomplete-sanitization on input that is already constrained to an enum/whitelist; common false-positive class.'
|
|
368
|
+
remedy: 'Add a CodeQL justification comment (`// codeql[js/incomplete-sanitization]`) explaining the enum constraint, then dismiss the alert with reason "false positive — input is enum-constrained".'
|
|
369
|
+
severity: low
|
|
370
|
+
propose_report: false
|
|
371
|
+
symptom: 'A CodeQL scan reports `js/incomplete-sanitization` on a line that sanitises user-supplied input which is already constrained to a small enum (e.g. `low|medium|high`).'
|
|
372
|
+
root_cause: 'CodeQL''s data-flow analysis cannot prove that an upstream check restricts the input to an enum, so it conservatively flags the regex-based normaliser as incomplete sanitization.'
|
|
373
|
+
fix: '1) Confirm via code inspection that the input is genuinely enum-constrained upstream (e.g. validated by a `SEVERITIES.has(x)` check). 2) Add an inline justification comment immediately above the flagged line: `// codeql[js/incomplete-sanitization] — input is enum-constrained by SEVERITIES.has() at line N`. 3) Open the alert in GitHub Security tab and dismiss with reason "False positive". 4) Push the justification comment so future scans also surface it.'
|
|
374
|
+
related_phases: [25, 27.5]
|
|
375
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### KFM-016 — `gitleaks` false positive on documentation examples
|
|
379
|
+
|
|
380
|
+
`gitleaks` flags literal example tokens (`ghp_…`, `sk-ant-…`) in
|
|
381
|
+
`reference/*.md` documentation. These are educational examples, not real
|
|
382
|
+
credentials, but the scanner cannot tell them apart.
|
|
383
|
+
|
|
384
|
+
```yaml
|
|
385
|
+
id: KFM-016
|
|
386
|
+
pattern: '(gitleaks.*finding.*reference/|gitleaks.*(ghp_|sk-ant-).*reference)'
|
|
387
|
+
diagnosis: 'gitleaks flagged a documentation example as a real secret; the path under reference/ contains an illustrative token literal, not a live credential.'
|
|
388
|
+
remedy: 'Either add the file path to .gitleaks.toml allowlist OR rewrite the example to use a clearly-fake placeholder like `EXAMPLE_TOKEN_REDACTED`.'
|
|
389
|
+
severity: low
|
|
390
|
+
propose_report: false
|
|
391
|
+
symptom: 'A `gitleaks` scan in CI fails with a finding pointing at a `reference/*.md` file containing a string that pattern-matches a token shape (e.g. `ghp_…` for a GitHub PAT or `sk-ant-…` for an Anthropic key).'
|
|
392
|
+
root_cause: 'gitleaks rules are pattern-based and cannot distinguish an example literal in documentation from a real leaked credential. Docs that include token shapes for teaching purposes trip the scanner.'
|
|
393
|
+
fix: '1) Confirm via `git log -p <file>` that the token is example-only and was never live. 2) Edit `.gitleaks.toml` and add the file path under `[allowlist] paths`. OR rewrite the example to use an obviously-fake placeholder like `ghp_EXAMPLE_REDACTED`. 3) Push the change and re-run the scan. 4) If the leak was real, follow secret-rotation playbook instead.'
|
|
394
|
+
related_phases: [25]
|
|
395
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### KFM-017 — `release.yml` version mismatch with `plugin.json`
|
|
399
|
+
|
|
400
|
+
The Phase 25 release safeguard rejects a `workflow_dispatch` invocation
|
|
401
|
+
whose input version doesn't match the version declared in `plugin.json`.
|
|
402
|
+
This is the manifest-lockstep guard catching a forgotten bump.
|
|
403
|
+
|
|
404
|
+
```yaml
|
|
405
|
+
id: KFM-017
|
|
406
|
+
pattern: '(release\.yml.*version (mismatch|does not match)|workflow_dispatch.*plugin\.json.*version)'
|
|
407
|
+
diagnosis: 'release.yml refused the workflow_dispatch because input version disagrees with plugin.json; the lockstep version guard is intentionally blocking.'
|
|
408
|
+
remedy: 'Either correct the dispatch input to match plugin.json, or bump plugin.json (and the other 5 manifests) to the target version and re-dispatch.'
|
|
409
|
+
severity: medium
|
|
410
|
+
propose_report: false
|
|
411
|
+
symptom: 'A manual `workflow_dispatch` of `release.yml` fails immediately with an error about input version not matching `plugin.json`. The release does not proceed.'
|
|
412
|
+
root_cause: 'Phase 25 added a manifest-lockstep guard to `release.yml`: the dispatch input version MUST match `plugin.json` exactly. The guard catches the case where someone tries to release a version without first bumping all 6 manifests.'
|
|
413
|
+
fix: '1) Read the current `plugin.json` version. 2) If the dispatch input was a typo, re-dispatch with the correct version. 3) If you genuinely intended a new version, run the 6-manifest lockstep bump first: `plugin.json`, `package.json`, `package-lock.json`, `README.md`, `CHANGELOG.md`, the SDK version constant. 4) Commit the bumps, then re-dispatch `release.yml`.'
|
|
414
|
+
related_phases: [25]
|
|
415
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### KFM-018 — `npm publish` 404 after `NPM_TOKEN` rotation
|
|
419
|
+
|
|
420
|
+
`npm publish` returns `404 Not Found` from the registry when the
|
|
421
|
+
`NPM_TOKEN` secret has been rotated upstream but the GitHub repo secret
|
|
422
|
+
still holds the old value. (Common after npm''s May 2026 Mini
|
|
423
|
+
Shai-Hulud forced rotation.)
|
|
424
|
+
|
|
425
|
+
```yaml
|
|
426
|
+
id: KFM-018
|
|
427
|
+
pattern: '(npm error 404|Not Found - PUT https://registry\.npmjs\.org/|npm publish.*401.*registry\.npmjs)'
|
|
428
|
+
diagnosis: 'npm publish failed with 404/401 against the registry; the most common cause in 2026 is a rotated NPM_TOKEN that the repo secret has not caught up to.'
|
|
429
|
+
remedy: 'Regenerate token at npmjs.com, update NPM_TOKEN repo secret in GitHub Settings, retry the release workflow.'
|
|
430
|
+
severity: medium
|
|
431
|
+
propose_report: false
|
|
432
|
+
symptom: 'A release run''s `npm publish` step fails with `npm error 404 Not Found - PUT https://registry.npmjs.org/<package>` or a 401 on the same URL. No version is published.'
|
|
433
|
+
root_cause: 'Either the `NPM_TOKEN` secret in the GitHub repo settings was rotated upstream (npmjs.com revoked the old token, common during the May 2026 Mini Shai-Hulud incident), or the token never had publish scope for the package.'
|
|
434
|
+
fix: '1) Log into npmjs.com → Access Tokens. 2) Generate a new Automation token with publish scope for the package. 3) In GitHub: Settings → Secrets → update `NPM_TOKEN` with the new value. 4) Re-dispatch the release workflow. 5) If the failure persists, verify the package name in `package.json` matches what npm expects and that 2FA settings allow automation tokens.'
|
|
435
|
+
related_phases: [25]
|
|
436
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### KFM-019 — macOS symlinked tmpdir comparison failure
|
|
440
|
+
|
|
441
|
+
Tests that compare a tmpdir-derived path against an expected absolute
|
|
442
|
+
path fail on macOS because `/var/folders/...` is a symlink to
|
|
443
|
+
`/private/var/folders/...`. The string comparison sees mismatched
|
|
444
|
+
prefixes; `fs.realpathSync` resolves them.
|
|
445
|
+
|
|
446
|
+
```yaml
|
|
447
|
+
id: KFM-019
|
|
448
|
+
pattern: '(/var/folders/.*\/private/var/folders|AssertionError.*expected.*\/private\/var|tmpdir.*symlink.*realpath)'
|
|
449
|
+
diagnosis: 'macOS tmpdir is a symlink (/var/folders/ -> /private/var/folders/); a string equality test against tmpdir path fails because one side is resolved and the other isn''t.'
|
|
450
|
+
remedy: 'Wrap tmpdir reads in fs.realpathSync before comparing; or normalize both sides to realpath at assertion time.'
|
|
451
|
+
severity: low
|
|
452
|
+
propose_report: true
|
|
453
|
+
symptom: 'A unit test that uses `os.tmpdir()` fails on macOS with an assertion showing `/private/var/folders/...` on one side and `/var/folders/...` on the other. Same test passes on Linux.'
|
|
454
|
+
root_cause: 'macOS''s `/var/folders` is a symlink to `/private/var/folders`. `os.tmpdir()` returns the unresolved form, but some Node APIs (especially after a `process.chdir` into the tmpdir) return the resolved form. String equality fails on the prefix.'
|
|
455
|
+
fix: '1) In test setup, wrap the tmpdir path: `const TMP = fs.realpathSync(os.tmpdir())`. 2) Use `TMP` consistently throughout the test. 3) For any assertion that receives a path from a Node API after `chdir`, also pass it through `fs.realpathSync`. 4) Re-run the test. propose_report:true because this is recurring developer-experience friction that warrants a tracked tooling issue.'
|
|
456
|
+
related_phases: [12, 14.6]
|
|
457
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### KFM-020 — Windows CRLF vs LF byte-comparison mismatch
|
|
461
|
+
|
|
462
|
+
Tests that byte-compare file contents fail on Windows because
|
|
463
|
+
`git show HEAD:<file>` returns LF line endings while
|
|
464
|
+
`fs.readFileSync` after checkout returns CRLF (when `core.autocrlf` is
|
|
465
|
+
`true`). Normalisation or `.gitattributes` resolves it.
|
|
466
|
+
|
|
467
|
+
```yaml
|
|
468
|
+
id: KFM-020
|
|
469
|
+
pattern: '(core\.autocrlf|CRLF.*(mismatch|conversion)|AssertionError.*(CRLF|line endings|eol)|git.*autocrlf.*true)'
|
|
470
|
+
diagnosis: 'Windows checkout converted LF to CRLF; byte-comparison between `git show HEAD:` (LF) and the working tree (CRLF) fails.'
|
|
471
|
+
remedy: 'Either set .gitattributes `* text=auto eol=lf` for the affected files, or normalize both sides with `.replace(/\\r\\n/g,"\\n")` before comparison.'
|
|
472
|
+
severity: medium
|
|
473
|
+
propose_report: true
|
|
474
|
+
symptom: 'A test that byte-compares file contents passes on Linux/macOS but fails on Windows with an `AssertionError` showing `\\r\\n` on one side and `\\n` on the other.'
|
|
475
|
+
root_cause: 'Windows git has `core.autocrlf=true` by default; on checkout, LF in the repo is converted to CRLF in the working tree. `git show HEAD:<file>` returns the LF-form, but `fs.readFileSync` after a normal checkout returns CRLF. Byte equality fails.'
|
|
476
|
+
fix: '1) Decide whether the file should be EOL-normalised: if yes, add `<glob> text=auto eol=lf` to `.gitattributes`, run `git add --renormalize .`, commit. 2) Or normalise both sides at compare time: `expected.replace(/\\r\\n/g,"\\n") === actual.replace(/\\r\\n/g,"\\n")`. 3) For test-fixture binary-ish files, also consider `<glob> -text` to disable EOL handling entirely. 4) Re-run the Windows test. propose_report:true because Windows-specific test failures are easy to miss in Linux-only CI.'
|
|
477
|
+
related_phases: [12, 14.6, 24]
|
|
478
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### KFM-021 — Skill name contains colon (agentskills.io slug regex)
|
|
482
|
+
|
|
483
|
+
Skills named with a colon (e.g. `get-design-done:foo`) fail the
|
|
484
|
+
agentskills.io spec validator. The spec reserves colons for namespace
|
|
485
|
+
delimiters at the registry level; on-disk skill folders must use
|
|
486
|
+
hyphens.
|
|
487
|
+
|
|
488
|
+
```yaml
|
|
489
|
+
id: KFM-021
|
|
490
|
+
pattern: '(agentskills\.io.*(slug|name).*invalid|skill name.*contains colon|SKILL.*invalid.*slug)'
|
|
491
|
+
diagnosis: 'Skill folder name contains a colon, which the agentskills.io spec reserves for registry-level namespacing; on-disk skill slugs must be hyphen-separated.'
|
|
492
|
+
remedy: 'Rename the skill folder to use a hyphen (e.g. `get-design-done:foo` -> `get-design-done-foo`) and update any cross-references.'
|
|
493
|
+
severity: medium
|
|
494
|
+
propose_report: false
|
|
495
|
+
symptom: 'A skill-validation step (or the publishing flow) rejects a skill folder with an error like `skill name "get-design-done:foo" is not a valid agentskills.io slug` or `slug must match /^[a-z0-9][a-z0-9-]*$/`.'
|
|
496
|
+
root_cause: 'The agentskills.io spec slug regex `/^[a-z0-9][a-z0-9-]*$/` excludes colons; colons are reserved at the registry level for namespacing. On-disk skill folders must use hyphens only.'
|
|
497
|
+
fix: '1) Rename the folder: `git mv skills/get-design-done:foo skills/get-design-done-foo`. 2) Update any cross-references in `plugin.json`, `README.md`, and other skills'' frontmatter `requires:` arrays. 3) Search-and-replace the old slug with the new one across `reference/` and `commands/`. 4) Re-run the validation.'
|
|
498
|
+
related_phases: [28.5, 28.6]
|
|
499
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### KFM-022 — Dependabot alert on transitive optional peer not in resolved tree
|
|
503
|
+
|
|
504
|
+
Dependabot opens an alert for a vulnerable transitive dependency that is
|
|
505
|
+
declared as an optional or peer dep upstream and is NOT actually
|
|
506
|
+
installed (`npm ls <dep>` shows nothing). The alert is dismissible with
|
|
507
|
+
"not vulnerable — not in resolved tree".
|
|
508
|
+
|
|
509
|
+
```yaml
|
|
510
|
+
id: KFM-022
|
|
511
|
+
pattern: '(Dependabot.*alert.*(transitive|optional|peer)|npm ls.*empty.*Dependabot)'
|
|
512
|
+
diagnosis: 'Dependabot opened an alert on a transitive optional/peer dependency that is not actually present in the resolved npm tree; the alert is real for some users but not for this install.'
|
|
513
|
+
remedy: 'Dismiss alert with reason "not vulnerable — transitive optional peer, not in resolved tree"; verify with `npm ls <dep>` (empty output confirms).'
|
|
514
|
+
severity: low
|
|
515
|
+
propose_report: false
|
|
516
|
+
symptom: 'A new Dependabot alert names a deeply-nested dependency the project does not directly use. Running `npm ls <dep>` from the project root returns empty output (no path to the package).'
|
|
517
|
+
root_cause: 'Dependabot reads `package-lock.json` and flags any package whose version matches a known CVE. Optional/peer transitive deps that npm did NOT install (because the host platform or peer mismatch ruled them out) still appear in the lockfile metadata, so Dependabot flags them even though they''re absent from the resolved tree.'
|
|
518
|
+
fix: '1) Verify absence: `npm ls <dep>` from the project root — empty output confirms the package is not installed. 2) Open the alert in GitHub Security tab → Dismiss → reason "Not vulnerable" with note "transitive optional peer; npm ls returns empty; not in resolved tree". 3) Optionally pin or update the parent dependency in a follow-up to remove the lockfile reference entirely. 4) Re-run any scan that depends on Dependabot state.'
|
|
519
|
+
related_phases: [25, 27.5]
|
|
520
|
+
first_observed_cycle: 'cycle-2026-05'
|
|
521
|
+
```
|