release-skill 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +3 -3
  4. package/CHANGELOG.md +89 -0
  5. package/INSTALL.md +216 -5
  6. package/INSTALL.zh-CN.md +358 -0
  7. package/README.md +411 -67
  8. package/README.zh-CN.md +377 -59
  9. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  10. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  11. package/adapters/claude/bin/release-skill.bundle.mjs +79284 -0
  12. package/adapters/claude/bin/release-skill.mjs +34 -0
  13. package/adapters/claude/native/safe-write/binding.gyp +40 -0
  14. package/adapters/claude/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  15. package/adapters/claude/native/safe-write/prebuilds.json +24 -0
  16. package/adapters/claude/native/safe-write/src/safe_write.cc +2032 -0
  17. package/adapters/claude/schemas/.render-manifest.json +37 -0
  18. package/adapters/claude/schemas/approval-record.schema.json +115 -0
  19. package/adapters/claude/schemas/artifact-lock.schema.json +111 -0
  20. package/adapters/claude/schemas/artifact-plan.schema.json +52 -0
  21. package/adapters/claude/schemas/artifact-policy.schema.json +76 -0
  22. package/adapters/claude/schemas/evidence-event.schema.json +89 -0
  23. package/adapters/claude/schemas/release-plan.schema.json +860 -0
  24. package/adapters/claude/schemas/release-project.schema.json +736 -0
  25. package/adapters/claude/schemas/release-run.schema.json +342 -0
  26. package/adapters/claude/skills/release-assess/SKILL.md +5 -6
  27. package/adapters/claude/skills/release-help/SKILL.md +14 -18
  28. package/adapters/claude/skills/release-prepare/SKILL.md +16 -6
  29. package/adapters/claude/skills/release-publish/SKILL.md +7 -7
  30. package/adapters/claude/skills/release-reconcile/SKILL.md +6 -6
  31. package/adapters/claude/skills/release-setup/SKILL.md +95 -0
  32. package/adapters/claude/skills/release-verify/SKILL.md +7 -7
  33. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  34. package/adapters/codex/bin/release-skill.bundle.mjs +79284 -0
  35. package/adapters/codex/bin/release-skill.mjs +34 -0
  36. package/adapters/codex/native/safe-write/binding.gyp +40 -0
  37. package/adapters/codex/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  38. package/adapters/codex/native/safe-write/prebuilds.json +24 -0
  39. package/adapters/codex/native/safe-write/src/safe_write.cc +2032 -0
  40. package/adapters/codex/schemas/.render-manifest.json +37 -0
  41. package/adapters/codex/schemas/approval-record.schema.json +115 -0
  42. package/adapters/codex/schemas/artifact-lock.schema.json +111 -0
  43. package/adapters/codex/schemas/artifact-plan.schema.json +52 -0
  44. package/adapters/codex/schemas/artifact-policy.schema.json +76 -0
  45. package/adapters/codex/schemas/evidence-event.schema.json +89 -0
  46. package/adapters/codex/schemas/release-plan.schema.json +860 -0
  47. package/adapters/codex/schemas/release-project.schema.json +736 -0
  48. package/adapters/codex/schemas/release-run.schema.json +342 -0
  49. package/adapters/codex/skills/release-assess/SKILL.md +12 -6
  50. package/adapters/codex/skills/release-help/SKILL.md +21 -18
  51. package/adapters/codex/skills/release-prepare/SKILL.md +23 -6
  52. package/adapters/codex/skills/release-publish/SKILL.md +14 -7
  53. package/adapters/codex/skills/release-reconcile/SKILL.md +13 -6
  54. package/adapters/codex/skills/release-setup/SKILL.md +102 -0
  55. package/adapters/codex/skills/release-verify/SKILL.md +14 -7
  56. package/bin/release-skill-cli.mjs +807 -0
  57. package/bin/release-skill.bundle.mjs +79284 -0
  58. package/bin/release-skill.mjs +23 -732
  59. package/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  60. package/native/safe-write/prebuilds.json +22 -2
  61. package/native/safe-write/src/safe_write.cc +11 -2
  62. package/package.json +8 -2
  63. package/references/02-project-config.md +55 -4
  64. package/references/05-evidence-and-errors.md +6 -2
  65. package/schemas/release-plan.schema.json +556 -65
  66. package/schemas/release-project.schema.json +406 -29
  67. package/schemas/release-run.schema.json +165 -18
  68. package/scripts/build-bundle.mjs +133 -0
  69. package/skills/release-assess/SKILL.md +5 -6
  70. package/skills/release-help/SKILL.md +14 -18
  71. package/skills/release-prepare/SKILL.md +16 -6
  72. package/skills/release-publish/SKILL.md +7 -7
  73. package/skills/release-reconcile/SKILL.md +6 -6
  74. package/skills/release-setup/SKILL.md +95 -0
  75. package/skills/release-verify/SKILL.md +7 -7
  76. package/skills-src/release-assess/SKILL.md +5 -6
  77. package/skills-src/release-help/SKILL.md +14 -18
  78. package/skills-src/release-prepare/SKILL.md +16 -6
  79. package/skills-src/release-publish/SKILL.md +7 -7
  80. package/skills-src/release-reconcile/SKILL.md +6 -6
  81. package/skills-src/release-setup/SKILL.md +95 -0
  82. package/skills-src/release-verify/SKILL.md +7 -7
  83. package/src/adapters/contract.mjs +3 -0
  84. package/src/adapters/git-github.mjs +84 -2
  85. package/src/adapters/npm.mjs +5 -13
  86. package/src/adapters/plugin-marketplace.mjs +132 -52
  87. package/src/adapters/push-snapshot.mjs +84 -17
  88. package/src/artifacts/policy.mjs +4 -7
  89. package/src/artifacts/safe-fs-backend-internal.mjs +69 -21
  90. package/src/commands/prepare.mjs +244 -20
  91. package/src/commands/publish.mjs +46 -0
  92. package/src/commands/reconcile.mjs +152 -0
  93. package/src/commands/setup.mjs +1525 -0
  94. package/src/commands/verify.mjs +122 -26
  95. package/src/core/approval.mjs +4 -6
  96. package/src/core/config.mjs +42 -8
  97. package/src/core/errors.mjs +4 -0
  98. package/src/core/pkg-root.mjs +22 -0
  99. package/src/core/plan.mjs +132 -4
  100. package/src/core/previous-public-baseline.mjs +21 -1
  101. package/src/core/run.mjs +4 -4
  102. package/src/core/trusted-resource.mjs +96 -0
  103. package/src/core/verification-gates.mjs +451 -0
  104. package/src/docs/version-gate.mjs +164 -0
  105. package/src/producers/build-adapters.mjs +512 -55
  106. package/src/snapshot/frozen.mjs +221 -7
  107. package/src/snapshot/public-map.mjs +7 -4
  108. package/src/snapshot/scan.mjs +2 -1
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # release-skill
2
2
 
