release-skill 0.1.1 → 0.1.3
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/CHANGELOG.md +60 -0
- package/INSTALL.md +179 -5
- package/INSTALL.zh-CN.md +320 -0
- package/README.md +347 -67
- package/README.zh-CN.md +318 -59
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/skills/release-help/SKILL.md +7 -4
- package/adapters/claude/skills/release-prepare/SKILL.md +11 -1
- package/adapters/claude/skills/release-publish/SKILL.md +6 -3
- package/adapters/claude/skills/release-reconcile/SKILL.md +1 -1
- package/adapters/claude/skills/release-setup/SKILL.md +111 -0
- package/adapters/claude/skills/release-verify/SKILL.md +5 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/skills/release-help/SKILL.md +7 -4
- package/adapters/codex/skills/release-prepare/SKILL.md +11 -1
- package/adapters/codex/skills/release-publish/SKILL.md +6 -3
- package/adapters/codex/skills/release-reconcile/SKILL.md +1 -1
- package/adapters/codex/skills/release-setup/SKILL.md +111 -0
- package/adapters/codex/skills/release-verify/SKILL.md +5 -2
- package/bin/release-skill.mjs +65 -9
- package/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/native/safe-write/prebuilds.json +22 -2
- package/native/safe-write/src/safe_write.cc +11 -2
- package/package.json +3 -1
- package/references/02-project-config.md +54 -3
- package/references/05-evidence-and-errors.md +6 -2
- package/schemas/release-plan.schema.json +550 -65
- package/schemas/release-project.schema.json +398 -29
- package/schemas/release-run.schema.json +165 -18
- package/skills/release-help/SKILL.md +7 -4
- package/skills/release-prepare/SKILL.md +11 -1
- package/skills/release-publish/SKILL.md +6 -3
- package/skills/release-reconcile/SKILL.md +1 -1
- package/skills/release-setup/SKILL.md +111 -0
- package/skills/release-verify/SKILL.md +5 -2
- package/skills-src/release-help/SKILL.md +7 -4
- package/skills-src/release-prepare/SKILL.md +11 -1
- package/skills-src/release-publish/SKILL.md +6 -3
- package/skills-src/release-reconcile/SKILL.md +1 -1
- package/skills-src/release-setup/SKILL.md +111 -0
- package/skills-src/release-verify/SKILL.md +5 -2
- package/src/adapters/contract.mjs +3 -0
- package/src/adapters/git-github.mjs +84 -2
- package/src/adapters/plugin-marketplace.mjs +65 -21
- package/src/adapters/push-snapshot.mjs +84 -17
- package/src/commands/prepare.mjs +223 -20
- package/src/commands/publish.mjs +45 -0
- package/src/commands/reconcile.mjs +152 -0
- package/src/commands/setup.mjs +886 -0
- package/src/commands/verify.mjs +122 -26
- package/src/core/config.mjs +34 -0
- package/src/core/errors.mjs +4 -0
- package/src/core/plan.mjs +123 -0
- package/src/core/previous-public-baseline.mjs +21 -1
- package/src/core/verification-gates.mjs +451 -0
- package/src/snapshot/frozen.mjs +89 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
5
|
Release preparation for Claude Code and Codex, with human-edited files kept intact.
|
|
6
6
|
|
|
@@ -10,28 +10,25 @@ reviewed artifacts first and publishes those same artifacts later; it does not
|
|
|
10
10
|
regenerate a README or re-pack the live workspace at the last step.
|
|
11
11
|
|
|
12
12
|
<!-- release-skill:capability:external-write-boundary -->
|
|
13
|
-
> **Current boundary:**
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
> remotes, and protocol fakes for `gh`, `npm`,
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
> public baselines (bound mode) and fails closed on drift; remote uniqueness checks
|
|
25
|
-
> are deferred to publish global preflight.
|
|
13
|
+
> **Current boundary:** v0.1.1 completed a real production release to GitHub and
|
|
14
|
+
> npm, followed by exact npm installation and Claude/Codex consumer installation
|
|
15
|
+
> verification from the frozen Git ref. The same workflow also has a local
|
|
16
|
+
> production-equivalent protocol suite using the real release-skill CLI and
|
|
17
|
+
> frozen artifacts, local bare Git remotes, and protocol fakes for `gh`, `npm`,
|
|
18
|
+
> Claude, and Codex. The suite does not provide OS-level network isolation, and
|
|
19
|
+
> it does not prove that another project's credentials, permissions, rate limits,
|
|
20
|
+
> or eventual-consistency behavior will match this release. Treat each project's
|
|
21
|
+
> first production run as a monitored canary. `prepare --online` observes bound
|
|
22
|
+
> previous-public baselines and fails closed on drift; remote uniqueness checks
|
|
23
|
+
> run during publish global preflight.
|
|
26
24
|
|
|
27
25
|
<!-- release-skill:capability:safe-first-command -->
|
|
28
|
-
> **v0.1.1
|
|
29
|
-
>
|
|
30
|
-
> already exists.
|
|
26
|
+
> **v0.1.1 production verified.** The npm-installed CLI is the supported user
|
|
27
|
+
> entry. Source checkout is the development/contributor fallback.
|
|
31
28
|
>
|
|
32
29
|
> **Start here:**
|
|
33
|
-
> -
|
|
34
|
-
> -
|
|
30
|
+
> - npm install: `npm install -g release-skill` → `release-skill help`
|
|
31
|
+
> - source checkout: `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs" help`
|
|
35
32
|
|
|
36
33
|
<!-- release-skill:maturity:v0.1-boundary -->
|
|
37
34
|
<!-- release-skill:maturity:boundary -->
|
|
@@ -70,12 +67,7 @@ truth, and never rewrite human truth.**
|
|
|
70
67
|
- Git 2.30+
|
|
71
68
|
- A target Git repository with at least one commit
|
|
72
69
|
|
|
73
|
-
|
|
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):**
|
|
70
|
+
**Install from npm (recommended):**
|
|
79
71
|
|
|
80
72
|
```bash
|
|
81
73
|
npm install -g release-skill
|
|
@@ -93,7 +85,7 @@ npx release-skill help
|
|
|
93
85
|
release-skill help
|
|
94
86
|
```
|
|
95
87
|
|
|
96
|
-
**Development install (from source checkout):**
|
|
88
|
+
**Development install (contributor fallback, from source checkout):**
|
|
97
89
|
|
|
98
90
|
Set the checkout location and install dependencies:
|
|
99
91
|
|
|
@@ -105,8 +97,6 @@ npm exec --yes pnpm@10.17.1 -- install --frozen-lockfile
|
|
|
105
97
|
|
|
106
98
|
Then use the CLI via `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs"`.
|
|
107
99
|
|
|
108
|
-
Create `.release-skill/project.yaml` in the target project:
|
|
109
|
-
|
|
110
100
|
First keep local plans, approvals, and frozen artifacts out of Git:
|
|
111
101
|
|
|
112
102
|
```gitignore
|
|
@@ -114,7 +104,142 @@ First keep local plans, approvals, and frozen artifacts out of Git:
|
|
|
114
104
|
!.release-skill/project.yaml
|
|
115
105
|
```
|
|
116
106
|
|
|
117
|
-
|
|
107
|
+
### First use: discover, then let a human finalize
|
|
108
|
+
|
|
109
|
+
When the project has no configuration, start with read-only setup. It scans
|
|
110
|
+
packages, plugin manifests, Git remotes, legacy `public-release.json`, and
|
|
111
|
+
quality scripts. It reports release-unit, tag, branch, previous-baseline, and
|
|
112
|
+
verification-gate candidates, but never treats README prose as instructions or
|
|
113
|
+
automatically selects or executes a script:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
release-skill setup --root /absolute/path/to/my-project --json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally exit with code 2. They
|
|
120
|
+
are unresolved decision states, not a crash. Automation should inspect the JSON
|
|
121
|
+
`status` instead of treating every nonzero setup result as an internal failure.
|
|
122
|
+
|
|
123
|
+
After review, create an answers JSON containing the complete `projectConfig`.
|
|
124
|
+
Its `selectedGateIds` must exactly match `projectConfig.verificationGates[].id`;
|
|
125
|
+
use an explicit empty array when no project-specific gate is selected. Dry-run
|
|
126
|
+
again with those answers, review the new `setupDigest`, then create the config:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"projectConfig": {
|
|
131
|
+
"apiVersion": "release-skill/v1",
|
|
132
|
+
"kind": "ReleaseProject",
|
|
133
|
+
"project": { "name": "my-project", "defaultBranch": "main" },
|
|
134
|
+
"releaseUnits": [{
|
|
135
|
+
"id": "my-project",
|
|
136
|
+
"source": ".",
|
|
137
|
+
"publicRepo": "owner/my-project",
|
|
138
|
+
"version": { "source": "package.json", "tagTemplate": "v{version}" },
|
|
139
|
+
"distributions": [{
|
|
140
|
+
"type": "npm",
|
|
141
|
+
"package": "my-project",
|
|
142
|
+
"access": "public",
|
|
143
|
+
"provenance": false,
|
|
144
|
+
"tag": "latest",
|
|
145
|
+
"registry": "https://registry.npmjs.org",
|
|
146
|
+
"publisher": "my-npm-username"
|
|
147
|
+
}],
|
|
148
|
+
"publicFiles": [
|
|
149
|
+
{ "from": "README.md", "to": "README.md", "mode": "preserve" },
|
|
150
|
+
{ "from": "package.json", "to": "package.json", "mode": "preserve" }
|
|
151
|
+
],
|
|
152
|
+
"requiredPublicFiles": ["README.md", "package.json"],
|
|
153
|
+
"previousPublicBaseline": { "mode": "none" },
|
|
154
|
+
"production": {
|
|
155
|
+
"branchTemplate": "release/{tag}",
|
|
156
|
+
"branchStrategy": "create-release-branch"
|
|
157
|
+
}
|
|
158
|
+
}]
|
|
159
|
+
},
|
|
160
|
+
"selectedGateIds": []
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This is a complete schema-shaped wrapper, not authoritative project data.
|
|
165
|
+
Replace every repository, channel, baseline, and public-file decision with the
|
|
166
|
+
reviewed facts from your project; use `mode: none` only when no public version
|
|
167
|
+
exists.
|
|
168
|
+
|
|
169
|
+
To select one reported gate, keep the same complete `projectConfig`, add a
|
|
170
|
+
top-level `verificationGates` array inside it, and make the wrapper's
|
|
171
|
+
`selectedGateIds` identical. For example, when dry-run reported
|
|
172
|
+
`my-project-script-test`:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"projectConfig": {
|
|
177
|
+
"apiVersion": "release-skill/v1",
|
|
178
|
+
"kind": "ReleaseProject",
|
|
179
|
+
"project": { "name": "my-project", "defaultBranch": "main" },
|
|
180
|
+
"releaseUnits": [{
|
|
181
|
+
"id": "my-project",
|
|
182
|
+
"source": ".",
|
|
183
|
+
"publicRepo": "owner/my-project",
|
|
184
|
+
"version": { "source": "package.json", "tagTemplate": "v{version}" },
|
|
185
|
+
"distributions": [{
|
|
186
|
+
"type": "npm",
|
|
187
|
+
"package": "my-project",
|
|
188
|
+
"access": "public",
|
|
189
|
+
"provenance": false,
|
|
190
|
+
"tag": "latest",
|
|
191
|
+
"registry": "https://registry.npmjs.org",
|
|
192
|
+
"publisher": "my-npm-username"
|
|
193
|
+
}],
|
|
194
|
+
"publicFiles": [
|
|
195
|
+
{ "from": "package.json", "to": "package.json", "mode": "preserve" }
|
|
196
|
+
],
|
|
197
|
+
"requiredPublicFiles": ["package.json"],
|
|
198
|
+
"previousPublicBaseline": { "mode": "none" },
|
|
199
|
+
"production": {
|
|
200
|
+
"branchTemplate": "release/{tag}",
|
|
201
|
+
"branchStrategy": "create-release-branch"
|
|
202
|
+
}
|
|
203
|
+
}],
|
|
204
|
+
"verificationGates": [{
|
|
205
|
+
"id": "my-project-script-test",
|
|
206
|
+
"phase": "snapshot-verify",
|
|
207
|
+
"scope": { "unit": "my-project" },
|
|
208
|
+
"command": ["node", "-e", "const p=require('./package.json');if(!p.name)process.exit(1)"],
|
|
209
|
+
"cwd": ".",
|
|
210
|
+
"timeoutMs": 30000,
|
|
211
|
+
"envAllowlist": []
|
|
212
|
+
}]
|
|
213
|
+
},
|
|
214
|
+
"selectedGateIds": ["my-project-script-test"]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The id must be copied from the current `gateCandidates`; do not invent one.
|
|
219
|
+
The example command is self-contained in the public snapshot. A project script
|
|
220
|
+
is valid only when the script and every dependency it needs are included in
|
|
221
|
+
`publicFiles`; a snapshot gate cannot see the parent workspace's tests,
|
|
222
|
+
development dependencies, or `node_modules` unless they are explicitly public.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
release-skill setup --root /absolute/path/to/my-project \
|
|
226
|
+
--answers /absolute/path/to/setup-answers.json --json
|
|
227
|
+
release-skill setup --root /absolute/path/to/my-project \
|
|
228
|
+
--answers /absolute/path/to/setup-answers.json \
|
|
229
|
+
--write --confirm-setup <setupDigest> --json
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Setup atomically creates only an absent `.release-skill/project.yaml`.
|
|
233
|
+
That create-once step uses the digest-registered `darwin-arm64` native
|
|
234
|
+
prebuild shipped in v0.1.3; unsupported platforms fail closed with
|
|
235
|
+
`SAFE_WRITE_UNAVAILABLE` instead of falling back to path-based writes.
|
|
236
|
+
`ALREADY_CONFIGURED`/`CONFIG_EXISTS` means the existing file remains
|
|
237
|
+
human-owned and must be edited incrementally. README, slogans, CHANGELOG, and
|
|
238
|
+
business scripts are never generated or overwritten. A project with no remote
|
|
239
|
+
channel reports `LOCAL_ONLY_DETECTED` instead of inventing production support.
|
|
240
|
+
|
|
241
|
+
The following is a minimal human-authored configuration. npm visibility,
|
|
242
|
+
public-file boundaries, and remote targets must be explicit:
|
|
118
243
|
|
|
119
244
|
```yaml
|
|
120
245
|
apiVersion: release-skill/v1
|
|
@@ -162,6 +287,7 @@ releaseUnits:
|
|
|
162
287
|
# status: READY
|
|
163
288
|
production:
|
|
164
289
|
branchTemplate: release/{tag}
|
|
290
|
+
branchStrategy: create-release-branch
|
|
165
291
|
releaseTitleTemplate: "{unit} {version}"
|
|
166
292
|
releaseNotes: "Human-maintained release notes"
|
|
167
293
|
```
|
|
@@ -192,6 +318,58 @@ After `merge` or `adopt`, rebind `previousPublicBaseline` to the accepted
|
|
|
192
318
|
immutable `repo`/`ref`/`commit`, then run a new `prepare --online --production`,
|
|
193
319
|
review, and approval.
|
|
194
320
|
|
|
321
|
+
Choose a branch strategy that matches the real repository:
|
|
322
|
+
|
|
323
|
+
- `create-release-branch` creates an absent immutable release branch and stops
|
|
324
|
+
if the name already exists.
|
|
325
|
+
- `advance-existing-branch` creates a single-parent commit on the exact
|
|
326
|
+
`previousPublicBaseline` commit and permits only an ordinary fast-forward
|
|
327
|
+
push; concurrent drift requires human intervention.
|
|
328
|
+
- `initialize-default-branch` creates an absent standard branch under control.
|
|
329
|
+
Only explicit `setAsDefaultBranch` and `expectedCurrentDefaultBranch` values
|
|
330
|
+
add a separately approved, observed, and reconcilable default-branch action.
|
|
331
|
+
|
|
332
|
+
Minimal configurations for the three strategies are:
|
|
333
|
+
|
|
334
|
+
```yaml
|
|
335
|
+
# New immutable release branch; the target must not exist.
|
|
336
|
+
previousPublicBaseline: { mode: none } # only for a true first public release
|
|
337
|
+
production:
|
|
338
|
+
branchTemplate: release/{tag}
|
|
339
|
+
branchStrategy: create-release-branch
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
```yaml
|
|
343
|
+
# Advance main; the bound ref must be exactly the target branch.
|
|
344
|
+
previousPublicBaseline:
|
|
345
|
+
mode: bound
|
|
346
|
+
repo: owner/my-project
|
|
347
|
+
ref: refs/heads/main
|
|
348
|
+
commit: 0123456789abcdef0123456789abcdef01234567
|
|
349
|
+
production:
|
|
350
|
+
branchTemplate: main
|
|
351
|
+
branchStrategy: advance-existing-branch
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```yaml
|
|
355
|
+
# One-time creation of an absent main and an explicit default-branch switch.
|
|
356
|
+
previousPublicBaseline:
|
|
357
|
+
mode: bound
|
|
358
|
+
repo: owner/my-project
|
|
359
|
+
ref: refs/heads/old-public-branch
|
|
360
|
+
commit: 0123456789abcdef0123456789abcdef01234567
|
|
361
|
+
production:
|
|
362
|
+
branchTemplate: main
|
|
363
|
+
branchStrategy: initialize-default-branch
|
|
364
|
+
setAsDefaultBranch: true
|
|
365
|
+
expectedCurrentDefaultBranch: old-public-branch
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The latter two require `prepare --online --production`. If the observed branch,
|
|
369
|
+
commit, target absence, or current default branch differs, stop and update the
|
|
370
|
+
human-owned source/config only after reviewing the real remote state; never
|
|
371
|
+
force-push or weaken the baseline.
|
|
372
|
+
|
|
195
373
|
This is a mechanics-only local example, not a complete npm publication map.
|
|
196
374
|
Before a real release, enumerate every public runtime file, executable, type
|
|
197
375
|
declaration, image, and linked document. In a monorepo, set `source` to a path
|
|
@@ -206,48 +384,71 @@ causes baseline validation to stop.
|
|
|
206
384
|
|
|
207
385
|
### Main workflow
|
|
208
386
|
|
|
209
|
-
Run these steps in order. Steps 1–
|
|
210
|
-
steps
|
|
387
|
+
Run these steps in order. Steps 1–4 are safe default (read-only or local-only);
|
|
388
|
+
steps 5–9 are production publishing with explicit human gates.
|
|
211
389
|
|
|
212
390
|
```bash
|
|
213
|
-
#
|
|
214
|
-
CLI=(
|
|
391
|
+
# npm-installed CLI (recommended):
|
|
392
|
+
CLI=(release-skill)
|
|
215
393
|
PROJECT=/absolute/path/to/my-project
|
|
216
|
-
|
|
217
|
-
#
|
|
394
|
+
ACTOR=your-name
|
|
395
|
+
# Development fallback (source checkout):
|
|
396
|
+
# CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
|
|
218
397
|
```
|
|
219
398
|
|
|
220
|
-
The
|
|
221
|
-
|
|
399
|
+
The npm-installed CLI is the supported user entry after v0.1.1 production
|
|
400
|
+
publication. The source checkout remains the development/contributor fallback.
|
|
222
401
|
|
|
223
402
|
1. **Environment check:**
|
|
224
403
|
```bash
|
|
225
404
|
"${CLI[@]}" help
|
|
226
405
|
```
|
|
227
|
-
2. **
|
|
406
|
+
2. **First-use setup (only when config is absent; read-only):**
|
|
407
|
+
```bash
|
|
408
|
+
"${CLI[@]}" setup --root "$PROJECT" --json
|
|
409
|
+
```
|
|
410
|
+
Complete the answers and exact `setupDigest` confirmation described above;
|
|
411
|
+
skip this step when configuration already exists.
|
|
412
|
+
3. **Readiness assessment (read-only):**
|
|
228
413
|
```bash
|
|
229
414
|
"${CLI[@]}" assess --root "$PROJECT" --offline --json
|
|
230
415
|
```
|
|
231
|
-
|
|
416
|
+
4. **Local snapshot and plan freeze:**
|
|
232
417
|
```bash
|
|
233
|
-
"${CLI[@]}" prepare --root "$PROJECT" --offline
|
|
418
|
+
"${CLI[@]}" prepare --root "$PROJECT" --offline \
|
|
419
|
+
--acknowledge-hook-side-effects \
|
|
420
|
+
--acknowledge-gate-side-effects --json
|
|
234
421
|
```
|
|
235
|
-
|
|
422
|
+
Omit an acknowledgement only when that project config has no corresponding
|
|
423
|
+
hook or snapshot gate. Never grant either acknowledgement before reviewing
|
|
424
|
+
the configured executable, arguments, working directory, and side effects.
|
|
425
|
+
5. **Human review:** inspect the returned `planPath`, `externalActions`,
|
|
236
426
|
`units[].targetVersion`, and `planDigest`. Each unit's snapshot is under
|
|
237
|
-
`<evidenceDir>/snapshots/<unit-id>/`. The
|
|
238
|
-
data under `.release-skill
|
|
239
|
-
|
|
427
|
+
`<evidenceDir>/snapshots/<unit-id>/`. The release-skill pipeline writes its
|
|
428
|
+
own data under `.release-skill/`; acknowledged project hooks and gates are
|
|
429
|
+
unsandboxed processes and may write elsewhere or access the network.
|
|
430
|
+
6. **Production plan freeze:**
|
|
240
431
|
```bash
|
|
241
|
-
"${CLI[@]}" prepare --root "$PROJECT" --online --production
|
|
432
|
+
PRODUCTION_JSON=$("${CLI[@]}" prepare --root "$PROJECT" --online --production \
|
|
433
|
+
--acknowledge-hook-side-effects \
|
|
434
|
+
--acknowledge-gate-side-effects --json)
|
|
435
|
+
printf '%s\n' "$PRODUCTION_JSON" | jq .
|
|
436
|
+
PLAN_PATH=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planPath')
|
|
437
|
+
PLAN_DIGEST=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planDigest')
|
|
242
438
|
```
|
|
439
|
+
As above, omit only acknowledgements that are not required by the project
|
|
440
|
+
config, and review every configured process before granting them.
|
|
243
441
|
Review the new plan's externalActions, npm policy, branch/tag, and frozen
|
|
244
442
|
digests. `prepare --json` returns the immutable production authority as
|
|
245
443
|
`<project>/.release-skill/plans/<planDigest>.json`; always carry that returned
|
|
246
444
|
`planPath` forward. `.release-skill/release-plan.json` is only a mutable
|
|
247
445
|
convenience alias and must not be passed to production approve/publish/reconcile.
|
|
248
|
-
|
|
446
|
+
7. **Approval:**
|
|
249
447
|
```bash
|
|
250
|
-
"${CLI[@]}" approve --plan
|
|
448
|
+
APPROVAL_JSON=$("${CLI[@]}" approve --plan "$PLAN_PATH" \
|
|
449
|
+
--digest "$PLAN_DIGEST" --actor "$ACTOR" --json)
|
|
450
|
+
printf '%s\n' "$APPROVAL_JSON" | jq .
|
|
451
|
+
APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
|
|
251
452
|
```
|
|
252
453
|
Returns the immutable production authority as `approvalPath` at
|
|
253
454
|
`<project>/.release-skill/approvals/<planDigest>/<approvalDigest>.json`.
|
|
@@ -257,18 +458,28 @@ verified; after that, the installed npm entry is the supported user default.
|
|
|
257
458
|
hours; a PARTIAL recovery may create a new approval for the same plan while
|
|
258
459
|
preserving every earlier approval byte-for-byte. Use the returned
|
|
259
460
|
`approvalPath` and `expiresAt` as authority.
|
|
260
|
-
|
|
461
|
+
8. **Publish (remote writes start here):**
|
|
261
462
|
```bash
|
|
262
|
-
"${CLI[@]}" publish --root "$PROJECT" \
|
|
263
|
-
--plan
|
|
264
|
-
--confirm-production
|
|
463
|
+
PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
|
|
464
|
+
--plan "$PLAN_PATH" --approval "$APPROVAL_PATH" \
|
|
465
|
+
--confirm-production "$PLAN_DIGEST" --json)
|
|
466
|
+
printf '%s\n' "$PUBLISH_JSON" | jq .
|
|
467
|
+
PUBLISH_RUN_PATH=$(printf '%s\n' "$PUBLISH_JSON" | jq -r '.runPath')
|
|
265
468
|
```
|
|
266
469
|
Save the returned `runPath`. `PUBLISHED` is **not** the terminal state.
|
|
267
|
-
|
|
470
|
+
9. **Verify (consumer install check):**
|
|
268
471
|
```bash
|
|
269
472
|
"${CLI[@]}" verify --root "$PROJECT" \
|
|
270
|
-
--plan
|
|
473
|
+
--plan "$PLAN_PATH" --run "$PUBLISH_RUN_PATH" \
|
|
474
|
+
--acknowledge-gate-side-effects --json
|
|
271
475
|
```
|
|
476
|
+
Omit the acknowledgement only when the plan has neither consumer gates nor
|
|
477
|
+
a configured npm `smokeBin`. Both execute installed project code without an
|
|
478
|
+
OS or network sandbox.
|
|
479
|
+
|
|
480
|
+
The handoff example requires `jq`. Without it, copy the same four returned JSON
|
|
481
|
+
fields exactly; do not pass the angle-bracket labels shown elsewhere as shell
|
|
482
|
+
syntax.
|
|
272
483
|
|
|
273
484
|
Production prepare seals a standalone Git commit/tree for every public snapshot
|
|
274
485
|
and creates a fixed tarball for every npm unit. Publish globally preflights all
|
|
@@ -411,17 +622,24 @@ Instead, use `reconcile` to inspect actual remote state, skip already-consistent
|
|
|
411
622
|
steps, and safely retry incomplete actions:
|
|
412
623
|
|
|
413
624
|
```bash
|
|
414
|
-
"${CLI[@]}" reconcile --root "$PROJECT" \
|
|
415
|
-
--run
|
|
416
|
-
--plan
|
|
417
|
-
--approval
|
|
418
|
-
--confirm-production
|
|
419
|
-
|
|
625
|
+
RECONCILE_JSON=$("${CLI[@]}" reconcile --root "$PROJECT" \
|
|
626
|
+
--run "$PUBLISH_RUN_PATH" \
|
|
627
|
+
--plan "$PLAN_PATH" \
|
|
628
|
+
--approval "$APPROVAL_PATH" \
|
|
629
|
+
--confirm-production "$PLAN_DIGEST" --json)
|
|
630
|
+
printf '%s\n' "$RECONCILE_JSON" | jq .
|
|
631
|
+
RECONCILE_RUN_PATH=$(printf '%s\n' "$RECONCILE_JSON" | jq -r '.runPath')
|
|
420
632
|
# Save reconcile's new runPath, then perform the fresh install verification.
|
|
421
633
|
"${CLI[@]}" verify --root "$PROJECT" \
|
|
422
|
-
--plan
|
|
634
|
+
--plan "$PLAN_PATH" --run "$RECONCILE_RUN_PATH" \
|
|
635
|
+
--acknowledge-gate-side-effects --json
|
|
423
636
|
```
|
|
424
637
|
|
|
638
|
+
Omit the verify acknowledgement only when the frozen plan has neither consumer
|
|
639
|
+
gates nor an npm `smokeBin`. The variables above are the exact values captured
|
|
640
|
+
by the main flow; if approval expired during recovery, create a fresh approval
|
|
641
|
+
for the same immutable plan and replace `APPROVAL_PATH` before reconcile.
|
|
642
|
+
|
|
425
643
|
`reconcile` queries the actual remote state (Git refs, npm version, GitHub
|
|
426
644
|
Release, marketplace install), skips any step whose evidence already matches
|
|
427
645
|
the frozen plan, and retries only safe and incomplete steps. Remote conflicts
|
|
@@ -435,6 +653,10 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
|
|
|
435
653
|
- validates project configuration and release units;
|
|
436
654
|
- reports readiness without changing the project during `assess`;
|
|
437
655
|
- copies configured public files into an isolated snapshot;
|
|
656
|
+
- discovers first-use candidates read-only and creates a config only once after
|
|
657
|
+
exact `setupDigest` confirmation;
|
|
658
|
+
- runs human-selected project gates in frozen-snapshot copies and exact
|
|
659
|
+
consumer installation roots;
|
|
438
660
|
- checks required files, path safety, exact bytes/modes, and obvious leaks;
|
|
439
661
|
- records Git/workspace identity and freezes a digest-bound release plan;
|
|
440
662
|
- binds approval to the plan digest, expiry, and explicit action allowlist;
|
|
@@ -447,6 +669,52 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
|
|
|
447
669
|
- stops subsequent checkpoints on failure and writes a separate run record
|
|
448
670
|
without mutating the frozen plan or undoing successful remote actions.
|
|
449
671
|
|
|
672
|
+
## Project-specific verification: hooks and gates
|
|
673
|
+
|
|
674
|
+
`hooks.docs/build/test/typecheck/lint` run before the snapshot is frozen. Use
|
|
675
|
+
them only for work that genuinely needs the parent workspace or generates
|
|
676
|
+
source files. They can modify files or access the network, so prepare requires
|
|
677
|
+
`--acknowledge-hook-side-effects`.
|
|
678
|
+
|
|
679
|
+
`verificationGates` are the controlled extension point for release calibration:
|
|
680
|
+
|
|
681
|
+
```yaml
|
|
682
|
+
verificationGates:
|
|
683
|
+
- id: package-contract
|
|
684
|
+
phase: snapshot-verify
|
|
685
|
+
scope: { unit: my-project }
|
|
686
|
+
command:
|
|
687
|
+
- node
|
|
688
|
+
- -e
|
|
689
|
+
- "const p=require('./package.json'); if (!p.name) process.exit(1)"
|
|
690
|
+
cwd: .
|
|
691
|
+
timeoutMs: 120000
|
|
692
|
+
envAllowlist: [CI]
|
|
693
|
+
- id: installed-help
|
|
694
|
+
phase: consumer-verify
|
|
695
|
+
scope: { unit: my-project, distribution: npm }
|
|
696
|
+
command: [node, scripts/check-installed-help.mjs]
|
|
697
|
+
cwd: .
|
|
698
|
+
timeoutMs: 30000
|
|
699
|
+
envAllowlist: []
|
|
700
|
+
expectedJson: { status: READY }
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
The snapshot example is deliberately self-contained and reads only a mapped
|
|
704
|
+
public file. Any replacement script and every dependency it needs must exist
|
|
705
|
+
in the frozen public snapshot. The consumer script must likewise be present in
|
|
706
|
+
the exact installed distribution; gates cannot borrow tests, development
|
|
707
|
+
dependencies, or `node_modules` from the parent workspace.
|
|
708
|
+
|
|
709
|
+
`snapshot-verify` runs in a disposable writable copy of the frozen public
|
|
710
|
+
snapshot. `consumer-verify` runs from an exact isolated npm/Claude/Codex install
|
|
711
|
+
root. Both use executable arrays instead of shell strings; definitions and
|
|
712
|
+
results enter digest-bound evidence, and prepare/verify require
|
|
713
|
+
`--acknowledge-gate-side-effects`. Gates are still project processes without a
|
|
714
|
+
network sandbox, so release-skill cannot promise that they will not write files
|
|
715
|
+
or access the network. Push, tag, default-branch changes, GitHub Releases, and
|
|
716
|
+
npm publish may never be hooks/gates; they remain controlled plan actions.
|
|
717
|
+
|
|
450
718
|
## What it does not do yet
|
|
451
719
|
|
|
452
720
|
<!-- release-skill:capability:unsupported-scope -->
|
|
@@ -455,17 +723,22 @@ Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
|
|
|
455
723
|
- no claim that a real production canary has run for marketplace verification;
|
|
456
724
|
- `prepare --online` observes previous public baselines (bound mode) and defers
|
|
457
725
|
remote uniqueness checks to publish global preflight;
|
|
458
|
-
- no
|
|
726
|
+
- no overwrite of branches/tags/releases or npm unpublish; create-only refs use
|
|
727
|
+
`--force-with-lease=<ref>:` solely as an atomic compare-and-set assertion that
|
|
728
|
+
the ref is absent, while existing branches use an ordinary non-force push;
|
|
459
729
|
- no promise of Windows or broad multi-platform native write support;
|
|
460
730
|
- no hidden commit, push, tag, release, or package publication.
|
|
461
731
|
|
|
462
732
|
### Write Safety
|
|
463
733
|
|
|
464
|
-
`
|
|
734
|
+
`setup` is read-only by default and may create a config only once after exact
|
|
735
|
+
digest confirmation. `assess` is read-only unless an explicit report output is requested. `prepare`
|
|
465
736
|
writes local files under `.release-skill/`; it does not write project source
|
|
466
737
|
files or remote services. If hooks are configured, they are arbitrary local
|
|
467
738
|
processes and require `--acknowledge-hook-side-effects`; hooks may have their
|
|
468
|
-
own filesystem or network side effects.
|
|
739
|
+
own filesystem or network side effects. Gates are also project processes and
|
|
740
|
+
require `--acknowledge-gate-side-effects`; they may have the same side effects.
|
|
741
|
+
`publish` is the production write entry
|
|
469
742
|
and requires both approval and the current plan digest. Omit hooks and use local
|
|
470
743
|
sandbox targets for the smallest safe rehearsal.
|
|
471
744
|
|
|
@@ -473,12 +746,18 @@ sandbox targets for the smallest safe rehearsal.
|
|
|
473
746
|
|
|
474
747
|
| Result | What to do |
|
|
475
748
|
|---|---|
|
|
749
|
+
| `NEEDS_INPUT` | Complete setup's repository, tag, channel, baseline, and gate decisions. |
|
|
750
|
+
| `LOCAL_ONLY_DETECTED` | Establish a remote channel or keep only a local configuration design; do not claim production readiness. |
|
|
751
|
+
| `SETUP_DIGEST_MISMATCH` | Facts or answers changed; rerun dry-run, review, and confirm the new digest. |
|
|
752
|
+
| `CONFIG_EXISTS` | Setup never overwrites the existing config; assess it and edit incrementally. |
|
|
753
|
+
| `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
754
|
| `CONFIG_INVALID` | Correct `.release-skill/project.yaml`, then rerun `assess`. |
|
|
477
755
|
| `PUBLIC_FILE_MISSING` | Add or correct the configured public file. |
|
|
478
756
|
| `FORBIDDEN_CONTENT_DETECTED` | Remove the leaked/private content, then prepare again. |
|
|
479
757
|
| `SNAPSHOT_FIDELITY_FAILED` | Inspect the source/snapshot path and rerun `prepare`. |
|
|
480
758
|
| `BASELINE_CHANGED` | Keep the human edit, then prepare, review, and approve again. |
|
|
481
|
-
| `GATE_FAILED` |
|
|
759
|
+
| `GATE_FAILED` during `prepare` | Fix the snapshot gate or frozen public artifact, then run a new `prepare`; the failed plan cannot be approved. |
|
|
760
|
+
| `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
761
|
| `PARTIAL` | Do not restart or delete remote state; review the returned `runPath` and run `reconcile` (see above). |
|
|
483
762
|
| `PUBLISHED` | Run `verify --plan <planPath> --run <publishRunPath>`; this is not terminal success. |
|
|
484
763
|
| `VERIFIED` | Remote state, exact npm install, and configured plugin consumer installs all matched the frozen plan. |
|
|
@@ -486,15 +765,16 @@ sandbox targets for the smallest safe rehearsal.
|
|
|
486
765
|
## Skills
|
|
487
766
|
|
|
488
767
|
- `release-help`: environment check and next-step guidance.
|
|
768
|
+
- `release-setup`: read-only discovery, human calibration, and create-once first-use configuration.
|
|
489
769
|
- `release-assess`: read-only release readiness report.
|
|
490
770
|
- `release-prepare`: local snapshot and reviewable release plan.
|
|
491
771
|
- `release-publish`: approved, digest-confirmed frozen GitHub+npm publishing.
|
|
492
772
|
- `release-reconcile`: evidence-based PARTIAL recovery with human intervention on conflicts.
|
|
493
773
|
- `release-verify`: post-publish verification; only `VERIFIED` is the happy end.
|
|
494
774
|
|
|
495
|
-
Conflicts still default to human intervention.
|
|
496
|
-
the
|
|
497
|
-
|
|
775
|
+
Conflicts still default to human intervention. The npm-installed `release-skill`
|
|
776
|
+
CLI is the supported user entry after v0.1.1 production publication; source
|
|
777
|
+
checkout remains the development/contributor fallback.
|
|
498
778
|
|
|
499
779
|
## License
|
|
500
780
|
|