bmad-module-skill-forge 0.10.1-alpha.0 → 1.0.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 +19 -2
- package/README.md +1 -1
- package/docs/RELEASING.md +432 -1
- package/docs/STABILITY.md +1 -1
- package/package.json +6 -6
- package/release-audits/v1.0.0-launch-audit.md +417 -0
- package/tools/cli/commands/install.js +3 -0
- package/tools/cli/commands/update.js +4 -2
|
@@ -7,13 +7,30 @@
|
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://github.com/armelhbobdad/bmad-module-skill-forge",
|
|
9
9
|
"repository": "https://github.com/armelhbobdad/bmad-module-skill-forge",
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"bmad",
|
|
12
|
+
"bmad-method",
|
|
13
|
+
"bmad-module",
|
|
14
|
+
"agent-skills",
|
|
15
|
+
"agentskills",
|
|
16
|
+
"agents",
|
|
17
|
+
"skills",
|
|
18
|
+
"skill-forge",
|
|
19
|
+
"compilation",
|
|
20
|
+
"forge",
|
|
21
|
+
"ast-grep",
|
|
22
|
+
"qmd",
|
|
23
|
+
"cocoindex",
|
|
24
|
+
"cocoindex-code",
|
|
25
|
+
"provenance",
|
|
26
|
+
"code-analysis"
|
|
27
|
+
],
|
|
11
28
|
"plugins": [
|
|
12
29
|
{
|
|
13
30
|
"name": "skill-forge",
|
|
14
31
|
"source": "./",
|
|
15
32
|
"description": "Evidence-based agent skills compiler with progressive capability tiers (Quick/Forge/Forge+/Deep).",
|
|
16
|
-
"version": "
|
|
33
|
+
"version": "1.0.0",
|
|
17
34
|
"author": {
|
|
18
35
|
"name": "Armel"
|
|
19
36
|
},
|
package/README.md
CHANGED
|
@@ -185,7 +185,7 @@ The v1.0.0 public API contract is documented in [docs/STABILITY.md](docs/STABILI
|
|
|
185
185
|
|
|
186
186
|
## Release Process
|
|
187
187
|
|
|
188
|
-
Maintainers: see [docs/RELEASING.md](docs/RELEASING.md) for branch-protection rules, required status checks, the [`release` environment with required-reviewer gate](docs/RELEASING.md#release-environment), the [npm Trusted Publisher registration](docs/RELEASING.md#npm-trusted-publisher) (OIDC-backed publish, auto-provenance), and
|
|
188
|
+
Maintainers: see [docs/RELEASING.md](docs/RELEASING.md) for branch-protection rules, required status checks, the [`release` environment with required-reviewer gate](docs/RELEASING.md#release-environment), the [npm Trusted Publisher registration](docs/RELEASING.md#npm-trusted-publisher) (OIDC-backed publish, auto-provenance), and the [rollback playbook](docs/RELEASING.md#rollback-playbook) covering seven failure scenarios.
|
|
189
189
|
|
|
190
190
|
## License
|
|
191
191
|
|
package/docs/RELEASING.md
CHANGED
|
@@ -182,4 +182,435 @@ The future `release.yaml` workflow (Story 3.1) publishes to npm via **OIDC trust
|
|
|
182
182
|
|
|
183
183
|
## Rollback Playbook
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
> **NEVER `npm unpublish` v1.0.0.** Once `bmad-module-skill-forge` is published under `--tag latest` at v1.0.0, the version is immutable by policy (NFR6). The default rollback is `npm deprecate` + ship forward. See Scenario C for the narrow 72h / zero-dependents exception — which applies to **pre-v1.0.0 versions only**.
|
|
186
|
+
|
|
187
|
+
Scenarios A–F are recovery paths for a bad publish; Scenario G is a meta-recovery path for the release workflow itself. Each scenario follows a five-element shape: **Trigger** → **CLI** → **Expected outcome** → **Constraints** → **Verification**. A compact [cross-reference matrix](#cross-reference-matrix) sits at the end of this section for under-pressure triage. All `gh api` examples in this section use name-based lookups (ruleset by `name=="Default"`, environment by literal `release`) so they survive ruleset or environment re-creation.
|
|
188
|
+
|
|
189
|
+
Placeholder substitutions used throughout:
|
|
190
|
+
|
|
191
|
+
- `<bad>` — the broken version just published (e.g. `0.10.1`).
|
|
192
|
+
- `<previous_good>` — the last known-good version immediately before `<bad>` (e.g. `0.10.0`).
|
|
193
|
+
- `<next_version>` — the fix version produced by the next `release.yaml` dispatch (e.g. `0.10.2`).
|
|
194
|
+
- `<run-id>` — a GitHub Actions run id visible in the Actions UI and via `gh run list --workflow=release.yaml`.
|
|
195
|
+
|
|
196
|
+
### Scenario A — "Bad version published to `latest`, no users yet (within ~minutes)"
|
|
197
|
+
|
|
198
|
+
- **Trigger.** Maintainer notices a defect within minutes of publish; `npm view bmad-module-skill-forge dist-tags` still shows `<bad>` on `latest`; download count is negligible; no dependents have pinned `<bad>` yet.
|
|
199
|
+
|
|
200
|
+
> **Note on cache lag.** `npm view … dist-tags` reads through a CDN with multi-second to multi-minute eventual consistency. "Still shows `<bad>` on `latest`" is not an authoritative "no users yet" signal. For freshness, cross-check with `npm view bmad-module-skill-forge@<bad> time --json | jq '. | to_entries | last'`.
|
|
201
|
+
|
|
202
|
+
- **CLI.**
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Flip latest back to the last known-good version.
|
|
206
|
+
npm dist-tag add bmad-module-skill-forge@<previous_good> latest
|
|
207
|
+
|
|
208
|
+
# Verify the flip landed.
|
|
209
|
+
npm dist-tag ls bmad-module-skill-forge
|
|
210
|
+
|
|
211
|
+
# Warn anyone who did install <bad> in the meantime.
|
|
212
|
+
npm deprecate bmad-module-skill-forge@<bad> "Pulled - use <previous_good> instead"
|
|
213
|
+
|
|
214
|
+
# Cut the fix forward via the canonical workflow (OIDC publish, SLSA-L2 provenance).
|
|
215
|
+
gh workflow run release.yaml -f version_bump=patch
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **Expected outcome.** `latest` now resolves to `<previous_good>`; `npm install bmad-module-skill-forge@<bad>` emits a deprecation warning; the subsequent patch cut advances `latest` to `<next_version>` cleanly.
|
|
219
|
+
- **Constraints.** Valid only while the blast radius is small. If any downstream has already pinned to `<bad>`, Scenario B applies instead — reverting `latest` won't reach those installs.
|
|
220
|
+
- **Verification.**
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npm view bmad-module-skill-forge dist-tags --json
|
|
224
|
+
# expected: {"latest":"<previous_good>", ...}
|
|
225
|
+
# Other dist-tags (e.g. "alpha") may also appear depending on prior releases.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Scenario B — "Bad version live for hours/days, users may be installing it"
|
|
229
|
+
|
|
230
|
+
- **Trigger.** Defect discovered after `<bad>` has held `latest` long enough that download count is non-zero, or dependents may have already pinned.
|
|
231
|
+
- **CLI.**
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Warn immediately — every install of <bad> now prints this string.
|
|
235
|
+
npm deprecate bmad-module-skill-forge@<bad> "Critical bug - use <next_version> instead"
|
|
236
|
+
|
|
237
|
+
# Cut the fix forward. release.yaml's dist-tag case-chain auto-updates `latest`
|
|
238
|
+
# for non-prerelease versions, so no manual `dist-tag add` is needed here.
|
|
239
|
+
gh workflow run release.yaml -f version_bump=patch
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
- **Expected outcome.** `npm install bmad-module-skill-forge` starts emitting the deprecation warning; the new patch publishes cleanly; `latest` advances to `<next_version>`.
|
|
243
|
+
- **Constraints.** **Do NOT** `npm dist-tag add @<previous_good> latest` in this scenario. For post-v1.0.0 releases that is an NFR6 violation (it re-exposes a deprecated version as `latest`); for pre-v1.0.0 it fragments user expectation and hides the fix. Ship forward via patch bump.
|
|
244
|
+
- **Verification.**
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Confirm the deprecation message landed on the registry (no install required —
|
|
248
|
+
# `npm install` would mutate the cwd's package.json/package-lock.json).
|
|
249
|
+
npm view bmad-module-skill-forge@<bad> deprecated
|
|
250
|
+
# expected: the exact deprecation string set above ("Critical bug - use <next_version> instead")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
NFR3 (rollback frequency <1/quarter post-v1.0.0) is evaluated against the frequency of Scenario B invocations — each trip through Scenario B is an NFR3 data point.
|
|
254
|
+
|
|
255
|
+
### Scenario C — "Bad version within last 72h, zero downloads, zero dependents"
|
|
256
|
+
|
|
257
|
+
- **Trigger.** A narrow eligibility window per the [npm unpublish policy](https://docs.npmjs.com/policies/unpublish/): less than 72 hours since publish **AND** zero downloads **AND** zero dependent packages registered on npmjs.com. All three conditions must be true. Compute the 72-hour boundary from the `time.modified` field of `npm view bmad-module-skill-forge@<bad> --json`, NOT from the responder's local clock — npm's policy engine uses its server timestamp and a drift of even a few minutes at the boundary will 422 the unpublish.
|
|
258
|
+
|
|
259
|
+
> **Caveat on the "zero downloads" criterion.** npm's downloads API is documented as eventually consistent with up to 24–48h lag post-publish. A version can show "0 downloads" while having been installed by hundreds of CI runs in that window. When in doubt in the first 24–48 hours, treat C as Scenario B.
|
|
260
|
+
|
|
261
|
+
- **CLI.**
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npm unpublish bmad-module-skill-forge@<bad>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
- **Expected outcome.** The version disappears from `npm view`. **But** the version _number_ is permanently burned — npm rejects any future publish of that same version string forever.
|
|
268
|
+
|
|
269
|
+
> **Permanent-burn warning.** The version number is burned. After `npm unpublish bmad-module-skill-forge@0.10.1`, the next `release.yaml` dispatch with `version_bump: patch` on `0.10.0` will produce `0.10.2`, **not** `0.10.1` again. Attempting `npm version 0.10.1 --no-git-tag-version` would drive the workflow's `npm publish` toward an npm-burned version and return `403`. Treat the unpublish as a one-way jump in the version number line, not a true undo.
|
|
270
|
+
|
|
271
|
+
- **Constraints.** **This scenario does NOT apply to v1.0.0.** Per NFR6, v1.0.0 is never unpublished regardless of eligibility window — use Scenario B instead. Scenario C is a pre-v1.0.0-only path.
|
|
272
|
+
- **Verification.**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npm view bmad-module-skill-forge@<bad> 2>&1
|
|
276
|
+
# expected: npm error 404 No match found for version <bad>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Scenario D — "Tag exists but npm publish failed"
|
|
280
|
+
|
|
281
|
+
- **Trigger.** `release.yaml` pushed the `v*` tag (tag-creation step runs before the npm publish step) and then the publish step failed — OIDC 404, tarball validation error, network drop. `npm view bmad-module-skill-forge@<version>` returns 404; `git tag -l v<version>` shows the tag locally and on origin.
|
|
282
|
+
- **CLI.**
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# VERIFY FIRST that npm did NOT publish — a tag-delete after a successful
|
|
286
|
+
# publish would orphan the npm artifact. If this returns a manifest, STOP
|
|
287
|
+
# and go to Scenario E.
|
|
288
|
+
npm view bmad-module-skill-forge@<version> 2>&1
|
|
289
|
+
|
|
290
|
+
# Tag-only recovery: clear the tag locally and on origin, then re-dispatch.
|
|
291
|
+
git tag -d v<version>
|
|
292
|
+
git push --delete origin v<version>
|
|
293
|
+
|
|
294
|
+
# Re-dispatch with the same bump input — release.yaml will produce a fresh
|
|
295
|
+
# clean tag and successful publish.
|
|
296
|
+
gh workflow run release.yaml -f version_bump=<same-input-as-before>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- **Expected outcome.** Tag cleared from origin; re-run produces a fresh clean tag plus a successful publish under the next version number.
|
|
300
|
+
- **Constraints.** Only safe if publish failed. If npm _did_ publish, use Scenario E — tag-deletion after a successful publish leaves the npm artifact without a matching git ref.
|
|
301
|
+
- **Do-NOT clause.** Never re-use the burned `<version>` number. `release.yaml` will produce the next version on redispatch (for example, re-running an `alpha` bump over `0.10.1-alpha.0` produces `0.10.1-alpha.1`, not `0.10.1-alpha.0` again). Forcing the original version back via `npm version <exact> --no-git-tag-version` is out of scope for rollback.
|
|
302
|
+
- **Story 3.2 load-bearing context.** The current `release.yaml` pushes the git tag **before** the npm publish step (see the `Create and push tag` job step vs. the `Publish to npm via OIDC trusted publishing` step). This ordering is pre-existing from Story 3.1 and tracked in `_bmad-output/implementation-artifacts/deferred-work.md` under `§ 3-1/3-2 code review "Create and push tag pushes the git tag BEFORE Publish to npm"` as a post-v1.0.0 hardening candidate. Until that lands, Scenario D's tag-delete path **is** the recovery.
|
|
303
|
+
- **Verification.**
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Tag gone on origin.
|
|
307
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/git/refs/tags/v<version> 2>&1
|
|
308
|
+
# expected: 404 Not Found
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Scenario E — "npm publish succeeded but GitHub Release / tag push failed"
|
|
312
|
+
|
|
313
|
+
- **Trigger.** Publish succeeded (`npm view bmad-module-skill-forge@<version>` returns a manifest) but **one of**: tag-push to origin failed (network drop, branch-protection edge case); the `Create GitHub Release` softprops step failed (API rate limit, auth expiry); the `Push commit to main` step was (correctly) skipped but the release artifact diverged from `main`.
|
|
314
|
+
- **Premise inversion note.** In the research doc's scenario ordering, publish was assumed to happen before tag push — so publish failure was the likely branch. In the live `release.yaml` the ordering is reversed: the tag push runs **before** the npm publish. That means a publish-succeeded-but-tag-missing window is small, but real — the `Create GitHub Release` and `Push commit to main` steps still run after publish and either can fail independently. This scenario covers the post-publish-tag-or-release-missing recovery path.
|
|
315
|
+
- **CLI — tag recovery.**
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Obtain the commit-sha the workflow used as its HEAD during the publish run
|
|
319
|
+
# (returns the SHA directly; do not grep the run log — the bump-commit echo
|
|
320
|
+
# line does not contain the SHA, only the post-commit confirmation does).
|
|
321
|
+
COMMIT_SHA=$(gh api \
|
|
322
|
+
repos/armelhbobdad/bmad-module-skill-forge/actions/runs/<run-id> \
|
|
323
|
+
--jq .head_sha)
|
|
324
|
+
|
|
325
|
+
# Recreate the annotated tag and push.
|
|
326
|
+
git tag -a v<version> "$COMMIT_SHA" -m "Release v<version>"
|
|
327
|
+
git push origin v<version>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
- **CLI — GitHub Release recovery.** The workflow's `Generate release notes`
|
|
331
|
+
step writes to `release_notes.md` inside the runner; that file is **not**
|
|
332
|
+
uploaded as an artifact, so it does not exist outside the run. Either scrape
|
|
333
|
+
the body from the run log first, or use `--generate-notes` to let GitHub
|
|
334
|
+
regenerate from commit subjects.
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# Option A: scrape the notes body from the run log into a local file.
|
|
338
|
+
gh run view <run-id> --log \
|
|
339
|
+
| sed -n '/^## Generate release notes/,/^##[^#]/p' \
|
|
340
|
+
> release_notes.md
|
|
341
|
+
gh release create v<version> \
|
|
342
|
+
--notes-file release_notes.md \
|
|
343
|
+
--title "Skill Forge (SKF) v<version>"
|
|
344
|
+
|
|
345
|
+
# Option B: let GitHub regenerate from commit subjects (loses the curated
|
|
346
|
+
# changelog body but always works).
|
|
347
|
+
gh release create v<version> \
|
|
348
|
+
--generate-notes \
|
|
349
|
+
--title "Skill Forge (SKF) v<version>"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
- **Expected outcome.** Tag landed on origin pointing at the correct commit; GitHub Release page reflects the published npm artifact; npm + GitHub + git state are now consistent.
|
|
353
|
+
- **Constraints.** **npm state is immutable.** Do NOT try to "clean up" the npm artifact so the release can be re-run from scratch. The npm artifact plus the orphaned post-recovery git state **is** the canonical record — NFR5 (audit-trail completeness) is satisfied by the successful npm publish plus the recovered tag and GitHub Release, not by a clean rerun.
|
|
354
|
+
- **Orphaned-commit caveat (Story 3.2 context).** If the run was dispatched from a feature branch and `Push commit to main` was correctly skipped, the `release: bump to v<version>` commit lives only in the workflow run log until the recovered tag above anchors it. This is NFR12-compliant and expected pre-v1.0.0 behavior; the tag is the authoritative pointer. This is exactly the pattern observed in the Story 3.2 alpha cut (`bmad-module-skill-forge@0.10.1-alpha.0`, run `24714953668`, tag `v0.10.1-alpha.0`, orphaned commit `2a57dcbd`).
|
|
355
|
+
- **Verification.**
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# Tag resolves.
|
|
359
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/git/refs/tags/v<version>
|
|
360
|
+
|
|
361
|
+
# GitHub Release exists.
|
|
362
|
+
gh release view v<version>
|
|
363
|
+
|
|
364
|
+
# npm + GitHub provenance still match.
|
|
365
|
+
npm view bmad-module-skill-forge@<version> --json | jq '.dist.attestations'
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Scenario F — "Suspected OIDC compromise / unauthorized publish"
|
|
369
|
+
|
|
370
|
+
- **Trigger (any of).**
|
|
371
|
+
- `npm view bmad-module-skill-forge time` shows a publish with a timestamp no maintainer triggered.
|
|
372
|
+
- `npm audit signatures` on a published tarball reports attestation verification failure.
|
|
373
|
+
- A GitHub Actions run shows reviewer approval that was not given.
|
|
374
|
+
- npm support notifies of suspected compromise.
|
|
375
|
+
- **Immediate CLI (within minutes of detection).** Revoke the OIDC trust surface before doing anything else:
|
|
376
|
+
- Visit `https://www.npmjs.com/package/bmad-module-skill-forge` → **Settings** tab → **Trusted Publisher** section → **Delete** the registration.
|
|
377
|
+
- This blocks _all_ future OIDC publishes until re-registered, stopping any further unauthorized use of the OIDC path mid-incident.
|
|
378
|
+
- **Audit CLI.**
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Full publish timeline.
|
|
382
|
+
npm view bmad-module-skill-forge time
|
|
383
|
+
|
|
384
|
+
# Per-version provenance shape (script-friendly).
|
|
385
|
+
npm view bmad-module-skill-forge@<suspect> --json \
|
|
386
|
+
| jq '.dist.attestations'
|
|
387
|
+
# expected shape:
|
|
388
|
+
# {
|
|
389
|
+
# "url": "https://registry.npmjs.org/-/npm/v1/attestations/bmad-module-skill-forge@<suspect>",
|
|
390
|
+
# "provenance": { "predicateType": "https://slsa.dev/provenance/v1" }
|
|
391
|
+
# }
|
|
392
|
+
|
|
393
|
+
# Human-friendly signature-chain check. `npm audit signatures` operates on
|
|
394
|
+
# the cwd's installed dependency tree — running it in an empty dir returns
|
|
395
|
+
# "0 packages have verified attestations", which is a false-clean signal
|
|
396
|
+
# mid-incident. Wrap the install in a throwaway dir, ignore lifecycle
|
|
397
|
+
# scripts (the suspect tarball may be hostile), then audit.
|
|
398
|
+
mkdir -p /tmp/skf-incident-verify && cd /tmp/skf-incident-verify
|
|
399
|
+
npm init -y >/dev/null
|
|
400
|
+
npm install --ignore-scripts bmad-module-skill-forge@<suspect>
|
|
401
|
+
npm audit signatures
|
|
402
|
+
# expected: "X packages have verified attestations" with attestation count >= 1
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
If `dist.attestations` is missing or the `url` field points somewhere unexpected, the publish bypassed the OIDC path.
|
|
406
|
+
|
|
407
|
+
- **Coordination CLI.** If a malicious version was actually published, contact `support@npmjs.com` with the version string, the attestation URL, and the Trusted Publisher registration timestamp; request a manual takedown. npm support can unpublish outside the 72-hour window for security-compromise cases.
|
|
408
|
+
- **Lockdown CLI.**
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
# Confirm no unexpected secrets were added to the repo.
|
|
412
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/actions/secrets
|
|
413
|
+
|
|
414
|
+
# Confirm the write-access list is unchanged.
|
|
415
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/collaborators
|
|
416
|
+
|
|
417
|
+
# Review all workflows for any pull_request trigger type that could run
|
|
418
|
+
# attacker-controlled code with repo secrets (e.g. `types: [opened]`
|
|
419
|
+
# without a trusted-author gate).
|
|
420
|
+
grep -rn "pull_request:" .github/workflows/
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
- **Post-incident reactivation.** After audit completes and root cause is identified and patched, re-register the Trusted Publisher via the npm UI with the four fields matching the table in `## npm Trusted Publisher` above (`organization=armelhbobdad`, `repository=bmad-module-skill-forge`, `workflow filename=release.yaml`, `environment=release`). Re-verify via an alpha cut (dispatch `release.yaml` from a temporarily-allowed feature branch per the `## Release Environment § Temporarily allowing a feature branch` procedure) before any stable release.
|
|
424
|
+
- **Pre-v1.0.0 context.** Trusted Publisher was pre-registered on 2026-04-20 (Story 1.3). A compromise discovered pre-v1.0.0 is recoverable via delete-and-re-register with no downstream-consumer blast radius (no stable release users yet). Post-v1.0.0, the incident has downstream blast radius and the `support@npmjs.com` coordination path is load-bearing.
|
|
425
|
+
- **Do-NOT clause — `NPM_TOKEN` rotation is not OIDC incident response.** Do NOT rotate `NPM_TOKEN` as a first response. The token is not on the OIDC path; rotating it does nothing to stop an OIDC compromise.
|
|
426
|
+
- If `NPM_TOKEN` itself is _also_ suspected compromised, revoke via `https://www.npmjs.com` → **Access Tokens** and remove from the repo: `gh secret delete NPM_TOKEN --repo armelhbobdad/bmad-module-skill-forge`. This is separate from the OIDC incident response above.
|
|
427
|
+
- `NPM_TOKEN` still exists at repo scope until Story 6.3 (post-v1.0.0). A compromised `NPM_TOKEN` is a **separate incident class** from OIDC compromise. During the window where OIDC is revoked _and_ `NPM_TOKEN` is also compromised, there is no valid publish path — the repo enters lockdown until Trusted Publisher is re-registered. Document the flip in the incident post-mortem; do not publish via any stale path.
|
|
428
|
+
|
|
429
|
+
### Scenario G — "release.yaml disabled, reverted, or missing from main"
|
|
430
|
+
|
|
431
|
+
- **Trigger.** `release.yaml` is the single-rooted release workflow (Story 3.3 Patch A neutralized the legacy `publish.yaml` `v*` tag trigger). If `release.yaml` is reverted, renamed, moved, or disabled at the repo-settings level (`gh workflow disable release.yaml`) and someone tries to cut a release, **no workflow fires**. Silent no-op until a maintainer checks `npm view` or the Actions UI.
|
|
432
|
+
- **Detection.** After any PR that touches `.github/workflows/*` — and before any release attempt — run:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/actions/workflows \
|
|
436
|
+
--jq '.workflows[] | select(.name=="Release") | {name, state, path}'
|
|
437
|
+
# expected: {"name":"Release","state":"active","path":".github/workflows/release.yaml"}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
If `state` is not `active` (the GitHub Actions API reports `disabled_manually` when disabled via the UI or `gh workflow disable`), or no matching workflow is returned at all (file renamed, moved, or deleted), Scenario G applies.
|
|
441
|
+
|
|
442
|
+
- **Recovery CLI.**
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# Case 1: workflow is disabled but the file is present.
|
|
446
|
+
gh workflow enable release.yaml
|
|
447
|
+
|
|
448
|
+
# Case 2: the file is missing or reverted.
|
|
449
|
+
# Revert the offending PR via the normal branch-protection review path.
|
|
450
|
+
# Do NOT hand-edit main — branch protection blocks direct push anyway.
|
|
451
|
+
gh pr view <offending-pr-number>
|
|
452
|
+
# `gh pr revert` opens a NEW revert PR; it does not merge anything itself.
|
|
453
|
+
# Capture the URL it prints, then approve + merge that revert PR through
|
|
454
|
+
# the standard review path before retrying the release.
|
|
455
|
+
gh pr revert <offending-pr-number>
|
|
456
|
+
# After approval (manual review step required by branch protection):
|
|
457
|
+
gh pr merge <revert-pr-number> --squash
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
- **Expected outcome.** `release.yaml` is back on `main` with `state: "active"`; next dispatch fires normally.
|
|
461
|
+
- **Prevention.** Single-root invariants to spot-check after any workflow-dir PR. The globs below cover both `.yaml` and `.yml` extensions (the repo currently has at least one `.yml` workflow):
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
# Any workflow with id-token: write should be a known release/provenance workflow.
|
|
465
|
+
grep -l 'id-token: write' .github/workflows/*.{yaml,yml} 2>/dev/null
|
|
466
|
+
# expected set:
|
|
467
|
+
# - docs.yaml (GitHub Pages — orthogonal to release)
|
|
468
|
+
# - publish.yaml (DEPRECATED — kept until Story 6.1 deletes it; workflow_dispatch-only,
|
|
469
|
+
# surviving as the Scenario G emergency hatch)
|
|
470
|
+
# - release.yaml (canonical)
|
|
471
|
+
|
|
472
|
+
# No `v*` push trigger should exist in any workflow — release.yaml is workflow_dispatch-only,
|
|
473
|
+
# and publish.yaml had its push:tags:v* trigger neutralized in Story 3.3 Patch A.
|
|
474
|
+
# Scan the 3 lines following each `push:` block for a v* tag pattern.
|
|
475
|
+
grep -A3 -E '^\s*push:' .github/workflows/*.{yaml,yml} 2>/dev/null \
|
|
476
|
+
| grep -E "['\"]v\*['\"]"
|
|
477
|
+
# expected: zero matches.
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
- **Escalation path — emergency hatch.** If Scenario G is detected mid-incident when a release is urgently needed, the legacy `.github/workflows/publish.yaml` retains `workflow_dispatch:` as an emergency hatch (NPM_TOKEN still at repo scope until Story 6.3). Invoke via:
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
gh workflow run publish.yaml
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Read the next two paragraphs **before** dispatching:
|
|
487
|
+
- **Auth vs. provenance — they are independent.** `publish.yaml` authenticates to npm with `NODE_AUTH_TOKEN=${{ secrets.NPM_TOKEN }}` (token-based; NOT OIDC trusted publishing — that path is `release.yaml`-only). However, the workflow also has `id-token: write` permission and calls `npm publish --provenance`, so npm WILL attach a SLSA-L2 provenance attestation derived from the GitHub Actions OIDC token. Provenance is independent of the auth-to-npm path. The trade-off is auth lineage (token vs. trusted-publisher OIDC), not attestation presence.
|
|
488
|
+
- **State divergence — `publish.yaml` does NOT bump version, push the `v*` tag, update `CHANGELOG.md`, or create a GitHub Release.** It only runs `npm publish` against whatever `package.json` is at HEAD of the dispatched ref. Every emergency-hatch use therefore leaves npm + git state diverged and requires a Scenario E-style reconciliation afterward: bump `package.json` manually on `main` (so the next release does not collide), then recreate the tag and GitHub Release per Scenario E.
|
|
489
|
+
|
|
490
|
+
Document every emergency-hatch use in an incident post-mortem, and re-enable `release.yaml` before the next release. This fallback disappears when Story 6.1 deletes `publish.yaml`.
|
|
491
|
+
|
|
492
|
+
- **Constraints.** Branch protection on `main` blocks direct pushes — recovery goes through a PR in every case. Do not attempt to sidestep branch protection to "fix" `release.yaml` faster; the cost of a bad release (NFR5 audit-trail breakage, NFR10 commit-trail breakage) far exceeds the cost of a normal-review PR.
|
|
493
|
+
|
|
494
|
+
### Cross-reference matrix
|
|
495
|
+
|
|
496
|
+
| Scenario | Trigger class | Primary CLI verb | NFR linkage |
|
|
497
|
+
| -------- | ------------------------------- | -------------------------------- | ----------------- |
|
|
498
|
+
| A | Fresh bad latest | `dist-tag` + `deprecate` | NFR3 |
|
|
499
|
+
| B | Stale bad latest | `deprecate` + ship forward | NFR3, NFR6 |
|
|
500
|
+
| C | Eligible unpublish | `unpublish` | (pre-v1.0.0 only) |
|
|
501
|
+
| D | Tag orphan (publish failed) | `tag -d` + `push --delete` | NFR12 |
|
|
502
|
+
| E | Post-publish state drift | `tag -a` + `gh release create` | NFR5 |
|
|
503
|
+
| F | OIDC compromise | revoke Trusted Publisher + audit | NFR7 |
|
|
504
|
+
| G | `release.yaml` disabled/missing | `workflow enable` / `pr revert` | NFR5, NFR10 |
|
|
505
|
+
|
|
506
|
+
### Baseline snapshots
|
|
507
|
+
|
|
508
|
+
The ruleset-restore snippet at the top of this document (`## Branch Protection on main § Restore from a saved baseline`) uses `gh api --method PUT .../rulesets/<id>`. That call returns `404` if the `Default` ruleset has been deleted, not merely edited. Keeping a recent `baseline-ruleset-Default-YYYYMMDD.json` on disk lets the PUT-then-POST fallback below recover from either case.
|
|
509
|
+
|
|
510
|
+
**One-time capture pattern** — run after any maintainer-initiated change to the ruleset or to the `release` environment, **not on a schedule**:
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
# Capture the current Default ruleset as a disaster-recovery baseline.
|
|
514
|
+
# Uses name-based lookup so the snippet survives ruleset re-creation (IDs change, names don't).
|
|
515
|
+
RULESET_ID=$(gh api repos/armelhbobdad/bmad-module-skill-forge/rulesets \
|
|
516
|
+
--jq '.[] | select(.name=="Default") | .id')
|
|
517
|
+
gh api "repos/armelhbobdad/bmad-module-skill-forge/rulesets/$RULESET_ID" \
|
|
518
|
+
> "_bmad-output/planning-artifacts/baseline-ruleset-Default-$(date +%Y%m%d).json"
|
|
519
|
+
|
|
520
|
+
# Capture the release environment + its branch policy list (two separate resources).
|
|
521
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/environments/release \
|
|
522
|
+
> "_bmad-output/planning-artifacts/baseline-env-release-$(date +%Y%m%d).json"
|
|
523
|
+
gh api repos/armelhbobdad/bmad-module-skill-forge/environments/release/deployment-branch-policies \
|
|
524
|
+
> "_bmad-output/planning-artifacts/baseline-env-release-branch-policies-$(date +%Y%m%d).json"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Baselines live in `_bmad-output/planning-artifacts/` (already git-tracked, not shipped with the npm package). The filename date stamp makes the freshness of the baseline obvious at a glance.
|
|
528
|
+
|
|
529
|
+
**Ruleset-deletion recovery path — PUT-then-POST.** The canonical restore flow is:
|
|
530
|
+
|
|
531
|
+
1. Try `PUT` first. If the ruleset exists but drifted, this reconciles it in place.
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
RULESET_ID=$(gh api repos/armelhbobdad/bmad-module-skill-forge/rulesets \
|
|
535
|
+
--jq '.[] | select(.name=="Default") | .id')
|
|
536
|
+
# Pick the most recent baseline file (the YYYYMMDD suffix is a real date
|
|
537
|
+
# written by the capture command above; the glob avoids hard-coding it).
|
|
538
|
+
BASELINE=$(ls -t _bmad-output/planning-artifacts/baseline-ruleset-Default-*.json \
|
|
539
|
+
| head -1)
|
|
540
|
+
jq '{name, target, enforcement, conditions, rules, bypass_actors}' "$BASELINE" \
|
|
541
|
+
> /tmp/restore.json
|
|
542
|
+
gh api --method PUT \
|
|
543
|
+
"repos/armelhbobdad/bmad-module-skill-forge/rulesets/$RULESET_ID" \
|
|
544
|
+
--input /tmp/restore.json
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
2. If the `PUT` returns `404` (ruleset was deleted, not edited), recreate via `POST`:
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
gh api --method POST \
|
|
551
|
+
repos/armelhbobdad/bmad-module-skill-forge/rulesets \
|
|
552
|
+
--input /tmp/restore.json
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
The `POST` body needs the same `name`, `target`, `enforcement`, `conditions`, `rules`, and `bypass_actors` fields as the PUT — all captured by the baseline snapshot above. Try `PUT` first; on `404`, `POST` recreates with identical semantics. The ruleset gets a new id on re-creation (IDs are not stable across delete+create cycles); update any hard-coded id references in this document on the next planned edit pass.
|
|
556
|
+
|
|
557
|
+
For the `release` environment, a deletion+restore similarly uses the two-call pattern already documented at `## Release Environment § Restore / re-apply` — feed the environment-level baseline JSON to the `PUT .../environments/release` call, then re-POST each entry from the branch-policies baseline to `.../environments/release/deployment-branch-policies`.
|
|
558
|
+
|
|
559
|
+
### Cutting v1.0.0-rc.1
|
|
560
|
+
|
|
561
|
+
- **Trigger.** Story 5.1 audit ([`release-audits/v1.0.0-launch-audit.md`](https://github.com/armelhbobdad/bmad-module-skill-forge/blob/main/release-audits/v1.0.0-launch-audit.md)) signed off and all blockers cleared; Story 5.2 ready to cut the first release-candidate. `package.json` still on a `0.x` line (at audit time: `0.10.0`). Running `release.yaml` with `version_bump: rc` on `0.10.0` would produce `0.10.1-rc.0` — NOT `1.0.0-rc.0` — because `npm version prerelease --preid=rc` advances the patch segment of the current version. The one-time hand-bump below lifts `package.json` onto the `1.0.0` line so the next workflow dispatch advances that line cleanly.
|
|
562
|
+
- **CLI.**
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
# Hand-bump main onto the 1.0.0 line. Do not tag; release.yaml owns tags.
|
|
566
|
+
git checkout main && git pull
|
|
567
|
+
npm version 1.0.0-rc.0 --no-git-tag-version
|
|
568
|
+
# Leave .claude-plugin/marketplace.json alone — release.yaml's jq-based
|
|
569
|
+
# sync step rewrites .plugins[0].version atomically at RC cut time. A
|
|
570
|
+
# double-bump here would race against that step.
|
|
571
|
+
# Include package-lock.json — npm version bumps both files; committing
|
|
572
|
+
# only package.json leaves the lockfile out-of-sync and breaks `npm ci`.
|
|
573
|
+
git add package.json package-lock.json
|
|
574
|
+
git commit -m "chore(release): pre-RC bump to 1.0.0-rc.0 (Story 5.1 hand-off)"
|
|
575
|
+
# Push via the normal PR path (branch protection blocks direct push to main).
|
|
576
|
+
# Merge the PR to main after review.
|
|
577
|
+
|
|
578
|
+
# Dispatch the canonical release workflow. `version_bump: rc` runs
|
|
579
|
+
# `npm version prerelease --preid=rc` on 1.0.0-rc.0 → 1.0.0-rc.1.
|
|
580
|
+
gh workflow run release.yaml -f version_bump=rc
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
- **Expected outcome.** `main` tip at `1.0.0-rc.1`; `npm view bmad-module-skill-forge@rc version` returns `1.0.0-rc.1`; a GitHub Release `v1.0.0-rc.1` exists with `prerelease: true` and the auto-generated body; `CHANGELOG.md` now carries an auto-generated `## [1.0.0-rc.1]` entry prepended ABOVE the hand-curated `## [1.0.0] - TBD` placeholder (Story 5.1 staged that placeholder; Story 5.3 reconciles the date at the v1.0.0 final cut); `.claude-plugin/marketplace.json`'s `.plugins[0].version` reflects `1.0.0-rc.1`.
|
|
584
|
+
- **Constraints.**
|
|
585
|
+
- Execute ONLY after `v1.0.0-launch-audit.md § Sign-off` is populated with a date.
|
|
586
|
+
- The pre-RC bump commit subject MUST be `chore(release): pre-RC bump to 1.0.0-rc.0 (Story 5.1 hand-off)` — use the `chore(release):` prefix (not bare `release:`) or the commit-msg hook rejects it for non-conventional form.
|
|
587
|
+
- Do NOT bump `.claude-plugin/marketplace.json` in the hand-bump commit. `release.yaml`'s jq-based marketplace sync handles it atomically at RC cut time; a double-bump here would race the workflow.
|
|
588
|
+
- The RC cut is the FIRST time the jq-based marketplace.json update path runs in anger. A failure there (jq syntax error, missing `jq` on runner — unlikely on `ubuntu-latest` but treat as a real risk) would halt the cut mid-step, possibly leaving a half-updated `CHANGELOG.md` on `main` with no `marketplace.json` follow-through. Recovery: `git revert` the pre-RC bump PR + retry after fixing the workflow.
|
|
589
|
+
- If the workflow dispatch emits `1.0.1-rc.0` instead of `1.0.0-rc.1`, the hand-bump commit never landed on `main` OR `npm version prerelease --preid=rc` behavior diverged from the documented semantics. Abort the RC cut and investigate before retry.
|
|
590
|
+
- **Verification.**
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
# Confirm the rc dist-tag advanced.
|
|
594
|
+
npm view bmad-module-skill-forge@rc version
|
|
595
|
+
# expected: 1.0.0-rc.1
|
|
596
|
+
|
|
597
|
+
npm view bmad-module-skill-forge dist-tags --json | jq -r .rc
|
|
598
|
+
# expected: 1.0.0-rc.1
|
|
599
|
+
|
|
600
|
+
# Confirm SLSA L2 provenance is attached (NFR4 reconfirm).
|
|
601
|
+
npm view bmad-module-skill-forge@1.0.0-rc.1 --json | jq '.dist.attestations'
|
|
602
|
+
# expected: non-null object containing a provenance url
|
|
603
|
+
|
|
604
|
+
# Confirm the GitHub Release exists and is marked prerelease.
|
|
605
|
+
gh release view v1.0.0-rc.1 --json tagName,isPrerelease
|
|
606
|
+
# expected: {"tagName":"v1.0.0-rc.1","isPrerelease":true}
|
|
607
|
+
|
|
608
|
+
# Confirm the CHANGELOG carries both the auto-generated rc entry and the
|
|
609
|
+
# hand-curated v1.0.0 placeholder (Story 5.3 reconciles the placeholder).
|
|
610
|
+
grep -c '^## \[1.0.0-rc.1\]' CHANGELOG.md # expected: 1
|
|
611
|
+
grep -c '^## \[1.0.0\] - TBD' CHANGELOG.md # expected: 1
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
#### Story 3.4 refactor note — PR-auto-merge flow
|
|
615
|
+
|
|
616
|
+
After Story 3.4 (GitHub issue [#198](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/198), [PR #199](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/199)), `release.yaml` no longer pushes the `release: bump to vX.Y.Z` commit directly to `main`. Instead, a main-dispatched cut pushes the release commit to a temp branch `release/bot/vX.Y.Z-<run_id>`, opens a bot PR against `main`, force-triggers `quality.yaml` against the temp branch via `workflow_dispatch` (so the 7 required status checks run and gate the merge), then auto-merges once checks pass and a maintainer approves. Approval is required at **two** gates — the `release` environment gate at job start, and the PR review-decision gate before auto-merge. Non-main dispatches (feature-branch alpha cuts) skip the PR dance and keep the legacy tag-only behavior. Story 5.2's dev agent on resume will refresh the § Cutting v1.0.0-rc.1 prose above to describe the refactored flow directly.
|
package/docs/STABILITY.md
CHANGED
|
@@ -3,7 +3,7 @@ title: SKF Stability & Public API Contract
|
|
|
3
3
|
description: What SemVer covers at v1.0.0 — the surfaces downstream consumers may pin against, and the surfaces explicitly excluded.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> **Status:**
|
|
6
|
+
> **Status:** STABLE — locked at v1.0.0. Amendments follow the "Changes to This Contract" section below.
|
|
7
7
|
|
|
8
8
|
This document is the public API contract for `bmad-module-skill-forge` at v1.0.0. It enumerates exactly which SKF surfaces [Semantic Versioning 2.0.0](https://semver.org/) covers and which are explicitly `@internal`. The goal is that a downstream consumer pinning `"bmad-module-skill-forge": "^1.0.0"` can identify — unambiguously — whether a given SKF change is breaking, additive, or internal. Breaking changes to covered surfaces require a major-version bump; additive changes to covered surfaces are minor bumps; changes to `@internal` surfaces are free to land in any release.
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "bmad-module-skill-forge",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "BMAD module — Turn code and docs into instructions AI agents can actually follow. Progressive capability tiers (Quick/Forge/Forge+/Deep).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bmad",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"lint:md": "markdownlint-cli2 \"**/*.md\"",
|
|
46
46
|
"prepare": "husky || exit 0",
|
|
47
47
|
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run test:schemas && npm run test:install && npm run test:cli && npm run test:workflow && npm run test:python && npm run test:knowledge && npm run validate:schemas && npm run validate:skills && npm run validate:refs && npm run docs:validate-drift",
|
|
48
|
-
"release": "npm run release:patch",
|
|
49
|
-
"release:major": "npm version major && git push --follow-tags",
|
|
50
|
-
"release:minor": "npm version minor && git push --follow-tags",
|
|
51
|
-
"release:patch": "npm version patch && git push --follow-tags",
|
|
52
|
-
"release:prerelease": "npm version prerelease && git push --follow-tags",
|
|
48
|
+
"release": "echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm run release:patch",
|
|
49
|
+
"release:major": "echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm version major && git push --follow-tags",
|
|
50
|
+
"release:minor": "echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm version minor && git push --follow-tags",
|
|
51
|
+
"release:patch": "echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm version patch && git push --follow-tags",
|
|
52
|
+
"release:prerelease": "echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm version prerelease && git push --follow-tags",
|
|
53
53
|
"skf:install": "node tools/cli/skf-cli.js install",
|
|
54
54
|
"skf:status": "node tools/cli/skf-cli.js status",
|
|
55
55
|
"skf:uninstall": "node tools/cli/skf-cli.js uninstall",
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: v1.0.0 Launch Audit
|
|
3
|
+
description: Pre-flight gate audit of every must-have launch-checklist item, with known pre-flight gap dispositions, before the v1.0.0-rc.1 cut.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Metadata
|
|
7
|
+
|
|
8
|
+
- **Date:** 2026-04-23
|
|
9
|
+
- **Responder:** Armel (@armelhbobdad) — Claude Opus 4.7 (1M context)
|
|
10
|
+
- **Baseline SHA:** [`5e407a2`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/5e407a2e69765849108fe6feeef8644542564f7a) — PR #195 merge closing Epic 4 (Stories 4.1 + 4.2 + code-review patches)
|
|
11
|
+
- **Working branch:** `feat/v1-readiness-audit` (fresh branch from `origin/main` at baseline SHA)
|
|
12
|
+
- **Scope:** 10 must-have launch-checklist bullets × 10 pre-flight backlog items (H1–H10) × 8 rollback playbook hardening items (R1–R8)
|
|
13
|
+
- **Epic 4 precondition verification:** `git log main --grep='Story 4' --oneline` returns four commits: `41ccf35` (Story 4.2 patches), `84affc3` (Story 4.2), `e597825` (Story 4.1 patches), `4d0c618` (Story 4.1) — all present on `main` per Epic 4 retro Commitment 1.
|
|
14
|
+
|
|
15
|
+
## Epic 5.1 AC Verification Matrix
|
|
16
|
+
|
|
17
|
+
| # | Bullet | AC reference | Expected artifact | Status | Evidence |
|
|
18
|
+
| --- | ------------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| 1 | `docs/STABILITY.md` exists defining v1.0.0 public API contract | Story 5.1 AC 4 | `docs/STABILITY.md` with `## Covered Surfaces` + 4 sibling H2s; STABLE banner | ✓ | `grep -c '^## Covered Surfaces' docs/STABILITY.md` → `1`; `## Covered Surfaces` H2 at `docs/STABILITY.md:10`; banner at `docs/STABILITY.md:6` flipped to `STABLE` per AC 14 (this story, Task 7) |
|
|
20
|
+
| 2 | `CHANGELOG.md` has a hand-curated v1.0.0 entry added at the top | Story 5.1 AC 5 + AC 13 | `## [1.0.0] - TBD` between `[Unreleased]` and `[0.10.0]` | ✓ | Insertion in `CHANGELOG.md` at lines 7–67 per AC 13 (this story, Task 6) |
|
|
21
|
+
| 3 | `README.md` install instructions verified via `npm pack` + clean-dir smoke test | Story 5.1 AC 6 | `npx <tarball> --version` + piped `install` both succeed | ✓ | See § Install Smoke Test Transcript below (Task 10) |
|
|
22
|
+
| 4 | `package.json` `repository` field populated | Story 5.1 AC 7 | `{"type":"git","url":"git+https://github.com/armelhbobdad/bmad-module-skill-forge.git"}` | ✓ | `jq .repository package.json` at `package.json:24-27` returns the above object |
|
|
23
|
+
| 5 | All advertised CLI flags documented in `--help` output and `docs/` | Story 5.1 AC 8 | Zero per-subcommand flags; STABILITY contract matches code | ✓ | `grep -n 'options: \[\]' tools/cli/commands/*.js` → 4 hits (install/update/status/uninstall); zero `--headless\|--batch\|--purge\|--allow-workspace-drift` hits in `tools/cli/`; matches `docs/STABILITY.md:18-21` contract |
|
|
24
|
+
| 6 | `release.yaml` on `main` + alpha cut history | Story 5.1 AC 9 | Active workflow; `v0.10.1-alpha.0` tag and tarball on npm | ✓ | `gh api .../actions/workflows` returns `{"name":"Release","path":".github/workflows/release.yaml","state":"active"}`; `git tag -l 'v0.10.1-alpha.*'` → `v0.10.1-alpha.0`; alpha cut SHA `2a57dcbd` (Story 3.2, 2026-04-21) |
|
|
25
|
+
| 7 | npm Trusted Publisher registered for this repo | Story 5.1 AC 9 | OIDC publish works; registered via org/repo/workflow/environment 4-tuple | ✓ | `npm view bmad-module-skill-forge dist-tags --json` → `{"latest":"0.10.0","alpha":"0.10.1-alpha.0"}`; alpha SLSA L2 provenance present; `release.yaml` workflow state `active` — combination proves OIDC-through-TP path |
|
|
26
|
+
| 8 | `release` GitHub Environment with required reviewer | Story 5.1 AC 9 | `protection_rules` includes `required_reviewers` | ✓ | `gh api .../environments/release` returns `protection_rules: [{type: "required_reviewers", reviewers: [{type: "User", name: "armelhbobdad"}]}, {type: "branch_policy", reviewers: []}]` |
|
|
27
|
+
| 9 | Branch protection on `main` active with required status checks | Story 5.1 AC 9 | Ruleset `Default` is `active`, targets `branch`, has non-empty `required_status_checks` list | ✓ | Ruleset id `13855503` (name-based lookup `name=="Default"`) is `enforcement: "active"`, `target: "branch"`; 7 required contexts: `prettier`, `eslint`, `markdownlint`, `validate (ubuntu-latest)`, `validate (windows-latest)`, `python (ubuntu-latest)`, `python (windows-latest)` |
|
|
28
|
+
| 10 | `docs/RELEASING.md` rollback playbook exists | Story 5.1 AC 9 | `## Rollback Playbook` + 7 `### Scenario [A-G]` H3s | ✓ | `grep -c '^## Rollback Playbook$' docs/RELEASING.md` → `1`; `grep -c '^### Scenario [A-G] —' docs/RELEASING.md` → `7`; H3s at lines 196 / 228 / 255 / 279 / 311 / 368 / 429 (post-R4/R5/R6 inserts) |
|
|
29
|
+
|
|
30
|
+
## Pre-Flight Backlog Dispositions (H1–H10)
|
|
31
|
+
|
|
32
|
+
| Item | One-line title | Label | Rationale | Link |
|
|
33
|
+
| ---- | --------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| H1 | `tools/cli/commands/install.js:23-26` silent-exit-0 on `{success: false}` | FIXED-IN-THIS-STORY | Added explicit `else { console.error(...); process.exit(1); }` per Story 5.1 AC 10 + Task 4 — falsy `result.success` now exits 1 with a stderr-routed diagnostic | [deferred-work.md → Deferred from: code review of 2-3](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
35
|
+
| H2 | `package.json.main` side-effectful entrypoint | ACCEPTED-AS-INTERNAL | `docs/STABILITY.md:24-28` (Path B) documents this as `@internal`; no programmatic export surface in the v1.0.0 contract, so the side-effect at `require('bmad-module-skill-forge')` is not a public promise and not subject to SemVer | [`docs/STABILITY.md:24-28`](./STABILITY.md) |
|
|
36
|
+
| H3 | `.github/workflows/release.yaml:115` unanchored `sed` on `marketplace.json` | FIXED-IN-THIS-STORY | Replaced with `jq --arg v "$VERSION" '.plugins[0].version = $v'` + temp-file + atomic rename + sanity check, per Story 5.1 AC 11 + Task 5 | [deferred-work.md → Deferred from: code review of 3-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
37
|
+
| H4 | Starlight sidebar does not surface `STABILITY.md` / `RELEASING.md` | FIXED-IN-THIS-STORY | Appended `{ label: 'Stability', slug: 'stability' }` + `{ label: 'Releasing', slug: 'releasing' }` to `website/astro.config.mjs` Reference bucket per Story 5.1 AC 12 + Task 9 | [deferred-work.md → Deferred from: code review of 1-2 / 2-3](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
38
|
+
| H5 | `CHANGELOG.md` missing hand-curated `## [1.0.0]` entry | FIXED-IN-THIS-STORY | Inserted `## [1.0.0] - TBD` section with `v1-0-0-changelog-draft.md` body between lines 5 and 7, and added `(pre-v0.3.0 tag, never published)` parenthetical to the dangling `Revert "0.2.0"` line, per Story 5.1 AC 5 / AC 13 + Task 6 | [deferred-work.md → 2-2](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
39
|
+
| H6 | `docs/STABILITY.md:6` DRAFT banner | FIXED-IN-THIS-STORY | Replaced DRAFT banner with `STABLE — locked at v1.0.0. Amendments follow the "Changes to This Contract" section below.` per Story 5.1 AC 14 + Task 7 | [`docs/STABILITY.md:6`](./STABILITY.md) |
|
|
40
|
+
| H7 | `0.10.1-alpha.0` CHANGELOG `closes [#11]` / `[#12]` pollution | NOT-APPLICABLE | The pollution lived in the immutable `v0.10.1-alpha.0` tarball and in the orphaned commit `2a57dcbd`; current `main`'s `CHANGELOG.md` does NOT contain those refs (`grep -c 'closes \[#1[12]\]' CHANGELOG.md` → `0`). Alpha tarball is historical / immutable; no repo edit | [Epic 3 retro](../_bmad-output/implementation-artifacts/epic-3-retro-2026-04-21.md) |
|
|
41
|
+
| H8 | `docs/RELEASING.md` missing `### Cutting v1.0.0-rc.1` hand-bump procedure | FIXED-IN-THIS-STORY | Authored new H3 section under `## Rollback Playbook` umbrella (last H3, after `### Baseline snapshots`) with the same five-element shape (Trigger / CLI / Outcome / Constraints / Verification) as Scenarios A–G, per Story 5.1 AC 15 + Task 8 | [`docs/RELEASING.md` § Cutting v1.0.0-rc.1](./RELEASING.md) |
|
|
42
|
+
| H9 | `prevent_self_review: false` on `release` env + admin ruleset bypass | DEFERRED-POST-V1 | Solo-maintainer repo today; both flips happen together at second-maintainer onboarding. Standing flag with no fire date | [Epic 3 retro standing flag + `deferred-work.md` 1-2](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
43
|
+
| H10 | `CONTRIBUTING.md` → `docs/` anchor validator coverage | DEFERRED-POST-V1 | Pre-existing tooling gap; not introduced by any Epic 1–4 story; captured as a CI-as-enforced-gate candidate for a dedicated post-v1.0.0 tooling story | [Epic 4 retro + `deferred-work.md`](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
44
|
+
|
|
45
|
+
## Rollback Playbook Hardening Dispositions (R1–R8)
|
|
46
|
+
|
|
47
|
+
| Item | One-line title | Label | Rationale | Link |
|
|
48
|
+
| ---- | --------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| R1 | Baseline `jq` filter GET-vs-PUT shape | DEFERRED-POST-V1 | Requires a round-trip restore drill to validate against the live ruleset; not suitable for a story authoring pass | [`deferred-work.md` → code review of 4-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
50
|
+
| R2 | Scenario G ghost workflows detection | DEFERRED-POST-V1 | Requires augmenting detection with `git ls-tree` check; bundled into a post-v1.0.0 ops story | [`deferred-work.md` → code review of 4-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
51
|
+
| R3 | Scenario G `gh workflow enable` admin permission | DEFERRED-POST-V1 | Couples with H9 (second-maintainer onboarding); dormant today | [`deferred-work.md` → code review of 4-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
52
|
+
| R4 | Scenario A npm-`dist-tags` CDN cache lag caveat | BUNDLED-IN-THIS-STORY | Added as a blockquote callout in `docs/RELEASING.md` Scenario A (near the `npm view … dist-tags` command) per Story 5.1 AC 16 + Task 8 | [`docs/RELEASING.md` Scenario A](./RELEASING.md) |
|
|
53
|
+
| R5 | Scenario C npm-clock-vs-local-clock caveat for 72h window | BUNDLED-IN-THIS-STORY | Added as inline sentence in Scenario C (72h-window paragraph) directing responders to compute the boundary from `time.modified` per Story 5.1 AC 16 + Task 8 | [`docs/RELEASING.md` Scenario C](./RELEASING.md) |
|
|
54
|
+
| R6 | Scenario C "zero downloads" stats-lag caveat | BUNDLED-IN-THIS-STORY | Added as blockquote callout on the "zero downloads, zero dependents" criterion directing responders to treat C as B in the first 24–48h per Story 5.1 AC 16 + Task 8 | [`docs/RELEASING.md` Scenario C](./RELEASING.md) |
|
|
55
|
+
| R7 | Scenario D tag-protection hardening | DEFERRED-POST-V1 | Speculative; no tag-protection rule exists today | [`deferred-work.md` → code review of 4-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
56
|
+
| R8 | Scenario F 2FA-degraded account-recovery variant | DEFERRED-POST-V1 | Incident-response design; belongs with Scenario F hardening post-v1.0.0 | [`deferred-work.md` → code review of 4-1](../_bmad-output/implementation-artifacts/deferred-work.md) |
|
|
57
|
+
|
|
58
|
+
## External Verification Transcripts
|
|
59
|
+
|
|
60
|
+
Live outputs collected during Task 2 of Story 5.1 dev (2026-04-23).
|
|
61
|
+
|
|
62
|
+
### `release.yaml` on `main` — workflow state
|
|
63
|
+
|
|
64
|
+
```console
|
|
65
|
+
$ gh api repos/armelhbobdad/bmad-module-skill-forge/actions/workflows \
|
|
66
|
+
--jq '.workflows[] | select(.path==".github/workflows/release.yaml") | {name, state, path}'
|
|
67
|
+
{"name":"Release","path":".github/workflows/release.yaml","state":"active"}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `release` GitHub Environment protection rules
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
$ gh api repos/armelhbobdad/bmad-module-skill-forge/environments/release \
|
|
74
|
+
--jq '{name, protection_rules: [.protection_rules[] | {type, reviewers: ([.reviewers // [] | .[] | {type, name: .reviewer.login // .reviewer.name}])}]}'
|
|
75
|
+
{"name":"release","protection_rules":[{"reviewers":[{"name":"armelhbobdad","type":"User"}],"type":"required_reviewers"},{"reviewers":[],"type":"branch_policy"}]}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Branch protection ruleset for `main` — enforcement + required status checks
|
|
79
|
+
|
|
80
|
+
```console
|
|
81
|
+
$ RULESET_ID=$(gh api repos/armelhbobdad/bmad-module-skill-forge/rulesets \
|
|
82
|
+
--jq '.[] | select(.name=="Default") | .id')
|
|
83
|
+
$ echo "$RULESET_ID"
|
|
84
|
+
13855503
|
|
85
|
+
|
|
86
|
+
$ gh api "repos/armelhbobdad/bmad-module-skill-forge/rulesets/$RULESET_ID" \
|
|
87
|
+
--jq '{id: .id, target, enforcement, rule_types: [.rules[].type]}'
|
|
88
|
+
{"enforcement":"active","id":13855503,"rule_types":["deletion","non_fast_forward","pull_request","code_quality","required_status_checks"],"target":"branch"}
|
|
89
|
+
|
|
90
|
+
$ gh api "repos/armelhbobdad/bmad-module-skill-forge/rulesets/$RULESET_ID" \
|
|
91
|
+
--jq '[.rules[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context]'
|
|
92
|
+
["prettier","eslint","markdownlint","validate (ubuntu-latest)","validate (windows-latest)","python (ubuntu-latest)","python (windows-latest)"]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### npm dist-tags + alpha attestation
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
$ npm view bmad-module-skill-forge dist-tags --json
|
|
99
|
+
{
|
|
100
|
+
"latest": "0.10.0",
|
|
101
|
+
"alpha": "0.10.1-alpha.0"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
$ npm view bmad-module-skill-forge@0.10.1-alpha.0 --json | jq '.dist.attestations // "absent"'
|
|
105
|
+
{
|
|
106
|
+
"url": "https://registry.npmjs.org/-/npm/v1/attestations/bmad-module-skill-forge@0.10.1-alpha.0",
|
|
107
|
+
"provenance": {
|
|
108
|
+
"predicateType": "https://slsa.dev/provenance/v1"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
$ git tag -l 'v0.10.1-alpha.*'
|
|
113
|
+
v0.10.1-alpha.0
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The alpha SLSA L2 provenance confirms that Story 3.2's alpha cut (`2a57dcbd`, 2026-04-21) published through the registered OIDC Trusted Publisher path. The canonical four-field TP match (org / repo / workflow filename / environment) is not visible via public API — a successful attested publish IS the live proof of registration.
|
|
117
|
+
|
|
118
|
+
### Story 4.2 `[DEPRECATED]`-on-stderr divergence — audit decision
|
|
119
|
+
|
|
120
|
+
Current `package.json:48` script body (verbatim):
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
echo '[DEPRECATED] Use .github/workflows/release.yaml via GitHub Actions. See docs/RELEASING.md.' >&2 && exit 1 ; npm run release:patch
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Story 4.2 AC 5 specified `echo '[DEPRECATED] …' && exit 1` (stdout-routed) as the literal form. The applied patch `41ccf35` routes the `echo` to `>&2` (stderr) — a divergence from AC 5's literal shape. Story 4.2's Review Findings Decision→Patch and the Epic 4 retro Commitment 4 both recommend keeping stderr routing.
|
|
127
|
+
|
|
128
|
+
**Decision (audit time):** KEEP stderr routing. **Rationale:** diagnostic notices are stderr by convention across the repo (`tools/cli/commands/install.js` routes `Installation failed.` to `console.error` per the Story 5.1 H1 fix — same principle); Story 6.2's eventual delete-the-script removal is unaffected by the routing choice. **Scope:** no re-edit of `package.json` in Story 5.1 per the seven-file envelope (AC 1).
|
|
129
|
+
|
|
130
|
+
## Install Smoke Test Transcript
|
|
131
|
+
|
|
132
|
+
Procedure per Story 5.1 AC 6 + Task 10: `npm pack` in repo root, then run `--version` and a piped `install` in a clean scratch directory.
|
|
133
|
+
|
|
134
|
+
### Tarball and install invocation
|
|
135
|
+
|
|
136
|
+
On the audit host (`node v24.12.0`, `npm 11.6.2`), the `npx --yes <tarball>` invocation from AC 6 step (c) failed under this npm version with `Permission denied` (npm 11.6.2 treats a `.tgz` argument to `npx` as a local executable rather than an installable package). The equivalent install-then-run form confirms the bin wrapper + commander dispatch resolve cleanly, which is the actual pass criterion for AC 6.
|
|
137
|
+
|
|
138
|
+
```console
|
|
139
|
+
$ npm pack
|
|
140
|
+
[...truncated notices...]
|
|
141
|
+
npm notice name: bmad-module-skill-forge
|
|
142
|
+
npm notice version: 0.10.0
|
|
143
|
+
npm notice filename: bmad-module-skill-forge-0.10.0.tgz
|
|
144
|
+
npm notice package size: 534.2 kB
|
|
145
|
+
npm notice unpacked size: 1.7 MB
|
|
146
|
+
npm notice total files: 228
|
|
147
|
+
bmad-module-skill-forge-0.10.0.tgz
|
|
148
|
+
|
|
149
|
+
$ mkdir -p /tmp/skf-install-smoke && cd /tmp/skf-install-smoke
|
|
150
|
+
$ npm install /path/to/bmad-module-skill-forge-0.10.0.tgz --no-save
|
|
151
|
+
added 21 packages in 7s
|
|
152
|
+
|
|
153
|
+
$ ./node_modules/.bin/bmad-module-skill-forge --version
|
|
154
|
+
0.10.0
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Pass:** `--version` resolves through the `bin` wrapper (`tools/skf-npx-wrapper.js`) → commander setup (`tools/cli/skf-cli.js`) → prints the declared version `0.10.0` with exit `0`.
|
|
158
|
+
|
|
159
|
+
### `echo | bmad-module-skill-forge install` (piped, first ~40 lines)
|
|
160
|
+
|
|
161
|
+
```console
|
|
162
|
+
$ cd /tmp/skf-install-smoke
|
|
163
|
+
$ echo -e '\n\n\n\n\n' | ./node_modules/.bin/bmad-module-skill-forge install 2>&1 | head -40
|
|
164
|
+
╔════════════════════════════════════════════════════════════════════════╗
|
|
165
|
+
║ ║
|
|
166
|
+
║ ███████╗██╗ ██╗███████╗ ║
|
|
167
|
+
║ ██╔════╝██║ ██╔╝██╔════╝ ║
|
|
168
|
+
║ ███████╗█████╔╝ █████╗ ║
|
|
169
|
+
║ ╚════██║██╔═██╗ ██╔══╝ ║
|
|
170
|
+
║ ███████║██║ ██╗██║ ║
|
|
171
|
+
║ ╚══════╝╚═╝ ╚═╝╚═╝ ║
|
|
172
|
+
║ ║
|
|
173
|
+
╟────────────────────────────────────────────────────────────────────────╢
|
|
174
|
+
║ Skill Forge ⚒ Agent Skill Compiler ║
|
|
175
|
+
║ Turn code and docs into instructions AI agents can actually follow. ║
|
|
176
|
+
║ v0.10.0 · MIT License · Open Source ║
|
|
177
|
+
╚════════════════════════════════════════════════════════════════════════╝
|
|
178
|
+
...
|
|
179
|
+
┌ Skill Forge Installer
|
|
180
|
+
│
|
|
181
|
+
● Target: /tmp/skf-install-smoke
|
|
182
|
+
│
|
|
183
|
+
● Skills will be installed in _bmad/skf/
|
|
184
|
+
◆ Project name:
|
|
185
|
+
│ skf-install-smoke
|
|
186
|
+
└
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Pass:** no `command not found`, no `ERR!`, no immediate non-zero exit. The `install` subcommand dispatch reaches the `ui.promptInstall()` interactive path, renders the banner, and blocks on the `Project name:` prompt (expected — `npx-wrapper` → `commander` → `commands/install.js` action → `UI.promptInstall()` resolves correctly). Completing the interactive flow end-to-end is out of scope for this smoke test (it is Story 5.2 / 5.4 territory); the pass criterion here is that the install code path is reachable with no structural error.
|
|
190
|
+
|
|
191
|
+
**Clean-up:** the tarball (`bmad-module-skill-forge-0.10.0.tgz`) and the scratch directory (`/tmp/skf-install-smoke`) were deleted after the transcript was captured. `git status --short` at commit time confirms no tarball in the working tree (see § Post-commit footer).
|
|
192
|
+
|
|
193
|
+
## Blockers
|
|
194
|
+
|
|
195
|
+
<!-- Populated at Task 11 / Task 12 after all other tasks land. -->
|
|
196
|
+
|
|
197
|
+
None — audit passes clean; Story 5.2 unblocked.
|
|
198
|
+
|
|
199
|
+
## Sign-off
|
|
200
|
+
|
|
201
|
+
Signed off by Armel (@armelhbobdad) at 2026-04-23 16:40Z (post-publish, after smoke test completion). v1.0.0-rc.3 survived smoke test clean; Story 5.3 unblocked.
|
|
202
|
+
|
|
203
|
+
## Post-commit footer
|
|
204
|
+
|
|
205
|
+
- **Audit-completion commit SHA:** [`540fdda`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/540fdda419fc7e71bfdc571dab279b5de1b42578) — seven-file envelope (`docs(release): pre-v1.0.0 readiness audit (Story 5.1)`)
|
|
206
|
+
- **Back-fill commit SHA:** this commit (the one that made this footer sentence self-referential)
|
|
207
|
+
- **`git status --short` after commit:** clean working tree (verified per Story 5.1 AC 1 seven-file envelope)
|
|
208
|
+
|
|
209
|
+
## Story 5.2 RC Cut + Smoke Test
|
|
210
|
+
|
|
211
|
+
### Hand-bump commit
|
|
212
|
+
|
|
213
|
+
- **Commit 1 subject:** `chore(release): pre-RC bump to 1.0.0-rc.0 (Story 5.1 hand-off)`
|
|
214
|
+
- **PR:** [#197](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/197)
|
|
215
|
+
- **Merge commit on main:** [`689501c`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/689501c) (merge-commit pattern, matching PR #195/#196)
|
|
216
|
+
- **Hand-bump commit SHA under merge:** [`3fc1f00`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/3fc1f00)
|
|
217
|
+
- **Scope:** `package.json` + `package-lock.json` — single-line version flip `0.10.0 → 1.0.0-rc.0`. No other files touched per AC 1(a) / AC 16.
|
|
218
|
+
- **`npm run quality`:** all 13 subcommands green before opening PR.
|
|
219
|
+
- **PR checks:** all 7 required status checks passed (eslint 22s, markdownlint 21s, prettier 24s, python ubuntu 24s, python windows 1m22s, validate ubuntu 21s, validate windows 1m10s).
|
|
220
|
+
- **Remote `main` post-merge verification (AC 5 stronger precondition):** `gh api repos/.../contents/package.json` → `1.0.0-rc.0`.
|
|
221
|
+
|
|
222
|
+
### Workflow dispatch run
|
|
223
|
+
|
|
224
|
+
**Seven dispatch attempts** (numbered 1–7 below, of which the 7th passed) were required to land a passing RC end-to-end. Each failed attempt surfaced a distinct latent defect in the release pipeline — none of which had been exercised by the pre-v1.0.0 alpha cut (`v0.10.1-alpha.0`) because that cut dispatched from a feature branch and took the `Skip main push` pattern, bypassing the entire `main`-bound path. Story 5.2 was the first real E2E validation of the canonical path.
|
|
225
|
+
|
|
226
|
+
| # | Run ID | Failed step | Root cause | Remediation |
|
|
227
|
+
|---|---|---|---|---|
|
|
228
|
+
| 1 | [24829166764](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24829166764) | `Push commit to main` | Branch protection ruleset `Default` rejected direct `github-actions[bot]` push (GH013). AC 17's claimed bot-bypass-via-PR was factually wrong. | Story 3.4 refactor: push via auto-merge PR ([issue #198](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/198)); merged as PRs #199 + #200. |
|
|
229
|
+
| 2 | [24838486851](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24838486851) | `Open bot PR` | Repo setting `can_approve_pull_request_reviews: false` blocked `gh pr create` under GITHUB_TOKEN. | Side-fix: `gh api -X PUT /actions/permissions/workflow -F can_approve_pull_request_reviews=true`. Should be codified as a Story 3.4 follow-up. |
|
|
230
|
+
| 3 | [24838762562](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24838762562) | `Wait for required status checks` | `gh pr checks --required --watch` does NOT see check-runs posted by `workflow_dispatch`-triggered runs (they land in a different check suite than the PR's pull_request suite). All 7 required contexts were green on the SHA but invisible to the PR rollup. | Story 3.5: replace `gh pr checks` with direct `/commits/:sha/check-runs` API polling ([issue #202](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/202)); merged as PR #203. |
|
|
231
|
+
| 4 | [24842070205](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24842070205) | `Wait for required status checks` (20-min timeout) | Story 3.5's `for CTX in $(jq -r '.[]')` unquoted command substitution word-split context names with spaces (`validate (ubuntu-latest)`, etc.) into separate tokens. | PR [#205](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/205): swap to `while IFS= read -r CTX; do ... done < <(...)`. |
|
|
232
|
+
| 5 | [24844278359](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24844278359) | `Wait for PR approval or admin-bypass merge` (30-min step timeout) | `gh pr view --json merged` is invalid — the valid field is `state` (returns `"OPEN"`/`"CLOSED"`/`"MERGED"`). The call exited 1, the guard treated it as transient and retried on every poll iteration until the enclosing `timeout-minutes: 30` on the step fired. Blocked both admin-bypass AND formal-approval paths. | PR [#207](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/207): swap `--json merged` → `--json state` + string comparison update in both Wait steps. |
|
|
233
|
+
| 6 | [24845860915](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24845860915) | `Create and push tag` | Transient GitHub git-backend 500 on `git fetch origin main` (`remote: Internal Server Error` + `fatal: unable to access ...: The requested URL returned error: 500`, raw log from the failed step). The tag-step's Story 3.4 P9 defensive guard correctly bailed rather than anchoring on stale `origin/main` tip. Not a code bug; the guard behaved as designed. | Dispatch again; 500 self-resolved on retry. |
|
|
234
|
+
| 7 (passing) | [24846332151](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24846332151) | — | Full end-to-end success. | — |
|
|
235
|
+
|
|
236
|
+
- **Passing run:** [24846332151](https://github.com/armelhbobdad/bmad-module-skill-forge/actions/runs/24846332151)
|
|
237
|
+
- **Start:** `2026-04-23T16:21:33Z`
|
|
238
|
+
- **End:** `2026-04-23T16:27:30Z`
|
|
239
|
+
- **Duration:** 5m 4s
|
|
240
|
+
- **Conclusion:** `success`
|
|
241
|
+
- **Release commit subject:** `release: bump to v1.0.0-rc.3`
|
|
242
|
+
- **Release commit SHA:** [`78ac999`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/78ac999) (authored by `github-actions[bot]`)
|
|
243
|
+
- **Bot PR merged via:** [#209](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/209) (admin-bypass-merge by @armelhbobdad at `2026-04-23T16:27:20Z`)
|
|
244
|
+
- **Merge commit on main:** [`5fad470`](https://github.com/armelhbobdad/bmad-module-skill-forge/commit/5fad470)
|
|
245
|
+
|
|
246
|
+
### npm publish evidence
|
|
247
|
+
|
|
248
|
+
Excerpted fields from `npm view bmad-module-skill-forge@1.0.0-rc.3 --json` (full output is ~200 lines; the fields below are the ones the ACs gate on):
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"version": "1.0.0-rc.3",
|
|
253
|
+
"dist": {
|
|
254
|
+
"tarball": "https://registry.npmjs.org/bmad-module-skill-forge/-/bmad-module-skill-forge-1.0.0-rc.3.tgz",
|
|
255
|
+
"shasum": "85bcb22f65cb98debda06c9ec22062bdc85fea4f",
|
|
256
|
+
"attestations": {
|
|
257
|
+
"url": "https://registry.npmjs.org/-/npm/v1/attestations/bmad-module-skill-forge@1.0.0-rc.3",
|
|
258
|
+
"provenance": { "predicateType": "https://slsa.dev/provenance/v1" }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`npm view bmad-module-skill-forge dist-tags --json` before and after the cut:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
// Pre-publish baseline (recorded at Task 1 recon per AC 7 baseline requirement)
|
|
268
|
+
{ "latest": "0.10.0", "alpha": "0.10.1-alpha.0" }
|
|
269
|
+
|
|
270
|
+
// Post-publish (observed after run 24846332151 completed)
|
|
271
|
+
{ "latest": "0.10.0", "alpha": "0.10.1-alpha.0", "rc": "1.0.0-rc.3" }
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`npm view bmad-module-skill-forge versions --json` (all published versions — confirms rc.1/rc.2 were never published to the registry):
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
["0.2.0","0.3.0","0.4.0","0.5.0","0.6.0","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.9.0","0.10.0","0.10.1-alpha.0","1.0.0-rc.3"]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- **AC 6 provenance:** ✅ SLSA L2 attestation present (`provenance/v1`).
|
|
281
|
+
- **AC 7 dist-tag:** ✅ `rc: 1.0.0-rc.3` added; `latest: 0.10.0` preserved from baseline; `alpha: 0.10.1-alpha.0` preserved from baseline. Baseline-vs-post diff is additive only.
|
|
282
|
+
|
|
283
|
+
### GitHub Release v1.0.0-rc.3
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"tagName": "v1.0.0-rc.3",
|
|
288
|
+
"isPrerelease": true,
|
|
289
|
+
"createdAt": "2026-04-23T16:27:20Z",
|
|
290
|
+
"name": "Skill Forge (SKF) v1.0.0-rc.3",
|
|
291
|
+
"url": "https://github.com/armelhbobdad/bmad-module-skill-forge/releases/tag/v1.0.0-rc.3"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Main-branch side-effects
|
|
296
|
+
|
|
297
|
+
- `git log main -1 --format='%H %s'` → `5fad470 Merge pull request #209 from armelhbobdad/release/bot/v1.0.0-rc.3-24846332151`
|
|
298
|
+
- `git log main --grep='bump to v1.0.0-rc.3' --oneline` → `78ac999 release: bump to v1.0.0-rc.3` (workflow-authored, one behind merge tip)
|
|
299
|
+
- `jq -r .version package.json` → `1.0.0-rc.3` ✅ (AC 8b)
|
|
300
|
+
- `jq -r '.plugins[0].version' .claude-plugin/marketplace.json` → `1.0.0-rc.3` ✅ (AC 8c — **first live proof of Story 5.1 H3 jq fix working end-to-end**)
|
|
301
|
+
- `grep -c '^## \[1.0.0-rc.3\]' CHANGELOG.md` → `1` ✅ (AC 8d)
|
|
302
|
+
- `grep -c '^## \[1.0.0\] - TBD' CHANGELOG.md` → `1` ✅ (AC 8e — placeholder survives)
|
|
303
|
+
- `head -5 CHANGELOG.md` → `# Changelog` + preamble + `## [Unreleased]` ✅ (AC 8f — Story 3.3 awk preamble-restore verified)
|
|
304
|
+
- `git tag -l 'v1.0.0-rc.3'` → `v1.0.0-rc.3` ✅ (AC 8g)
|
|
305
|
+
- `gh release view v1.0.0-rc.3` → `isPrerelease: true` ✅ (AC 8h)
|
|
306
|
+
|
|
307
|
+
**Topology note (AC 8 re-interpretation under Story 3.4 PR-auto-merge flow):** the pre-3.4 AC 8(a) wording ("main tip SHA advanced by exactly one commit") is outdated — under the new PR-auto-merge flow, main advances by TWO commits per release cut (merge commit + the release commit underneath). `git log main -1` returns the merge commit; the release commit is reachable via `git log main --grep='bump to v1.0.0-rc.3'`. Functionally equivalent; the verification grep in AC 1 still works unchanged.
|
|
308
|
+
|
|
309
|
+
### Clean-env smoke test transcripts
|
|
310
|
+
|
|
311
|
+
**`--version` transcript (AC 9b):** `npx --yes bmad-module-skill-forge@rc --version` in a fresh `/tmp/skf-rc3-smoke/` → printed `1.0.0-rc.3`, exit `0`. The npm `11.6.2` `Permission denied` issue that Story 5.1 Task 10 hit on `.tgz`-path did NOT recur on the registry-resolved `@rc` path — as expected per Story 5.2 Dev Notes (F).
|
|
312
|
+
|
|
313
|
+
**`install` transcript (AC 9d) — deviation from spec noted:** AC 9(d) specified piped-defaults via `printf 'skf-rc1-smoke\n\n\n\n\n' | npx ... install`, but the install TUI's `multiselect` IDE prompt (`required: true`) cannot be driven by piped stdin — the piped newlines get consumed by the first 3 text prompts (project name, skills folder, forge folder), and prompt 4 (multiselect) requires keystroke-level interaction (space + enter). Story 5.1 Task 10 also only smoke-tested `--version`, not the full install. **This is an AC 9 documentation defect, not an RC defect — tracked at [issue #211](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/211) for Story 5.4 / Epic 5 retrospective resolution.** To complete the install smoke test, Armel ran the install **interactively** in a real terminal, selecting Claude Code as the IDE; `echo "install exit: $?"` after completion printed `install exit: 0`. Install completed cleanly with Ferris agent set up and all expected artifacts present:
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
◇ Installation complete! ──────────────────────────────────╮
|
|
317
|
+
│ │
|
|
318
|
+
│ Get Started │
|
|
319
|
+
│ 1. Open this folder in Claude Code │
|
|
320
|
+
│ 2. Activate Ferris: /skf-forger │
|
|
321
|
+
│ 3. Ferris (your Skill Architect) will guide you through │
|
|
322
|
+
│ setting up and forging your first agent skill │
|
|
323
|
+
│ │
|
|
324
|
+
├───────────────────────────────────────────────────────────╯
|
|
325
|
+
│
|
|
326
|
+
└ ⚒ Agent: Ferris (Skill Architect & Integrity Guardian)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Artifact verification:
|
|
330
|
+
|
|
331
|
+
- `ls /tmp/skf-rc3-smoke/_bmad/skf/` (full listing):
|
|
332
|
+
- `config.yaml` (installer-generated)
|
|
333
|
+
- `knowledge/`, `shared/`, `module.yaml`, `module-help.csv` (module metadata)
|
|
334
|
+
- Skill dirs: `skf-analyze-source`, `skf-audit-skill`, `skf-brief-skill`, `skf-create-skill`, `skf-create-stack-skill`, `skf-drop-skill`, `skf-export-skill`, `skf-forger`, `skf-quick-skill`, `skf-refine-architecture`, `skf-rename-skill`, `skf-setup`, `skf-test-skill`, `skf-update-skill`, `skf-verify-stack`
|
|
335
|
+
- `[ -f /tmp/skf-rc3-smoke/_bmad/_config/skf-manifest.yaml ]` → present
|
|
336
|
+
- Zero `Installation failed.` in stderr
|
|
337
|
+
|
|
338
|
+
**Skill count note:** the tarball ships 15 `skf-*` directories (listed above); the `status` command's `Skills: 14` count excludes one of them (likely `skf-setup`, which is a one-shot initialization helper rather than an invocable skill). This 15-vs-14 drift is a status-counting semantics detail, not a shipping defect — recorded here for Story 5.4 / Epic 5 retrospective to codify which `skf-*` prefix entries the count should include.
|
|
339
|
+
|
|
340
|
+
**`status` transcript (AC 10):** `npx --yes bmad-module-skill-forge@rc status` exit `0`. All 6 STABILITY.md contract fields present in stdout:
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
Skill Forge — Status
|
|
344
|
+
v1.0.0-rc.3
|
|
345
|
+
|
|
346
|
+
Installation
|
|
347
|
+
Project: skf-rc3-smoke
|
|
348
|
+
SKF folder: _bmad/skf/
|
|
349
|
+
Agent: installed
|
|
350
|
+
Skills: 14
|
|
351
|
+
Installed: 4/23/2026
|
|
352
|
+
Manifest: present
|
|
353
|
+
|
|
354
|
+
IDEs
|
|
355
|
+
● Claude Code
|
|
356
|
+
|
|
357
|
+
Forge Tier
|
|
358
|
+
Not detected yet — run @Ferris SF
|
|
359
|
+
ast-grep: not detected
|
|
360
|
+
gh CLI: not detected
|
|
361
|
+
ccc: not detected
|
|
362
|
+
QMD: not detected
|
|
363
|
+
|
|
364
|
+
Output Folders
|
|
365
|
+
Skills: skills/ ✓
|
|
366
|
+
Forge data: forge-data/ ✓
|
|
367
|
+
|
|
368
|
+
Sidecar
|
|
369
|
+
State: initialized
|
|
370
|
+
Location: _bmad/_memory/forger-sidecar/
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
| STABILITY.md contract field | Observed | ✓ |
|
|
374
|
+
|---|---|---|
|
|
375
|
+
| installation-present | Installation section rendered | ✓ |
|
|
376
|
+
| installed version string | `v1.0.0-rc.3` | ✓ |
|
|
377
|
+
| forge-tier name | `Not detected yet` (plus ast-grep/gh/ccc/QMD tool rows) | ✓ |
|
|
378
|
+
| configured IDE list | `● Claude Code` | ✓ |
|
|
379
|
+
| workflow-skill count | `Skills: 14` | ✓ |
|
|
380
|
+
| sidecar state | `State: initialized` | ✓ |
|
|
381
|
+
| output-folder paths | `Skills: skills/ ✓` + `Forge data: forge-data/ ✓` | ✓ |
|
|
382
|
+
|
|
383
|
+
**`uninstall` transcript (AC 11):** `printf 'y\n' | npx --yes bmad-module-skill-forge@rc uninstall` — note that unlike `install`, uninstall's only prompt is a simple `Yes/No` confirm which IS satisfied by piped `y\n`. Exit `0`. Final message: `SKF uninstalled successfully.` Post-uninstall:
|
|
384
|
+
|
|
385
|
+
- `ls /tmp/skf-rc3-smoke/_bmad/skf/ 2>/dev/null | wc -l` → `0`
|
|
386
|
+
- `[ -f /tmp/skf-rc3-smoke/_bmad/_config/skf-manifest.yaml ]` → absent ✓
|
|
387
|
+
|
|
388
|
+
### NFR1 cycle-time record
|
|
389
|
+
|
|
390
|
+
- **Dispatch → success wall-clock:** 5m 4s (run 24846332151: `2026-04-23T16:21:33Z` → `16:27:30Z`).
|
|
391
|
+
- **NFR1 target:** 20 min (15 CI + 5 approval).
|
|
392
|
+
- **Status:** **Provisional PASS — 1/10 data points collected** against NFR1's rolling-over-10-releases target. The 5m 4s run is well under the 20-min envelope, but a single observation cannot confirm the rolling target; Stories 5.3 / 5.4 will add the next two data points.
|
|
393
|
+
- **Caveat:** this measurement excludes the cumulative time of attempts 1–6 (all failures). Total story wall-clock from first dispatch attempt to successful publish was ~6 hours including dev-cycle time on Stories 3-4, 3-5, and PRs #205/#207. That cumulative cost is retrospective-relevant but does NOT represent steady-state NFR1 performance — it represents first-time E2E discovery overhead, which by definition cannot repeat.
|
|
394
|
+
|
|
395
|
+
### Decision: v1.0.0-rc.3 status
|
|
396
|
+
|
|
397
|
+
**PASS — cleared for Story 5.3 promotion to v1.0.0 under --tag latest with manual approval.**
|
|
398
|
+
|
|
399
|
+
Note to Story 5.3 dev-agent: the `## [1.0.0] - TBD` block at `CHANGELOG.md:396` was verified accurate against shipped rc.3 behavior (AC 15 — all bullets match: 1 agent Ferris, 14 workflows, 23 IDEs via `platform-codes.yaml`, tier names Quick/Forge/Forge+/Deep match the `Forge Tier` detection section). No CHANGELOG refinements flagged for Story 5.3. The only date-flip Story 5.3 must handle is `- TBD` → `- 2026-04-23` (or whatever date 5.3 dispatches).
|
|
400
|
+
|
|
401
|
+
**Derogation from original AC 1 RC-number envelope:** the originally-planned RC number was `rc.1`; actually-shipped is `rc.3`. Stories 5.1 and 5.2 both authored AC text referencing `rc.1` literal. Under Story 5.2's 7-attempt execution, `rc.1` and `rc.2` were both committed to main (via PR #206's bypass-merge and PR #208's auto-merge respectively) but never tagged or published because downstream defects blocked each. Both are cosmetic-only stale release commits on main — npm immutability does NOT apply (nothing was published under those names); dist-tags never pointed at them. Story 5.3 dispatches on top of `rc.3` → `1.0.0`. This derogation is recorded per AC 12's spirit (defect discovery protocol), though each failure was a pipeline defect rather than an RC-content defect.
|
|
402
|
+
|
|
403
|
+
**Negative-evidence for the rc.1/rc.2 "never tagged/never published" claim** (run 2026-04-23 post-publish):
|
|
404
|
+
|
|
405
|
+
```
|
|
406
|
+
$ git tag -l 'v1.0.0-rc.1' 'v1.0.0-rc.2' 'v1.0.0-rc.3'
|
|
407
|
+
v1.0.0-rc.3
|
|
408
|
+
# rc.1 and rc.2 absent — only rc.3 was tagged
|
|
409
|
+
|
|
410
|
+
$ npm view bmad-module-skill-forge versions --json | jq '. | map(select(startswith("1.0.0-rc")))'
|
|
411
|
+
["1.0.0-rc.3"]
|
|
412
|
+
# rc.1 and rc.2 absent — only rc.3 was published to the registry
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**AC 12 title-convention note (D2 resolution):** AC 12 prescribes issue titles `v1.0.0-rc.N blocker: <one-line>` for failures of AC 6/7/8/9/10/11 (RC-content defects). Issues [#198](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/198) and [#202](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/202) were filed during Story 5.2 with `release.yaml`-prefixed titles instead, because each failure was a pipeline/workflow defect (failing step inside `release.yaml`), not an RC-content defect — none of the ACs 6–11 fired. The title-convention distinction: use `v1.0.0-rc.N blocker:` when the RC itself has a content defect (wrong version on npm, missing attestation, broken install, etc.); use `fix(release):` conventional-commit prefix when the release workflow has a defect. Codify this split at the Epic 5 retrospective.
|
|
416
|
+
|
|
417
|
+
**Lesson for Epic 5 retrospective:** The canonical release path from `main` dispatch had zero prior E2E validation before Story 5.2 attempted it. Stories 3-1 through 3-4 implicitly assumed the path worked; each defect surfaced by Story 5.2 was invisible because the alpha cut (`v0.10.1-alpha.0`) took the non-main-dispatch branch and skipped every affected step. Recommendation: future release-pipeline epics must include a **staging dispatch test** against a sandbox repo (or at minimum a full-workflow dry-run against the default branch) before declaring the pipeline ready. Catching 5+ defects via the first real v1.0.0 cut is demonstrably more expensive than discovering them in pre-launch testing.
|
|
@@ -23,6 +23,9 @@ module.exports = {
|
|
|
23
23
|
if (result && result.success) {
|
|
24
24
|
ui.displaySuccess(config.skfFolder, config.ides, config._action);
|
|
25
25
|
process.exit(0);
|
|
26
|
+
} else {
|
|
27
|
+
console.error(chalk.red('\nInstallation failed.'));
|
|
28
|
+
process.exit(1);
|
|
26
29
|
}
|
|
27
30
|
} catch (error) {
|
|
28
31
|
console.error(chalk.red('\nInstallation failed:'), error.message);
|
|
@@ -52,9 +52,11 @@ module.exports = {
|
|
|
52
52
|
}
|
|
53
53
|
const ui = new UI();
|
|
54
54
|
ui.displaySuccess(SKF_FOLDER, ides, 'update');
|
|
55
|
+
process.exit(0);
|
|
56
|
+
} else {
|
|
57
|
+
console.error(chalk.red('\nUpdate failed.'));
|
|
58
|
+
process.exit(1);
|
|
55
59
|
}
|
|
56
|
-
|
|
57
|
-
process.exit(0);
|
|
58
60
|
} catch (error) {
|
|
59
61
|
console.error(chalk.red('\nUpdate failed:'), error.message);
|
|
60
62
|
process.exit(1);
|