3
- [简体中文](README.zh-CN.md)
3
+ [简体中文](README.zh-CN.md) · Installation: [English](INSTALL.md) / [简体中文](INSTALL.zh-CN.md)
4
4
 
5
+ <!-- release-skill:release-version: 0.1.4 -->
5
6
  Release preparation for Claude Code and Codex, with human-edited files kept intact.
6
7
 
7
8
  release-skill helps a maintainer answer three questions: what will be released,
@@ -10,28 +11,29 @@ reviewed artifacts first and publishes those same artifacts later; it does not
10
11
  regenerate a README or re-pack the live workspace at the last step.
11
12
 
12
13
  <!-- release-skill:capability:external-write-boundary -->
13
- > **Current boundary:** read-only `assess`, offline `prepare`, frozen Git
14
- > branch/tag, GitHub Release, npm tarball, and Claude/Codex marketplace consumer
15
- > installation verification have passed a local production-equivalent protocol
16
- > sandbox using the real release-skill CLI and frozen artifacts, local bare Git
17
- > remotes, and protocol fakes for `gh`, `npm`, Claude, and Codex. Separate
18
- > isolated local probes have exercised the installed Claude/Codex CLIs; no real
19
- > marketplace or production API was contacted. The tests do not provide
20
- > OS-level network isolation. We
21
- > have not run a real production canary for you; treat the first real release as
22
- > a monitored canary. Real APIs, auth, permissions, rate limits, and eventual
23
- > consistency are outside this sandbox claim. `prepare --online` observes previous
24
- > public baselines (bound mode) and fails closed on drift; remote uniqueness checks
25
- > are deferred to publish global preflight.
14
+ > **Current boundary:** v0.1.4 is the current release. v0.1.1 completed a real production release
15
+ > to GitHub and npm the first production-verified milestone — followed by
16
+ > exact npm installation and Claude/Codex consumer installation verification
17
+ > from the frozen Git ref; "current release" and "first production-verified
18
+ > milestone" are two distinct facts and must not be conflated. The same
19
+ > workflow also has a local production-equivalent protocol suite using the
20
+ > real release-skill CLI and frozen artifacts, local bare Git remotes, and
21
+ > protocol fakes for `gh`, `npm`, Claude, and Codex. The suite does not
22
+ > provide OS-level network isolation, and it does not prove that another
23
+ > project's credentials, permissions, rate limits, or eventual-consistency
24
+ > behavior will match this release. Treat each project's first production run
25
+ > as a monitored canary. `prepare --online` observes bound previous-public
26
+ > baselines and fails closed on drift; remote uniqueness checks run during
27
+ > publish global preflight.
26
28
 
27
29
  <!-- release-skill:capability:safe-first-command -->
28
- > **v0.1.1 release candidate:** until `npm view release-skill version` returns
29
- > `0.1.1`, use the source-checkout command below; do not assume the npm command
30
- > already exists.
30
+ > **Production path verified since the v0.1.1 milestone; v0.1.4 is the current
31
+ > release.** The npm-installed CLI is the supported user entry. Source checkout
32
+ > is the development/contributor fallback.
31
33
  >
32
34
  > **Start here:**
33
- > - published package: `release-skill help`
34
- > - current release candidate: `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs" help`
35
+ > - npm install: `npm install -g release-skill` → `release-skill help`
36
+ > - source checkout: `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs" help`
35
37
 
36
38
  <!-- release-skill:maturity:v0.1-boundary -->
37
39
  <!-- release-skill:maturity:boundary -->
@@ -58,6 +60,13 @@ slogans, examples, prose, formatting, and later human edits.
58
60
  intervention; the tool does not force or overwrite them.
59
61
  - Only files listed in `publicFiles` are copied. Add translated READMEs, images,
60
62
  demos, and linked documents explicitly when they belong in the release.
63
+ - A release freezes only the current truth: `prepare` never refreshes or
64
+ rewrites human docs. Maintainers update README, INSTALL, and CHANGELOG first
65
+ — including the machine-readable `release-skill:release-version` markers,
66
+ which must equal the `package.json` version, and the formal CHANGELOG
67
+ heading for the current version — then prepare, review, and approve. A
68
+ pre-release gate fails closed when any doc version marker or the CHANGELOG
69
+ current-version entry drifts.
61
70
 
62
71
  This is the preservation contract: **copy current truth, freeze reviewed
63
72
  truth, and never rewrite human truth.**
@@ -70,12 +79,7 @@ truth, and never rewrite human truth.**
70
79
  - Git 2.30+
71
80
  - A target Git repository with at least one commit
72
81
 
73
- > **v0.1.1 release candidate:** before the production release completes, the
74
- > npm registry may return 404. Use the source-checkout path below for review.
75
- > After `npm view release-skill version` returns `0.1.1`, the npm-installed CLI
76
- > is the supported user entry and the source checkout is a development fallback.
77
-
78
- **Install from npm (supported after v0.1.1 publication):**
82
+ **Install from npm (recommended):**
79
83
 
80
84
  ```bash
81
85
  npm install -g release-skill
@@ -93,7 +97,7 @@ npx release-skill help
93
97
  release-skill help
94
98
  ```
95
99
 
96
- **Development install (from source checkout):**
100
+ **Development install (contributor fallback, from source checkout):**
97
101
 
98
102
  Set the checkout location and install dependencies:
99
103
 
@@ -105,8 +109,6 @@ npm exec --yes pnpm@10.17.1 -- install --frozen-lockfile
105
109
 
106
110
  Then use the CLI via `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs"`.
107
111
 
108
- Create `.release-skill/project.yaml` in the target project:
109
-
110
112
  First keep local plans, approvals, and frozen artifacts out of Git:
111
113
 
112
114
  ```gitignore
@@ -114,7 +116,194 @@ First keep local plans, approvals, and frozen artifacts out of Git:
114
116
  !.release-skill/project.yaml
115
117
  ```
116
118
 
117
- Then create the configuration. npm visibility must be explicit:
119
+ ### First use: deterministic setup without loading the full report
120
+
121
+ Setup is read-only by default. Keep its potentially large report in temporary
122
+ files; show the user or Agent only the deterministic `compactSummary` review
123
+ view. The summary does not replace authorization: `setupDigest` still binds the
124
+ complete facts, candidates, and answers.
125
+
126
+ ```bash
127
+ PROJECT=/absolute/path/to/my-project
128
+ SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
129
+ REPORT="$SETUP_SESSION/discovery.json"
130
+ ANSWERS="$SETUP_SESSION/answers.json"
131
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
132
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
133
+
134
+ release-skill setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
135
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary){console.error("compactSummary missing");process.exit(2)}process.stdout.write(JSON.stringify(r.compactSummary,null,2)+"\n")' "$REPORT"
136
+ ```
137
+
138
+ `NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally exit with code 2. If
139
+ `proposalConflicts` is non-empty—including `PUBLIC_REPO_AUTHORITY_CONFLICT` or
140
+ a public-file mapping conflict—stop and let a human correct the conflicting
141
+ repository or mapping authority, then rerun setup. Do not guess a winner.
142
+
143
+ With no conflicts, copy the machine proposal mechanically. The Agent must not
144
+ rewrite or transcribe it:
145
+
146
+ ```bash
147
+ SETUP_SESSION='/session-directory-absolute-path-printed-above'
148
+ PROJECT='/project-absolute-path-printed-above'
149
+ REPORT="$SETUP_SESSION/discovery.json"
150
+ ANSWERS="$SETUP_SESSION/answers.json"
151
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
152
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if((r.proposalConflicts??[]).length){console.error("proposal conflicts require human resolution");process.exit(2)}if(!r.recommendedAnswers){console.error("recommendedAnswers missing");process.exit(2)}fs.writeFileSync(process.argv[2],JSON.stringify(r.recommendedAnswers,null,2)+"\n",{flag:"wx",mode:0o600})' "$REPORT" "$ANSWERS"
153
+
154
+ release-skill setup --root "$PROJECT" --answers "$ANSWERS" --json > "$BOUND_REPORT"
155
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary||!r.setupDigest){console.error("bound setup report incomplete");process.exit(2)}process.stdout.write(JSON.stringify({compactSummary:r.compactSummary,setupDigest:r.setupDigest},null,2)+"\n")' "$BOUND_REPORT"
156
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
157
+ ```
158
+
159
+ Review that bound summary and exact digest once. After explicit human
160
+ confirmation, use the confirmed digest literal to create the configuration:
161
+
162
+ ```bash
163
+ SETUP_SESSION=<session-directory-absolute-path-printed-above>
164
+ PROJECT=<project-absolute-path-printed-above>
165
+ ANSWERS="$SETUP_SESSION/answers.json"
166
+ CREATED_REPORT="$SETUP_SESSION/created.json"
167
+ POST_REPORT="$SETUP_SESSION/post-setup.json"
168
+ ASSESS_REPORT="$SETUP_SESSION/assess.json"
169
+ release-skill setup --root "$PROJECT" --answers "$ANSWERS" \
170
+ --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT"
171
+ release-skill setup --root "$PROJECT" --json > "$POST_REPORT"
172
+ set +e
173
+ release-skill assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
174
+ ASSESS_EXIT=$?
175
+ set -e
176
+ [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
177
+ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>JSON.parse(fs.readFileSync(x,"utf8")));if(c.status!=="CONFIG_CREATED"||p.status!=="ALREADY_CONFIGURED"||!["ASSESSED","NEEDS_INPUT","BLOCKED"].includes(a.status)){process.exit(2)}process.stdout.write(JSON.stringify({created:c.status,postSetup:p.status,assessment:{status:a.status,summary:a.summary,gapCount:(a.gaps??[]).length,blockingCodes:(a.gaps??[]).filter(g=>g.severity==="error").map(g=>g.code)}},null,2)+"\n")' "$CREATED_REPORT" "$POST_REPORT" "$ASSESS_REPORT"
178
+ node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
179
+ ```
180
+
181
+ The write must return `CONFIG_CREATED`; the next setup must return
182
+ `ALREADY_CONFIGURED`. Existing configuration is never regenerated—make only
183
+ reviewed incremental edits. Discovered interpreter/package-manager scripts are
184
+ `SIDE_EFFECTS_UNPROVEN` and are not selected automatically. Add a project-specific
185
+ hook or gate only after human review: edit `projectConfig.hooks`, or edit
186
+ `verificationGates` and add the same id to `selectedGateIds`, then rerun the
187
+ bound dry-run. Keep human files at `mode: preserve`, and
188
+ use `sourceScope: workspace` only for explicit cross-unit shared sources.
189
+
190
+ #### Advanced schema reference—not the first-use path
191
+
192
+ The wrapper below illustrates the schema only. Do not hand-write it during the
193
+ normal setup path; mechanically extract `recommendedAnswers` as shown above.
194
+
195
+ ```json
196
+ {
197
+ "projectConfig": {
198
+ "apiVersion": "release-skill/v1",
199
+ "kind": "ReleaseProject",
200
+ "project": { "name": "my-project", "defaultBranch": "main" },
201
+ "releaseUnits": [{
202
+ "id": "my-project",
203
+ "source": ".",
204
+ "publicRepo": "owner/my-project",
205
+ "version": { "source": "package.json", "tagTemplate": "v{version}" },
206
+ "distributions": [{
207
+ "type": "npm",
208
+ "package": "my-project",
209
+ "access": "public",
210
+ "provenance": false,
211
+ "tag": "latest",
212
+ "registry": "https://registry.npmjs.org",
213
+ "publisher": "my-npm-username"
214
+ }],
215
+ "publicFiles": [
216
+ { "from": "README.md", "to": "README.md", "mode": "preserve" },
217
+ { "from": "package.json", "to": "package.json", "mode": "preserve" }
218
+ ],
219
+ "requiredPublicFiles": ["README.md", "package.json"],
220
+ "previousPublicBaseline": { "mode": "none" },
221
+ "production": {
222
+ "branchTemplate": "release/{tag}",
223
+ "branchStrategy": "create-release-branch"
224
+ }
225
+ }]
226
+ },
227
+ "selectedGateIds": []
228
+ }
229
+ ```
230
+
231
+ This is a schema reference, not an onboarding template. Normal setup must use
232
+ the machine proposal. `mode: none` is valid only when no public version exists.
233
+
234
+ The following reference shows the exact relationship between a manually
235
+ reviewed gate and `selectedGateIds`. Apply that relationship only as an
236
+ incremental edit to the extracted machine proposal:
237
+
238
+ ```json
239
+ {
240
+ "projectConfig": {
241
+ "apiVersion": "release-skill/v1",
242
+ "kind": "ReleaseProject",
243
+ "project": { "name": "my-project", "defaultBranch": "main" },
244
+ "releaseUnits": [{
245
+ "id": "my-project",
246
+ "source": ".",
247
+ "publicRepo": "owner/my-project",
248
+ "version": { "source": "package.json", "tagTemplate": "v{version}" },
249
+ "distributions": [{
250
+ "type": "npm",
251
+ "package": "my-project",
252
+ "access": "public",
253
+ "provenance": false,
254
+ "tag": "latest",
255
+ "registry": "https://registry.npmjs.org",
256
+ "publisher": "my-npm-username"
257
+ }],
258
+ "publicFiles": [
259
+ { "from": "package.json", "to": "package.json", "mode": "preserve" }
260
+ ],
261
+ "requiredPublicFiles": ["package.json"],
262
+ "previousPublicBaseline": { "mode": "none" },
263
+ "production": {
264
+ "branchTemplate": "release/{tag}",
265
+ "branchStrategy": "create-release-branch"
266
+ }
267
+ }],
268
+ "verificationGates": [{
269
+ "id": "my-project-script-test",
270
+ "phase": "snapshot-verify",
271
+ "scope": { "unit": "my-project" },
272
+ "command": ["node", "-e", "const p=require('./package.json');if(!p.name)process.exit(1)"],
273
+ "cwd": ".",
274
+ "timeoutMs": 30000,
275
+ "envAllowlist": []
276
+ }]
277
+ },
278
+ "selectedGateIds": ["my-project-script-test"]
279
+ }
280
+ ```
281
+
282
+ The id must be copied from the current `gateCandidates`; do not invent one.
283
+ The example command is self-contained in the public snapshot. A project script
284
+ is valid only when the script and every dependency it needs are included in
285
+ `publicFiles`; a snapshot gate cannot see the parent workspace's tests,
286
+ development dependencies, or `node_modules` unless they are explicitly public.
287
+
288
+ ```bash
289
+ release-skill setup --root /absolute/path/to/my-project \
290
+ --answers /absolute/path/to/setup-answers.json --json
291
+ release-skill setup --root /absolute/path/to/my-project \
292
+ --answers /absolute/path/to/setup-answers.json \
293
+ --write --confirm-setup <setupDigest> --json
294
+ ```
295
+
296
+ Setup atomically creates only an absent `.release-skill/project.yaml`.
297
+ That create-once step uses the digest-registered `darwin-arm64` native
298
+ prebuild shipped in v0.1.3; unsupported platforms fail closed with
299
+ `SAFE_WRITE_UNAVAILABLE` instead of falling back to path-based writes.
300
+ `ALREADY_CONFIGURED`/`CONFIG_EXISTS` means the existing file remains
301
+ human-owned and must be edited incrementally. README, slogans, CHANGELOG, and
302
+ business scripts are never generated or overwritten. A project with no remote
303
+ channel reports `LOCAL_ONLY_DETECTED` instead of inventing production support.
304
+
305
+ The following is a minimal human-authored configuration. npm visibility,
306
+ public-file boundaries, and remote targets must be explicit:
118
307
 
119
308
  ```yaml
120
309
  apiVersion: release-skill/v1
@@ -162,6 +351,7 @@ releaseUnits:
162
351
  # status: READY
163
352
  production:
164
353
  branchTemplate: release/{tag}
354
+ branchStrategy: create-release-branch
165
355
  releaseTitleTemplate: "{unit} {version}"
166
356
  releaseNotes: "Human-maintained release notes"
167
357
  ```
@@ -192,6 +382,58 @@ After `merge` or `adopt`, rebind `previousPublicBaseline` to the accepted
192
382
  immutable `repo`/`ref`/`commit`, then run a new `prepare --online --production`,
193
383
  review, and approval.
194
384
 
385
+ Choose a branch strategy that matches the real repository:
386
+
387
+ - `create-release-branch` creates an absent immutable release branch and stops
388
+ if the name already exists.
389
+ - `advance-existing-branch` creates a single-parent commit on the exact
390
+ `previousPublicBaseline` commit and permits only an ordinary fast-forward
391
+ push; concurrent drift requires human intervention.
392
+ - `initialize-default-branch` creates an absent standard branch under control.
393
+ Only explicit `setAsDefaultBranch` and `expectedCurrentDefaultBranch` values
394
+ add a separately approved, observed, and reconcilable default-branch action.
395
+
396
+ Minimal configurations for the three strategies are:
397
+
398
+ ```yaml
399
+ # New immutable release branch; the target must not exist.
400
+ previousPublicBaseline: { mode: none } # only for a true first public release
401
+ production:
402
+ branchTemplate: release/{tag}
403
+ branchStrategy: create-release-branch
404
+ ```
405
+
406
+ ```yaml
407
+ # Advance main; the bound ref must be exactly the target branch.
408
+ previousPublicBaseline:
409
+ mode: bound
410
+ repo: owner/my-project
411
+ ref: refs/heads/main
412
+ commit: 0123456789abcdef0123456789abcdef01234567
413
+ production:
414
+ branchTemplate: main
415
+ branchStrategy: advance-existing-branch
416
+ ```
417
+
418
+ ```yaml
419
+ # One-time creation of an absent main and an explicit default-branch switch.
420
+ previousPublicBaseline:
421
+ mode: bound
422
+ repo: owner/my-project
423
+ ref: refs/heads/old-public-branch
424
+ commit: 0123456789abcdef0123456789abcdef01234567
425
+ production:
426
+ branchTemplate: main
427
+ branchStrategy: initialize-default-branch
428
+ setAsDefaultBranch: true
429
+ expectedCurrentDefaultBranch: old-public-branch
430
+ ```
431
+
432
+ The latter two require `prepare --online --production`. If the observed branch,
433
+ commit, target absence, or current default branch differs, stop and update the
434
+ human-owned source/config only after reviewing the real remote state; never
435
+ force-push or weaken the baseline.
436
+
195
437
  This is a mechanics-only local example, not a complete npm publication map.
196
438
  Before a real release, enumerate every public runtime file, executable, type
197
439
  declaration, image, and linked document. In a monorepo, set `source` to a path
@@ -206,48 +448,71 @@ causes baseline validation to stop.
206
448
 
207
449
  ### Main workflow
208
450
 
209
- Run these steps in order. Steps 1–3 are safe default (read-only or local-only);
210
- steps 48 are production publishing with explicit human gates.
451
+ Run these steps in order. Steps 1–4 are safe default (read-only or local-only);
452
+ steps 59 are production publishing with explicit human gates.
211
453
 
212
454
  ```bash
213
- # Current v0.1.1 release candidate:
214
- CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
455
+ # npm-installed CLI (recommended):
456
+ CLI=(release-skill)
215
457
  PROJECT=/absolute/path/to/my-project
216
- # After npm view reports 0.1.1 and the package is installed:
217
- # CLI=(release-skill)
458
+ ACTOR=your-name
459
+ # Development fallback (source checkout):
460
+ # CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
218
461
  ```
219
462
 
220
- The source-checkout entry remains the candidate default until npm publication is
221
- verified; after that, the installed npm entry is the supported user default.
463
+ The npm-installed CLI is the supported user entry after v0.1.1 production
464
+ publication. The source checkout remains the development/contributor fallback.
222
465
 
223
466
  1. **Environment check:**
224
467
  ```bash
225
468
  "${CLI[@]}" help
226
469
  ```
227
- 2. **Readiness assessment (read-only):**
470
+ 2. **First-use setup (only when config is absent; read-only):**
471
+ ```bash
472
+ "${CLI[@]}" setup --root "$PROJECT" --json
473
+ ```
474
+ Follow the mechanical `compactSummary` and `recommendedAnswers` path above;
475
+ confirm the bound `setupDigest` once, and skip this step when configuration exists.
476
+ 3. **Readiness assessment (read-only):**
228
477
  ```bash
229
478
  "${CLI[@]}" assess --root "$PROJECT" --offline --json
230
479
  ```
231
- 3. **Local snapshot and plan freeze:**
480
+ 4. **Local snapshot and plan freeze:**
232
481
  ```bash
233
- "${CLI[@]}" prepare --root "$PROJECT" --offline --json
482
+ "${CLI[@]}" prepare --root "$PROJECT" --offline \
483
+ --acknowledge-hook-side-effects \
484
+ --acknowledge-gate-side-effects --json
234
485
  ```
235
- 4. **Human review:** inspect the returned `planPath`, `externalActions`,
486
+ Omit an acknowledgement only when that project config has no corresponding
487
+ hook or snapshot gate. Never grant either acknowledgement before reviewing
488
+ the configured executable, arguments, working directory, and side effects.
489
+ 5. **Human review:** inspect the returned `planPath`, `externalActions`,
236
490
  `units[].targetVersion`, and `planDigest`. Each unit's snapshot is under
237
- `<evidenceDir>/snapshots/<unit-id>/`. The command writes only local release
238
- data under `.release-skill/`.
239
- 5. **Production plan freeze:**
491
+ `<evidenceDir>/snapshots/<unit-id>/`. The release-skill pipeline writes its
492
+ own data under `.release-skill/`; acknowledged project hooks and gates are
493
+ unsandboxed processes and may write elsewhere or access the network.
494
+ 6. **Production plan freeze:**
240
495
  ```bash
241
- "${CLI[@]}" prepare --root "$PROJECT" --online --production --json
496
+ PRODUCTION_JSON=$("${CLI[@]}" prepare --root "$PROJECT" --online --production \
497
+ --acknowledge-hook-side-effects \
498
+ --acknowledge-gate-side-effects --json)
499
+ printf '%s\n' "$PRODUCTION_JSON" | jq .
500
+ PLAN_PATH=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planPath')
501
+ PLAN_DIGEST=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planDigest')
242
502
  ```
503
+ As above, omit only acknowledgements that are not required by the project
504
+ config, and review every configured process before granting them.
243
505
  Review the new plan's externalActions, npm policy, branch/tag, and frozen
244
506
  digests. `prepare --json` returns the immutable production authority as
245
507
  `<project>/.release-skill/plans/<planDigest>.json`; always carry that returned
246
508
  `planPath` forward. `.release-skill/release-plan.json` is only a mutable
247
509
  convenience alias and must not be passed to production approve/publish/reconcile.
248
- 6. **Approval:**
510
+ 7. **Approval:**
249
511
  ```bash
250
- "${CLI[@]}" approve --plan <planPath> --digest <planDigest> --actor <name> --json
512
+ APPROVAL_JSON=$("${CLI[@]}" approve --plan "$PLAN_PATH" \
513
+ --digest "$PLAN_DIGEST" --actor "$ACTOR" --json)
514
+ printf '%s\n' "$APPROVAL_JSON" | jq .
515
+ APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
251
516
  ```
252
517
  Returns the immutable production authority as `approvalPath` at
253
518
  `<project>/.release-skill/approvals/<planDigest>/<approvalDigest>.json`.
@@ -257,18 +522,28 @@ verified; after that, the installed npm entry is the supported user default.
257
522
  hours; a PARTIAL recovery may create a new approval for the same plan while
258
523
  preserving every earlier approval byte-for-byte. Use the returned
259
524
  `approvalPath` and `expiresAt` as authority.
260
- 7. **Publish (remote writes start here):**
525
+ 8. **Publish (remote writes start here):**
261
526
  ```bash
262
- "${CLI[@]}" publish --root "$PROJECT" \
263
- --plan <planPath> --approval <approvalPath> \
264
- --confirm-production <planDigest> --json
527
+ PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
528
+ --plan "$PLAN_PATH" --approval "$APPROVAL_PATH" \
529
+ --confirm-production "$PLAN_DIGEST" --json)
530
+ printf '%s\n' "$PUBLISH_JSON" | jq .
531
+ PUBLISH_RUN_PATH=$(printf '%s\n' "$PUBLISH_JSON" | jq -r '.runPath')
265
532
  ```
266
533
  Save the returned `runPath`. `PUBLISHED` is **not** the terminal state.
267
- 8. **Verify (consumer install check):**
534
+ 9. **Verify (consumer install check):**
268
535
  ```bash
269
536
  "${CLI[@]}" verify --root "$PROJECT" \
270
- --plan <planPath> --run <publishRunPath> --json
537
+ --plan "$PLAN_PATH" --run "$PUBLISH_RUN_PATH" \
538
+ --acknowledge-gate-side-effects --json
271
539
  ```
540
+ Omit the acknowledgement only when the plan has neither consumer gates nor
541
+ a configured npm `smokeBin`. Both execute installed project code without an
542
+ OS or network sandbox.
543
+
544
+ The handoff example requires `jq`. Without it, copy the same four returned JSON
545
+ fields exactly; do not pass the angle-bracket labels shown elsewhere as shell
546
+ syntax.
272
547
 
273
548
  Production prepare seals a standalone Git commit/tree for every public snapshot
274
549
  and creates a fixed tarball for every npm unit. Publish globally preflights all
@@ -411,17 +686,24 @@ Instead, use `reconcile` to inspect actual remote state, skip already-consistent
411
686
  steps, and safely retry incomplete actions:
412
687
 
413
688
  ```bash
414
- "${CLI[@]}" reconcile --root "$PROJECT" \
415
- --run <publishRunPath> \
416
- --plan <planPath> \
417
- --approval <approvalPath> \
418
- --confirm-production <planDigest> \
419
- --json
689
+ RECONCILE_JSON=$("${CLI[@]}" reconcile --root "$PROJECT" \
690
+ --run "$PUBLISH_RUN_PATH" \
691
+ --plan "$PLAN_PATH" \
692
+ --approval "$APPROVAL_PATH" \
693
+ --confirm-production "$PLAN_DIGEST" --json)
694
+ printf '%s\n' "$RECONCILE_JSON" | jq .
695
+ RECONCILE_RUN_PATH=$(printf '%s\n' "$RECONCILE_JSON" | jq -r '.runPath')
420
696
  # Save reconcile's new runPath, then perform the fresh install verification.
421
697
  "${CLI[@]}" verify --root "$PROJECT" \
422
- --plan <planPath> --run <reconcileRunPath> --json
698
+ --plan "$PLAN_PATH" --run "$RECONCILE_RUN_PATH" \
699
+ --acknowledge-gate-side-effects --json
423
700
  ```
424
701
 
702
+ Omit the verify acknowledgement only when the frozen plan has neither consumer
703
+ gates nor an npm `smokeBin`. The variables above are the exact values captured
704
+ by the main flow; if approval expired during recovery, create a fresh approval
705
+ for the same immutable plan and replace `APPROVAL_PATH` before reconcile.
706
+
425
707
  `reconcile` queries the actual remote state (Git refs, npm version, GitHub
426
708
  Release, marketplace install), skips any step whose evidence already matches
427
709
  the frozen plan, and retries only safe and incomplete steps. Remote conflicts
@@ -435,6 +717,10 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
435
717
  - validates project configuration and release units;
436
718
  - reports readiness without changing the project during `assess`;
437
719
  - copies configured public files into an isolated snapshot;
720
+ - discovers first-use candidates read-only and creates a config only once after
721
+ exact `setupDigest` confirmation;
722
+ - runs human-selected project gates in frozen-snapshot copies and exact
723
+ consumer installation roots;
438
724
  - checks required files, path safety, exact bytes/modes, and obvious leaks;
439
725
  - records Git/workspace identity and freezes a digest-bound release plan;
440
726
  - binds approval to the plan digest, expiry, and explicit action allowlist;
@@ -447,6 +733,52 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
447
733
  - stops subsequent checkpoints on failure and writes a separate run record
448
734
  without mutating the frozen plan or undoing successful remote actions.
449
735
 
736
+ ## Project-specific verification: hooks and gates
737
+
738
+ `hooks.docs/build/test/typecheck/lint` run before the snapshot is frozen. Use
739
+ them only for work that genuinely needs the parent workspace or generates
740
+ source files. They can modify files or access the network, so prepare requires
741
+ `--acknowledge-hook-side-effects`.
742
+
743
+ `verificationGates` are the controlled extension point for release calibration:
744
+
745
+ ```yaml
746
+ verificationGates:
747
+ - id: package-contract
748
+ phase: snapshot-verify
749
+ scope: { unit: my-project }
750
+ command:
751
+ - node
752
+ - -e
753
+ - "const p=require('./package.json'); if (!p.name) process.exit(1)"
754
+ cwd: .
755
+ timeoutMs: 120000
756
+ envAllowlist: [CI]
757
+ - id: installed-help
758
+ phase: consumer-verify
759
+ scope: { unit: my-project, distribution: npm }
760
+ command: [node, scripts/check-installed-help.mjs]
761
+ cwd: .
762
+ timeoutMs: 30000
763
+ envAllowlist: []
764
+ expectedJson: { status: READY }
765
+ ```
766
+
767
+ The snapshot example is deliberately self-contained and reads only a mapped
768
+ public file. Any replacement script and every dependency it needs must exist
769
+ in the frozen public snapshot. The consumer script must likewise be present in
770
+ the exact installed distribution; gates cannot borrow tests, development
771
+ dependencies, or `node_modules` from the parent workspace.
772
+
773
+ `snapshot-verify` runs in a disposable writable copy of the frozen public
774
+ snapshot. `consumer-verify` runs from an exact isolated npm/Claude/Codex install
775
+ root. Both use executable arrays instead of shell strings; definitions and
776
+ results enter digest-bound evidence, and prepare/verify require
777
+ `--acknowledge-gate-side-effects`. Gates are still project processes without a
778
+ network sandbox, so release-skill cannot promise that they will not write files
779
+ or access the network. Push, tag, default-branch changes, GitHub Releases, and
780
+ npm publish may never be hooks/gates; they remain controlled plan actions.
781
+
450
782
  ## What it does not do yet
451
783
 
452
784
  <!-- release-skill:capability:unsupported-scope -->
@@ -455,17 +787,22 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
455
787
  - no claim that a real production canary has run for marketplace verification;
456
788
  - `prepare --online` observes previous public baselines (bound mode) and defers
457
789
  remote uniqueness checks to publish global preflight;
458
- - no force push, overwrite of branches/tags/releases, or npm unpublish;
790
+ - no overwrite of branches/tags/releases or npm unpublish; create-only refs use
791
+ `--force-with-lease=<ref>:` solely as an atomic compare-and-set assertion that
792
+ the ref is absent, while existing branches use an ordinary non-force push;
459
793
  - no promise of Windows or broad multi-platform native write support;
460
794
  - no hidden commit, push, tag, release, or package publication.
461
795
 
462
796
  ### Write Safety
463
797
 
464
- `assess` is read-only unless an explicit report output is requested. `prepare`
798
+ `setup` is read-only by default and may create a config only once after exact
799
+ digest confirmation. `assess` is read-only unless an explicit report output is requested. `prepare`
465
800
  writes local files under `.release-skill/`; it does not write project source
466
801
  files or remote services. If hooks are configured, they are arbitrary local
467
802
  processes and require `--acknowledge-hook-side-effects`; hooks may have their
468
- own filesystem or network side effects. `publish` is the production write entry
803
+ own filesystem or network side effects. Gates are also project processes and
804
+ require `--acknowledge-gate-side-effects`; they may have the same side effects.
805
+ `publish` is the production write entry
469
806
  and requires both approval and the current plan digest. Omit hooks and use local
470
807
  sandbox targets for the smallest safe rehearsal.
471
808
 
@@ -473,12 +810,18 @@ sandbox targets for the smallest safe rehearsal.
473
810
 
474
811
  | Result | What to do |
475
812
  |---|---|
813
+ | `NEEDS_INPUT` | Complete setup's repository, tag, channel, baseline, and gate decisions. |
814
+ | `LOCAL_ONLY_DETECTED` | Establish a remote channel or keep only a local configuration design; do not claim production readiness. |
815
+ | `SETUP_DIGEST_MISMATCH` | Facts or answers changed; rerun dry-run, review, and confirm the new digest. |
816
+ | `CONFIG_EXISTS` | Setup never overwrites the existing config; assess it and edit incrementally. |
817
+ | `SAFE_WRITE_UNAVAILABLE` | Automatic create-once setup is unsupported on this platform; keep the dry-run report and create the reviewed config manually without overwriting an existing file. |
476
818
  | `CONFIG_INVALID` | Correct `.release-skill/project.yaml`, then rerun `assess`. |
477
819
  | `PUBLIC_FILE_MISSING` | Add or correct the configured public file. |
478
820
  | `FORBIDDEN_CONTENT_DETECTED` | Remove the leaked/private content, then prepare again. |
479
821
  | `SNAPSHOT_FIDELITY_FAILED` | Inspect the source/snapshot path and rerun `prepare`. |
480
822
  | `BASELINE_CHANGED` | Keep the human edit, then prepare, review, and approve again. |
481
- | `GATE_FAILED` | Inspect frozen artifacts, auth, remote uniqueness, and digest confirmation. |
823
+ | `GATE_FAILED` during `prepare` | Fix the snapshot gate or frozen public artifact, then run a new `prepare`; the failed plan cannot be approved. |
824
+ | `GATE_FAILED` during `verify` | If the consumer environment failed, repair it and rerun `verify` from the same `PUBLISHED` run. If the published artifact is defective, release a new patch version; never overwrite it. |
482
825
  | `PARTIAL` | Do not restart or delete remote state; review the returned `runPath` and run `reconcile` (see above). |
483
826
  | `PUBLISHED` | Run `verify --plan <planPath> --run <publishRunPath>`; this is not terminal success. |
484
827
  | `VERIFIED` | Remote state, exact npm install, and configured plugin consumer installs all matched the frozen plan. |
@@ -486,15 +829,16 @@ sandbox targets for the smallest safe rehearsal.
486
829
  ## Skills
487
830
 
488
831
  - `release-help`: environment check and next-step guidance.
832
+ - `release-setup`: read-only discovery, human calibration, and create-once first-use configuration.
489
833
  - `release-assess`: read-only release readiness report.
490
834
  - `release-prepare`: local snapshot and reviewable release plan.
491
835
  - `release-publish`: approved, digest-confirmed frozen GitHub+npm publishing.
492
836
  - `release-reconcile`: evidence-based PARTIAL recovery with human intervention on conflicts.
493
837
  - `release-verify`: post-publish verification; only `VERIFIED` is the happy end.
494
838
 
495
- Conflicts still default to human intervention. Before v0.1.1 is published, use
496
- the source CLI shown above; after registry verification, the supported user
497
- entry is the npm-installed `release-skill` CLI.
839
+ Conflicts still default to human intervention. The npm-installed `release-skill`
840
+ CLI is the supported user entry after v0.1.1 production publication; source
841
+ checkout remains the development/contributor fallback.
498
842
 
499
843
  ## License
500
844