release-skill 0.2.2 → 0.2.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 (54) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codebuddy-plugin/plugin.json +4 -2
  4. package/.codex-plugin/plugin.json +2 -2
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/CHANGELOG.md +41 -0
  7. package/CONTRIBUTING.md +27 -0
  8. package/INSTALL.md +95 -139
  9. package/INSTALL.zh-CN.md +70 -121
  10. package/README.md +265 -916
  11. package/README.zh-CN.md +222 -537
  12. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  13. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  14. package/adapters/claude/bin/release-skill.bundle.mjs +18710 -16694
  15. package/adapters/claude/schemas/release-plan.schema.json +137 -0
  16. package/adapters/claude/schemas/release-project.schema.json +93 -0
  17. package/adapters/claude/schemas/release-run.schema.json +70 -2
  18. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  19. package/adapters/codex/bin/release-skill.bundle.mjs +18710 -16694
  20. package/adapters/codex/schemas/release-plan.schema.json +137 -0
  21. package/adapters/codex/schemas/release-project.schema.json +93 -0
  22. package/adapters/codex/schemas/release-run.schema.json +70 -2
  23. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  24. package/adapters/kimi/bin/release-skill.bundle.mjs +18710 -16694
  25. package/adapters/kimi/schemas/release-plan.schema.json +137 -0
  26. package/adapters/kimi/schemas/release-project.schema.json +93 -0
  27. package/adapters/kimi/schemas/release-run.schema.json +70 -2
  28. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  29. package/adapters/workbuddy/bin/release-skill.bundle.mjs +18710 -16694
  30. package/adapters/workbuddy/schemas/release-plan.schema.json +137 -0
  31. package/adapters/workbuddy/schemas/release-project.schema.json +93 -0
  32. package/adapters/workbuddy/schemas/release-run.schema.json +70 -2
  33. package/bin/release-skill.bundle.mjs +18710 -16694
  34. package/package.json +1 -1
  35. package/schemas/release-plan.schema.json +137 -0
  36. package/schemas/release-project.schema.json +93 -0
  37. package/schemas/release-run.schema.json +70 -2
  38. package/src/adapters/plugin-marketplace.mjs +1602 -605
  39. package/src/commands/prepare.mjs +441 -32
  40. package/src/commands/publish.mjs +107 -75
  41. package/src/commands/reconcile.mjs +92 -327
  42. package/src/commands/setup.mjs +148 -20
  43. package/src/commands/verify.mjs +315 -25
  44. package/src/core/baseline.mjs +21 -1
  45. package/src/core/checkpoints.mjs +50 -7
  46. package/src/core/config.mjs +15 -0
  47. package/src/core/errors.mjs +2 -0
  48. package/src/core/installation-contract.mjs +341 -0
  49. package/src/core/plan.mjs +307 -6
  50. package/src/platforms/codebuddy.mjs +191 -238
  51. package/src/platforms/codex.mjs +369 -0
  52. package/src/platforms/kimi.mjs +164 -119
  53. package/src/platforms/registry.mjs +180 -4
  54. package/src/producers/build-adapters.mjs +9 -2
package/README.md CHANGED
@@ -2,37 +2,41 @@
2
2
 
3
3
  [简体中文](README.zh-CN.md) · Installation: [English](INSTALL.md) / [简体中文](INSTALL.zh-CN.md)
4
4
 
5
- <!-- release-skill:release-version: 0.2.2 -->
6
- Release preparation for Claude Code, Codex, and Kimi Code, with human-edited files kept intact.
5
+ <!-- release-skill:release-version: 0.2.4 -->
6
+ Release preparation for Claude Code, CodeBuddy, WorkBuddy, Codex, and Kimi Code, with human-edited files kept intact.
7
7
 
8
8
  release-skill helps a maintainer answer three questions: what will be released,
9
- which checks still fail, and which exact bytes will reach users. It freezes the
10
- reviewed artifacts first and publishes those same artifacts later; it does not
11
- regenerate a README or re-pack the live workspace at the last step.
9
+ which checks still fail, and which exact bytes will reach users. release-skill
10
+ does not regenerate or rewrite project source files. `prepare` copies each
11
+ configured public file into an isolated snapshot and verifies the copied bytes —
12
+ it freezes the reviewed artifacts first and publishes those same artifacts later.
13
+ Setup surfaces only the deterministic `compactSummary` review view; the full
14
+ report stays in a temporary session directory.
12
15
 
13
16
  <!-- release-skill:managed:start id=latest-release -->
14
- **0.2.2** (2026-07-26)
17
+ **0.2.4** (2026-07-28)
15
18
 
16
- v0.2.2 closes the CodeBuddy platform distribution loop and adds the external independent marketplace distribution form. CodeBuddy/WorkBuddy joins the publish/reconcile/verify pipeline through a human attestation closed loop isomorphic to kimi: because the codebuddy CLI cannot pin a frozen ref (no ref option; installs track the default branch/latest), the execute phase writes a manual install requirement (never execs the CLI), and observe/verify consume a structured human attestation with read-only install-point validation — missing, expired, mismatched, or path-escaping attestations all fail closed, at the same severity as kimi (post-publish verification cannot be waived). The external marketplace form lets a distribution declare `marketplaceRepo` so prepare freezes the external marketplace HEAD online (codex hard-frozen to a commit sha, claude weak-frozen to its default branch name) with frozen-sha and index-entry integrity checks, and plugin-marketplace gains matching preflight/observe branches under the `external-marketplace-v1` payload contract while the inline/kimi/codebuddy/legacy branches stay byte-for-byte unchanged. Installation docs (en + zh-CN) are unified on the independent marketplace `ifoohoo/artifact-skill-set` primary path. The npm package name (`release-skill`), publishing identity (`publisher: mzdbxqh`), public repository (`ifoohoo/release-skill`), and corporate maintainer remain unchanged.
17
-
18
- **Added**
19
-
20
- - **CodeBuddy platform human attestation closed loop**: CodeBuddy/WorkBuddy joins the release pipeline (`publish`/`reconcile`/`verify`) via a human attestation closed loop isomorphic to kimi. The codebuddy CLI's marketplace add/install cannot pin a frozen ref (no ref option; installs track the default branch/`latest`, measured), so the automated install checkpoint cannot guarantee frozen-artifact identity. Execute writes a manual install requirement and never execs the CLI; observe/verify consume a structured human attestation and read-only-validate the install point. Missing, expired, mismatched, or path-escaping attestations all fail closed, at the same severity as kimi (post-publish verification cannot be waived). Two measured install channels: desktop installs through the WorkBuddy desktop unified marketplace `artifact-skill-set` (`installPath` must contain the `/.workbuddy/plugins/marketplaces/artifact-skill-set/plugins/<plugin>` tail segment, segment-checked); cli runs the bundled CLI under an isolated `HOME=<authorityDir>/codebuddy-home` (`installPath` must be contained in that isolated home's marketplace plugin root). Attestations carry `installChannel` + `marketplace` fields and are validated per channel. Pipeline routing: platform id `codebuddy`, distributionType `codebuddy-plugin`, actionType `codebuddy-marketplace-install`, Tier 3 (same tier as kimi); the build adapter keeps its historical directory name `workbuddy` (`adapters/workbuddy/`, `.codebuddy-plugin/plugin.json` manifest, bytes unchanged).
21
- - **External independent marketplace distribution form**: a distribution declaring `marketplaceRepo` enables the external form. The prepare production loop freezes the external marketplace HEAD online via `git ls-remote --symref` (codex hard-frozen to a commit sha, claude weak-frozen to its default branch name) and validates the external index entry via `gh api` (name match, exactly one entry, `entry.version == target version` for the claude form); declaring the external form while offline fails closed, and the external repository is strictly read-only. Frozen actions carry `repo=marketplaceRepo`, `ref=add-ref`, `marketplaceCommitSha`, `marketplaceLocation=external`, `payloadContract=external-marketplace-v1`; `snapshotPath`/`manifestDigest` still bind this unit's frozen snapshot (payload authority unchanged). Plan integrity gains an external branch (repo match, `marketplaceLocation`, 40-hex `marketplaceCommitSha`, ref structure safety); kimi/codebuddy with `marketplaceRepo` fail closed. `plugin-marketplace` gains external preflight/observe branches: `external-marketplace-v1` uses the same whole-tree containment semantics as `declared-manifest-v1` (authority is `.`, host-added paths recorded as `extraInstalledPaths` rather than failing); preflight skips the in-snapshot marketplace segment and reads the plugin manifest from the snapshot root, validating name/version and the frozen fields; observe reuses the existing strategy for install-side entry comparison (weak-frozen claude version drift fails closed). Inline/kimi/codebuddy/legacy/`declared-manifest-v1` branches are byte-for-byte unchanged.
19
+ v0.2.4 is a documentation and marketplace-source remediation release. It corrects the default marketplace source to the bundled-family repository (ifoohoo/release-skill), eliminates stale v0.1.9 residuals, improves README bilingual consistency and navigation, and strengthens anti-regression gates for version drift.
22
20
 
23
21
  **Changed**
24
22
 
25
- - **Installation docs unified on the marketplace primary path (en + zh-CN)**: README and INSTALL (all four documents) now route plugin installation through the independent marketplace `ifoohoo/artifact-skill-set` as the primary path, and the READMEs add a workflow overview and platform distribution description. Standards and public references (`standards/06-adapter-contract.md`, `references/06-adapter-contract.md`) are synchronized to the codebuddy attestation form and the external marketplace form with its ordering constraints.
23
+ - **Default marketplace source corrected**: all installation documentation now uses the bundled-family repository `ifoohoo/release-skill` instead of the external marketplace `ifoohoo/artifact-skill-set`. Claude Code install command is now `/plugin marketplace add ifoohoo/release-skill` with `release-skill@release-skill`.
24
+ - **README restructured for readability**: both EN and ZH READMEs now include a table of contents, Documentation navigation section, and reorganized chapter flow (Quick start moved before preservation contract). Significantly shorter than before.
25
+ - **Positioning sentences completed**: README and root workspace README now list all supported platforms (Claude Code, CodeBuddy, WorkBuddy, Codex, Kimi Code).
26
+ - **Anti-regression gate expanded**: `sync-version.mjs` TEXT_TARGETS now covers the safe-first-command version statement in both EN and ZH READMEs, preventing future v0.1.9-type drift.
26
27
 
27
28
  **Fixed**
28
29
 
29
- - **CodeBuddy plugin manifest `skills` field is now an array**: the `.codebuddy-plugin/plugin.json` manifest's `skills` field is emitted as an array, matching the CodeBuddy host's expected shape.
30
- - **`verificationGate` scope distribution enum includes `codebuddy-plugin`**: `release-project.schema.json` adds `codebuddy-plugin` to the `verificationGate.scope.distribution` enum (mirroring the existing `kimi-plugin` rule), keeping the schema and the runtime gate whitelist consistent; the embedded schema copies in all four adapters are rebuilt to match.
30
+ - **v0.1.9 residual eliminated**: the safe-first-command block in both READMEs now correctly references the current version (was stuck at v0.1.9).
31
+ - **Stale counting removed**: replaced fragile 'All four plugin hosts' / '四种插件宿主' with 'All supported plugin hosts' / '各插件宿主'.
32
+ - **Root README boundary corrected**: clarified that README/INSTALL/CHANGELOG are human-maintained source files, while references/schemas/adapters/skills are generated artifacts.
33
+ - **CONTRIBUTING updated**: added 'Do not edit generated files' section documenting the authority source and regeneration workflow for references/, schemas/, adapters/, and skills/.
34
+ - **AGENTS.md language rule adjusted**: governance rules file may use English; user-facing documentation remains Chinese.
31
35
  <!-- release-skill:managed:end id=latest-release -->
32
36
 
33
37
  <!-- release-skill:capability:external-write-boundary -->
34
- > **Current boundary:** v0.2.2 is the current release (v0.1.9 previously held
35
- > published status before the codex migrated-command-skills fix was added).
38
+ > **Current boundary:** v0.2.4 is the current release (v0.2.2 previously held
39
+ > published status before the platform verification convergence fix was added).
36
40
  > v0.1.1 completed a real production release to GitHub and npm — the first
37
41
  > production-verified milestone — followed by
38
42
  > exact npm installation and Claude/Codex consumer installation verification
@@ -49,7 +53,7 @@ v0.2.2 closes the CodeBuddy platform distribution loop and adds the external ind
49
53
  > publish global preflight.
50
54
 
51
55
  <!-- release-skill:capability:safe-first-command -->
52
- > **Production path verified since the v0.1.1 milestone; v0.1.9 is the current
56
+ > **Production path verified since the v0.1.1 milestone; v0.2.4 is the current
53
57
  > release.** The npm-installed CLI is the supported user entry. Source checkout
54
58
  > is the development/contributor fallback.
55
59
  >
@@ -65,89 +69,26 @@ v0.2.2 closes the CodeBuddy platform distribution loop and adds the external ind
65
69
  > specifically requires `prepare --online --production`. Without digest confirmation,
66
70
  > no remote preflight or write starts.
67
71
 
68
- ## Release workflow overview
72
+ ## Table of contents
69
73
 
70
- release-skill models the release lifecycle as a strict state machine so that every
71
- stage has explicit entry and exit conditions and no stage can be skipped. The
72
- normative definition lives in `references/01-state-machine.md`.
73
-
74
- ```text
75
- DISCOVERED -> ASSESSED -> PREPARED -> APPROVED -> PUBLISHING -> PUBLISHED -> VERIFIED
76
- exception states: NEEDS_INPUT / BLOCKED / PARTIAL
77
- ```
78
-
79
- Each CLI command maps to one transition:
80
-
81
- - `help` checks the environment; `setup` discovers the project and, after digest
82
- confirmation, creates the config once.
83
- - `assess` performs a read-only readiness evaluation (`DISCOVERED -> ASSESSED`).
84
- - `prepare` runs the verification gates, freezes an immutable release plan, and
85
- copies the configured public files into an isolated snapshot
86
- (`ASSESSED -> PREPARED`). It writes only under `.release-skill/` and never
87
- touches remote services.
88
- - `approve` records human approval bound to the plan digest with a 24-hour expiry
89
- (`PREPARED -> APPROVED`). A changed plan invalidates the approval automatically.
90
- - `publish` executes the external write checkpoints in order
91
- (`APPROVED -> PUBLISHING -> PUBLISHED`).
92
- - `reconcile` recovers from `PARTIAL`; `verify` performs a fresh consumer-install
93
- check in an isolated environment (`PUBLISHED -> VERIFIED`).
94
-
95
- `PUBLISHED` is **not** the terminal state. Only a fresh `verify` run that confirms
96
- remote state and exact consumer installs match the frozen plan reaches `VERIFIED`.
97
-
98
- **Publishing checkpoint order.** `publish` runs a read-only global preflight over
99
- all actions, then executes and observes in this fixed order: public snapshot
100
- branch -> signed/tracked tag -> npm publish -> GitHub Release -> configured
101
- Claude/Codex plugin-marketplace install -> run record. Any failure stops subsequent
102
- checkpoints and the run lands in `PARTIAL`. The system never auto-deletes remote
103
- tags, unpublishes packages, or restarts from scratch; `reconcile` queries the actual
104
- remote state, skips already-consistent steps, and retries only safe, incomplete
105
- actions, while remote conflicts require a human decision.
106
-
107
- ## Why this is safe for a hand-edited README
108
-
109
- release-skill does not regenerate or rewrite project source files. `prepare` copies
110
- each configured public file from the current workspace into an isolated local
111
- snapshot and verifies the copied bytes. That includes the complete README:
112
- slogans, examples, prose, formatting, and later human edits.
113
-
114
- - A later prepare reads the current file again; it does not rebuild it from a template.
115
- - The snapshot must match the source bytes exactly.
116
- - A changed plan gets a new digest, so an old approval cannot authorize it.
117
- - A source edit after prepare makes publish stop before remote writes. Preserve
118
- the edit by preparing, reviewing, and approving a new plan.
119
- - Tampering with a frozen snapshot, Git object, or tarball fails its digest gate.
120
- - Existing remote branches, tags, releases, or npm versions require human
121
- intervention; the tool does not force or overwrite them.
122
- - Only files listed in `publicFiles` are copied. Add translated READMEs, images,
123
- demos, and linked documents explicitly when they belong in the release.
124
- - A release freezes only the current truth: `prepare` never refreshes or
125
- rewrites human docs. Maintainers update README, INSTALL, and CHANGELOG first
126
- — including the machine-readable `release-skill:release-version` markers,
127
- which must equal the `package.json` version, and the formal CHANGELOG
128
- heading for the current version — then prepare, review, and approve. A
129
- pre-release gate fails closed when any doc version marker or the CHANGELOG
130
- current-version entry drifts.
131
-
132
- This is the preservation contract: **copy current truth, freeze reviewed
133
- truth, and never rewrite human truth.**
74
+ - [Quick start](#quick-start)
75
+ - [Release workflow](#release-workflow)
76
+ - [Documentation](#documentation)
77
+ - [Skills](#skills)
78
+ - [Platform distribution](#platform-distribution)
79
+ - [License](#license)
134
80
 
135
81
  ## Quick start
136
82
 
137
- Every read-only step below keeps potentially large reports in temporary files
138
- and surfaces only the deterministic `compactSummary` review view; the summary
139
- is a review aid, never a substitute for the bound digest authorization.
140
-
141
- ### Install / requirements
83
+ ### Install
142
84
 
143
- - Node.js 22+
144
- - Git 2.30+
145
- - A target Git repository with at least one commit
85
+ - Node.js 22+, Git 2.30+, a target Git repository with at least one commit.
146
86
 
147
- **Install from npm (recommended):**
87
+ **npm (recommended):**
148
88
 
149
89
  ```bash
150
90
  npm install -g release-skill
91
+ release-skill help
151
92
  ```
152
93
 
153
94
  Or run directly without installing:
@@ -156,251 +97,200 @@ Or run directly without installing:
156
97
  npx release-skill help
157
98
  ```
158
99
 
159
- **Verify the install:**
100
+ **Plugin (Claude Code / CodeBuddy / WorkBuddy / Codex):**
160
101
 
161
- ```bash
162
- release-skill help
163
- ```
164
-
165
- **Install as a plugin (Claude Code / CodeBuddy / WorkBuddy / Codex / Kimi Code):**
102
+ Claude Code, CodeBuddy, WorkBuddy, and Codex install from the bundled-family
103
+ marketplace `ifoohoo/release-skill`:
166
104
 
167
- All four plugin hosts install from the unified marketplace
168
- `ifoohoo/artifact-skill-set` — for example, in a Claude Code session:
169
-
170
- ```
171
- /plugin marketplace add ifoohoo/artifact-skill-set
172
- /plugin install release-skill@artifact-skill-set
173
105
  ```
174
-
175
- `ifoohoo/artifact-skill-set` is an **external independent marketplace**: the
176
- plugin repository carries only the plugin manifest, while the marketplace index
177
- is centralized in the external marketplace repository. When a release unit's
178
- plugin distribution declares `marketplaceRepo`, `prepare --online --production`
179
- freezes the external marketplace HEAD (Codex pins the commit sha — strong
180
- freeze; Claude pins the default branch name — weak freeze) and verifies the
181
- installed payload against the unit's own frozen snapshot whole-tree. **Release
182
- ordering:** publish the external marketplace index first — its entry version
183
- must equal the target release version — before `prepare` can freeze a
184
- marketplace sha containing that entry. See [INSTALL.md](INSTALL.md) for the
185
- per-platform commands and `references/06-adapter-contract.md` §2.3/§2.4 for the
186
- contract and the advanced direct-repository options.
187
-
188
- **Development install (contributor fallback, from source checkout):**
189
-
190
- Set the checkout location and install dependencies:
191
-
192
- ```bash
193
- export RELEASE_SKILL_HOME=/absolute/path/to/release-skill
194
- cd "$RELEASE_SKILL_HOME"
195
- npm exec --yes pnpm@10.17.1 -- install --frozen-lockfile
106
+ /plugin marketplace add ifoohoo/release-skill
107
+ /plugin install release-skill@release-skill
196
108
  ```
197
109
 
198
- Then use the CLI via `node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs"`.
110
+ > **Prerequisite: GitHub access.** The `owner/repo` shorthand makes Claude Code
111
+ > clone via SSH. If you do not use SSH, pass the full HTTPS URL —
112
+ > `/plugin marketplace add https://github.com/ifoohoo/release-skill` — or set
113
+ > `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`.
199
114
 
200
- First keep local plans, approvals, and frozen artifacts out of Git:
115
+ **Kimi Code:** Kimi Code has no marketplace install API. Install manually from a
116
+ pinned release tag — see [INSTALL.md](INSTALL.md#install-as-a-kimi-code-plugin).
201
117
 
202
- ```gitignore
203
- .release-skill/*
204
- !.release-skill/project.yaml
205
- ```
118
+ See [INSTALL.md](INSTALL.md) for CodeBuddy, Codex, and Kimi Code commands.
206
119
 
207
- ### First use: deterministic setup without loading the full report
120
+ ### Main workflow
208
121
 
209
- Setup is read-only by default. Keep its potentially large report in temporary
210
- files; show the user or Agent only the deterministic `compactSummary` review
211
- view. The summary does not replace authorization: `setupDigest` still binds the
212
- complete facts, candidates, and answers.
122
+ Run these steps in order. Steps 1-4 are safe (read-only or local-only);
123
+ steps 5-9 require explicit human gates.
213
124
 
214
125
  ```bash
126
+ CLI=(release-skill) # or: CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
215
127
  PROJECT=/absolute/path/to/my-project
216
- SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
217
- REPORT="$SETUP_SESSION/discovery.json"
218
- ANSWERS="$SETUP_SESSION/answers.json"
219
- BOUND_REPORT="$SETUP_SESSION/bound.json"
220
- printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
221
-
222
- release-skill setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
223
- 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"
128
+ ACTOR=your-name
224
129
  ```
225
130
 
226
- `NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally exit with code 2. If
227
- `proposalConflicts` is non-empty—including `PUBLIC_REPO_AUTHORITY_CONFLICT` or
228
- a public-file mapping conflict—stop and let a human correct the conflicting
229
- repository or mapping authority, then rerun setup. Do not guess a winner.
131
+ 1. **help** check the environment:
132
+ ```bash
133
+ "${CLI[@]}" help
134
+ ```
135
+ 2. **setup** — first-use only (read-only discovery, then create-once config):
136
+ ```bash
137
+ SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
138
+ REPORT="$SETUP_SESSION/discovery.json"
139
+ ANSWERS="$SETUP_SESSION/answers.json"
140
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
141
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
142
+ ```
143
+ If `proposalConflicts` is non-empty, stop and let a human correct the
144
+ conflicting repository or mapping authority. With no conflicts, extract
145
+ `recommendedAnswers` mechanically (never hand-write complete answers):
146
+ ```bash
147
+ SETUP_SESSION='<session-directory-absolute-path-printed-above>'
148
+ 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"
149
+ ```
150
+ Confirm the bound `setupDigest` once, then create the config:
151
+ ```bash
152
+ SETUP_SESSION='<session-directory-absolute-path-printed-above>'
153
+ PROJECT='<project-absolute-path-printed-above>'
154
+ ANSWERS="$SETUP_SESSION/answers.json"
155
+ CREATED_REPORT="$SETUP_SESSION/created.json"
156
+ POST_REPORT="$SETUP_SESSION/post-setup.json"
157
+ ASSESS_REPORT="$SETUP_SESSION/assess.json"
158
+ "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" \
159
+ --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT"
160
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$POST_REPORT"
161
+ set +e
162
+ "${CLI[@]}" assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
163
+ ASSESS_EXIT=$?
164
+ set -e
165
+ [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
166
+ 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)}' "$CREATED_REPORT" "$POST_REPORT" "$ASSESS_REPORT"
167
+ node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
168
+ ```
169
+ The write must return `CONFIG_CREATED`; the next setup must return
170
+ `ALREADY_CONFIGURED`. Existing configuration is never regenerated — make only
171
+ reviewed incremental edits. Discovered scripts are `SIDE_EFFECTS_UNPROVEN`.
172
+ Add a project-specific hook or gate only after human review: edit
173
+ `projectConfig.hooks`, or edit `verificationGates` and add the same id to
174
+ `selectedGateIds`, then rerun the bound dry-run.
175
+ See [INSTALL.md](INSTALL.md#first-use-setup) for the full multi-step flow.
176
+ 3. **assess** — read-only readiness:
177
+ ```bash
178
+ "${CLI[@]}" assess --root "$PROJECT" --offline --json
179
+ ```
180
+ 4. **prepare** — local snapshot and plan freeze:
181
+ ```bash
182
+ "${CLI[@]}" prepare --root "$PROJECT" --offline \
183
+ --acknowledge-hook-side-effects \
184
+ --acknowledge-gate-side-effects --json
185
+ ```
186
+ Omit an acknowledgement only when that project config has no corresponding
187
+ hook or snapshot gate. Never grant either acknowledgement before reviewing
188
+ the configured executable, arguments, working directory, and side effects.
189
+ 5. **Human review:** inspect `planPath`, `externalActions`, `targetVersion`, and `planDigest`.
190
+ 6. **prepare --production** — freeze the production plan:
191
+ ```bash
192
+ PLAN_JSON=$("${CLI[@]}" prepare --root "$PROJECT" --online --production \
193
+ --acknowledge-hook-side-effects \
194
+ --acknowledge-gate-side-effects --json)
195
+ PLAN_PATH=$(printf '%s\n' "$PLAN_JSON" | jq -r '.planPath')
196
+ PLAN_DIGEST=$(printf '%s\n' "$PLAN_JSON" | jq -r '.planDigest')
197
+ ```
198
+ 7. **approve** — human approval bound to the plan digest (24-hour expiry):
199
+ ```bash
200
+ APPROVAL_JSON=$("${CLI[@]}" approve --plan "$PLAN_PATH" \
201
+ --digest "$PLAN_DIGEST" --actor "$ACTOR" --json)
202
+ APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
203
+ ```
204
+ 8. **publish** — remote writes start here:
205
+ ```bash
206
+ PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
207
+ --plan "$PLAN_PATH" --approval "$APPROVAL_PATH" \
208
+ --confirm-production "$PLAN_DIGEST" --json)
209
+ PUBLISH_RUN_PATH=$(printf '%s\n' "$PUBLISH_JSON" | jq -r '.runPath')
210
+ ```
211
+ `PUBLISHED` is **not** the terminal state.
212
+ 9. **verify** — consumer install check:
213
+ ```bash
214
+ "${CLI[@]}" verify --root "$PROJECT" \
215
+ --plan "$PLAN_PATH" --run "$PUBLISH_RUN_PATH" \
216
+ --acknowledge-gate-side-effects --json
217
+ ```
230
218
 
231
- With no conflicts, copy the machine proposal mechanically. The Agent must not
232
- rewrite or transcribe it:
219
+ The handoff example requires `jq`. Without it, copy the returned JSON fields
220
+ exactly; do not pass angle-bracket labels as shell syntax.
233
221
 
234
- ```bash
235
- SETUP_SESSION='/session-directory-absolute-path-printed-above'
236
- PROJECT='/project-absolute-path-printed-above'
237
- REPORT="$SETUP_SESSION/discovery.json"
238
- ANSWERS="$SETUP_SESSION/answers.json"
239
- BOUND_REPORT="$SETUP_SESSION/bound.json"
240
- 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"
241
-
242
- release-skill setup --root "$PROJECT" --answers "$ANSWERS" --json > "$BOUND_REPORT"
243
- 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"
244
- printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
245
- ```
222
+ ### PARTIAL recovery and reconcile
246
223
 
247
- Review that bound summary and exact digest once. After explicit human
248
- confirmation, use the confirmed digest literal to create the configuration:
224
+ When `publish` succeeds at some checkpoints but fails at others, the run enters
225
+ `PARTIAL` status. **Do not restart from scratch and do not delete remote state.**
249
226
 
250
227
  ```bash
251
- SETUP_SESSION=<session-directory-absolute-path-printed-above>
252
- PROJECT=<project-absolute-path-printed-above>
253
- ANSWERS="$SETUP_SESSION/answers.json"
254
- CREATED_REPORT="$SETUP_SESSION/created.json"
255
- POST_REPORT="$SETUP_SESSION/post-setup.json"
256
- ASSESS_REPORT="$SETUP_SESSION/assess.json"
257
- release-skill setup --root "$PROJECT" --answers "$ANSWERS" \
258
- --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT"
259
- release-skill setup --root "$PROJECT" --json > "$POST_REPORT"
260
- set +e
261
- release-skill assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
262
- ASSESS_EXIT=$?
263
- set -e
264
- [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
265
- 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"
266
- node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
228
+ RECONCILE_JSON=$("${CLI[@]}" reconcile --root "$PROJECT" \
229
+ --run "$PUBLISH_RUN_PATH" \
230
+ --plan "$PLAN_PATH" \
231
+ --approval "$APPROVAL_PATH" \
232
+ --confirm-production "$PLAN_DIGEST" --json)
233
+ RECONCILE_RUN_PATH=$(printf '%s\n' "$RECONCILE_JSON" | jq -r '.runPath')
234
+ "${CLI[@]}" verify --root "$PROJECT" \
235
+ --plan "$PLAN_PATH" --run "$RECONCILE_RUN_PATH" \
236
+ --acknowledge-gate-side-effects --json
267
237
  ```
268
238
 
269
- The write must return `CONFIG_CREATED`; the next setup must return
270
- `ALREADY_CONFIGURED`. Existing configuration is never regenerated—make only
271
- reviewed incremental edits. Discovered interpreter/package-manager scripts are
272
- `SIDE_EFFECTS_UNPROVEN` and are not selected automatically. Add a project-specific
273
- hook or gate only after human review: edit `projectConfig.hooks`, or edit
274
- `verificationGates` and add the same id to `selectedGateIds`, then rerun the
275
- bound dry-run. Keep human files at `mode: preserve`, and
276
- use `sourceScope: workspace` only for explicit cross-unit shared sources.
239
+ `reconcile` queries the actual remote state, skips already-consistent steps,
240
+ and retries only safe incomplete actions. Remote conflicts require human
241
+ decision. Successful reconcile returns `PUBLISHED`, not `VERIFIED`.
277
242
 
278
- #### Advanced schema reference—not the first-use path
243
+ ## Release workflow
279
244
 
280
- The wrapper below illustrates the schema only. Do not hand-write it during the
281
- normal setup path; mechanically extract `recommendedAnswers` as shown above.
245
+ release-skill models the release lifecycle as a strict state machine
246
+ (normative definition: `references/01-state-machine.md`):
282
247
 
283
- ```json
284
- {
285
- "projectConfig": {
286
- "apiVersion": "release-skill/v1",
287
- "kind": "ReleaseProject",
288
- "project": { "name": "my-project", "defaultBranch": "main" },
289
- "releaseUnits": [{
290
- "id": "my-project",
291
- "source": ".",
292
- "publicRepo": "owner/my-project",
293
- "version": { "source": "package.json", "tagTemplate": "v{version}" },
294
- "distributions": [{
295
- "type": "npm",
296
- "package": "my-project",
297
- "access": "public",
298
- "provenance": false,
299
- "tag": "latest",
300
- "registry": "https://registry.npmjs.org",
301
- "publisher": "my-npm-username"
302
- }],
303
- "publicFiles": [
304
- { "from": "README.md", "to": "README.md", "mode": "preserve" },
305
- { "from": "package.json", "to": "package.json", "mode": "preserve" }
306
- ],
307
- "requiredPublicFiles": ["README.md", "package.json"],
308
- "previousPublicBaseline": { "mode": "none" },
309
- "production": {
310
- "branchTemplate": "release/{tag}",
311
- "branchStrategy": "create-release-branch"
312
- }
313
- }]
314
- },
315
- "selectedGateIds": []
316
- }
248
+ ```text
249
+ DISCOVERED -> ASSESSED -> PREPARED -> APPROVED -> PUBLISHING -> PUBLISHED -> VERIFIED
250
+ exception states: NEEDS_INPUT / BLOCKED / PARTIAL
317
251
  ```
318
252
 
319
- This is a schema reference, not an onboarding template. Normal setup must use
320
- the machine proposal. `mode: none` is valid only when no public version exists.
253
+ Each CLI command maps to one transition. `PUBLISHED` is **not** the terminal
254
+ state only a fresh `verify` that confirms remote state and consumer installs
255
+ match the frozen plan reaches `VERIFIED`.
321
256
 
322
- The following reference shows the exact relationship between a manually
323
- reviewed gate and `selectedGateIds`. Apply that relationship only as an
324
- incremental edit to the extracted machine proposal:
257
+ **Preservation contract:** release-skill does not regenerate or rewrite project
258
+ source files. `prepare` copies each configured public file into an isolated
259
+ snapshot and verifies the copied bytes. A later prepare reads the current file
260
+ again; it never rebuilds from a template. Only files listed in `publicFiles` are
261
+ copied. `prepare` never refreshes or rewrites human docs — maintainers update
262
+ README, INSTALL, and CHANGELOG first, then prepare, review, and approve.
325
263
 
326
- ```json
327
- {
328
- "projectConfig": {
329
- "apiVersion": "release-skill/v1",
330
- "kind": "ReleaseProject",
331
- "project": { "name": "my-project", "defaultBranch": "main" },
332
- "releaseUnits": [{
333
- "id": "my-project",
334
- "source": ".",
335
- "publicRepo": "owner/my-project",
336
- "version": { "source": "package.json", "tagTemplate": "v{version}" },
337
- "distributions": [{
338
- "type": "npm",
339
- "package": "my-project",
340
- "access": "public",
341
- "provenance": false,
342
- "tag": "latest",
343
- "registry": "https://registry.npmjs.org",
344
- "publisher": "my-npm-username"
345
- }],
346
- "publicFiles": [
347
- { "from": "package.json", "to": "package.json", "mode": "preserve" }
348
- ],
349
- "requiredPublicFiles": ["package.json"],
350
- "previousPublicBaseline": { "mode": "none" },
351
- "production": {
352
- "branchTemplate": "release/{tag}",
353
- "branchStrategy": "create-release-branch"
354
- }
355
- }],
356
- "verificationGates": [{
357
- "id": "my-project-script-test",
358
- "phase": "snapshot-verify",
359
- "scope": { "unit": "my-project" },
360
- "command": ["node", "-e", "const p=require('./package.json');if(!p.name)process.exit(1)"],
361
- "cwd": ".",
362
- "timeoutMs": 30000,
363
- "envAllowlist": []
364
- }]
365
- },
366
- "selectedGateIds": ["my-project-script-test"]
367
- }
368
- ```
264
+ **Write safety:** `setup` is read-only by default (create-once after digest
265
+ confirmation). `prepare` writes only under `.release-skill/`. `publish` is the
266
+ production write entry, requiring both approval and the current plan digest.
267
+ Project hooks and gates are acknowledged local processes without an OS sandbox.
369
268
 
370
- The id must be copied from the current `gateCandidates`; do not invent one.
371
- The example command is self-contained in the public snapshot. A project script
372
- is valid only when the script and every dependency it needs are included in
373
- `publicFiles`; a snapshot gate cannot see the parent workspace's tests,
374
- development dependencies, or `node_modules` unless they are explicitly public.
269
+ ## Documentation
375
270
 
376
- ```bash
377
- release-skill setup --root /absolute/path/to/my-project \
378
- --answers /absolute/path/to/setup-answers.json --json
379
- release-skill setup --root /absolute/path/to/my-project \
380
- --answers /absolute/path/to/setup-answers.json \
381
- --write --confirm-setup <setupDigest> --json
382
- ```
271
+ | Document | Description |
272
+ |---|---|
273
+ | [INSTALL.md](INSTALL.md) / [INSTALL.zh-CN.md](INSTALL.zh-CN.md) | Full installation guide: npm, plugin, source checkout, setup flow, branch strategies |
274
+ | [CHANGELOG.md](CHANGELOG.md) | Release history |
275
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute (includes generated-artifact rules) |
276
+ | [SECURITY.md](SECURITY.md) | Security policy |
277
+ | `references/01-state-machine.md` | Normative state machine definition |
278
+ | `references/02-project-config.md` | Project configuration schema reference |
279
+ | `references/05-evidence-and-errors.md` | Evidence format and error codes |
280
+ | `references/06-adapter-contract.md` | Adapter and marketplace contract details |
281
+ | [GitHub Issues](https://github.com/ifoohoo/release-skill/issues) | Bug reports and feature requests |
383
282
 
384
- Setup atomically creates only an absent `.release-skill/project.yaml`.
385
- That create-once step uses the digest-registered `darwin-arm64` native
386
- prebuild shipped in v0.1.3; unsupported platforms fail closed with
387
- `SAFE_WRITE_UNAVAILABLE` instead of falling back to path-based writes.
388
- `ALREADY_CONFIGURED`/`CONFIG_EXISTS` means the existing file remains
389
- human-owned and must be edited incrementally. README, slogans, CHANGELOG, and
390
- business scripts are never generated or overwritten. A project with no remote
391
- channel reports `LOCAL_ONLY_DETECTED` instead of inventing production support.
283
+ ## Configuration
392
284
 
393
- The following is a minimal human-authored configuration. npm visibility,
394
- public-file boundaries, and remote targets must be explicit:
285
+ A minimal human-authored configuration (see [INSTALL.md](INSTALL.md) for the
286
+ full schema and setup flow):
395
287
 
396
288
  ```yaml
397
289
  apiVersion: release-skill/v1
398
290
  kind: ReleaseProject
399
-
400
291
  project:
401
292
  name: my-project
402
293
  defaultBranch: main
403
-
404
294
  releaseUnits:
405
295
  - id: my-project
406
296
  source: .
@@ -415,371 +305,25 @@ releaseUnits:
415
305
  - from: package.json
416
306
  to: package.json
417
307
  mode: preserve
418
- - from: LICENSE
419
- to: LICENSE
420
- mode: preserve
421
- requiredPublicFiles: [README.md, LICENSE, package.json]
308
+ requiredPublicFiles: [README.md, package.json]
422
309
  previousPublicBaseline:
423
- mode: none # first release: no prior public version exists
310
+ mode: none
424
311
  distributions:
425
312
  - type: npm
426
313
  package: my-project
427
- access: public # or restricted; choose the real package policy
428
- provenance: false # use true only after CI/OIDC is configured
314
+ access: public
315
+ provenance: false
429
316
  tag: latest
430
317
  registry: https://registry.npmjs.org
431
318
  publisher: my-npm-username
432
- # Optional: CLI smoke verification. When smokeBin is set, verify
433
- # installs the package in an isolated directory and runs the named
434
- # binary. Without smokeBin, verify only confirms install + name/version.
435
- # smokeBin: my-project
436
- # smokeArgs: [help, --json]
437
- # smokeExpectedJson:
438
- # command: help
439
- # status: READY
440
319
  production:
441
320
  branchTemplate: release/{tag}
442
321
  branchStrategy: create-release-branch
443
- releaseTitleTemplate: "{unit} {version}"
444
- releaseNotes: "Human-maintained release notes"
445
322
  ```
446
323
 
447
- Every release unit must declare its previous public baseline. Use `mode: none`
448
- only when you have verified that no earlier public version exists. For an
449
- existing public repository, bind the exact immutable ref and commit instead:
450
-
451
- ```yaml
452
- previousPublicBaseline:
453
- mode: bound
454
- repo: owner/my-project
455
- ref: release/v0.9.0
456
- commit: 0123456789abcdef0123456789abcdef01234567
457
- ```
458
-
459
- `none` is not a conflict-check bypass: publish still checks target branch,
460
- tag, GitHub Release, and npm version uniqueness before any write. A bound
461
- production prepare must run online so the ref-to-commit mapping can be observed.
462
- The default observer does not download remote file contents, so it reports a
463
- mapping diff and marks content diff unavailable. On drift, stop and choose
464
- `merge`, `adopt`, or `reject` manually. First obtain and review the actual remote
465
- commit; the tool does not download or merge its files. `merge` keeps both local
466
- and remote edits in the human-owned source. `adopt` copies the reviewed remote
467
- bytes into that source. `reject` stops the release while the remote/ref is
468
- investigated or corrected; never switch to `mode: none` to bypass the drift.
469
- After `merge` or `adopt`, rebind `previousPublicBaseline` to the accepted
470
- immutable `repo`/`ref`/`commit`, then run a new `prepare --online --production`,
471
- review, and approval.
472
-
473
- Choose a branch strategy that matches the real repository:
474
-
475
- - `create-release-branch` creates an absent immutable release branch and stops
476
- if the name already exists.
477
- - `advance-existing-branch` creates a single-parent commit on the exact
478
- `previousPublicBaseline` commit and permits only an ordinary fast-forward
479
- push; concurrent drift requires human intervention.
480
- - `initialize-default-branch` creates an absent standard branch under control.
481
- Only explicit `setAsDefaultBranch` and `expectedCurrentDefaultBranch` values
482
- add a separately approved, observed, and reconcilable default-branch action.
483
-
484
- Minimal configurations for the three strategies are:
485
-
486
- ```yaml
487
- # New immutable release branch; the target must not exist.
488
- previousPublicBaseline: { mode: none } # only for a true first public release
489
- production:
490
- branchTemplate: release/{tag}
491
- branchStrategy: create-release-branch
492
- ```
493
-
494
- ```yaml
495
- # Advance main; the bound ref must be exactly the target branch.
496
- previousPublicBaseline:
497
- mode: bound
498
- repo: owner/my-project
499
- ref: refs/heads/main
500
- commit: 0123456789abcdef0123456789abcdef01234567
501
- production:
502
- branchTemplate: main
503
- branchStrategy: advance-existing-branch
504
- ```
505
-
506
- ```yaml
507
- # One-time creation of an absent main and an explicit default-branch switch.
508
- previousPublicBaseline:
509
- mode: bound
510
- repo: owner/my-project
511
- ref: refs/heads/old-public-branch
512
- commit: 0123456789abcdef0123456789abcdef01234567
513
- production:
514
- branchTemplate: main
515
- branchStrategy: initialize-default-branch
516
- setAsDefaultBranch: true
517
- expectedCurrentDefaultBranch: old-public-branch
518
- ```
519
-
520
- The latter two require `prepare --online --production`. If the observed branch,
521
- commit, target absence, or current default branch differs, stop and update the
522
- human-owned source/config only after reviewing the real remote state; never
523
- force-push or weaken the baseline.
524
-
525
- This is a mechanics-only local example, not a complete npm publication map.
526
- Before a real release, enumerate every public runtime file, executable, type
527
- declaration, image, and linked document. In a monorepo, set `source` to a path
528
- such as `packages/my-plugin`, and keep each `from` path relative to the workspace
529
- root, for example `packages/my-plugin/README.md`.
530
-
531
- Before the first prepare, preferably commit `.gitignore`, `.release-skill/project.yaml`,
532
- the README, version files, and all intended release content so the Git baseline
533
- is easy to reproduce. Uncommitted edits that already exist at prepare time and
534
- remain unchanged are included in the snapshot/baseline; only a later change
535
- causes baseline validation to stop.
536
-
537
- ### Main workflow
538
-
539
- Run these steps in order. Steps 1–4 are safe default (read-only or local-only);
540
- steps 5–9 are production publishing with explicit human gates.
541
-
542
- ```bash
543
- # npm-installed CLI (recommended):
544
- CLI=(release-skill)
545
- PROJECT=/absolute/path/to/my-project
546
- ACTOR=your-name
547
- # Development fallback (source checkout):
548
- # CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
549
- ```
550
-
551
- The npm-installed CLI is the supported user entry after v0.1.1 production
552
- publication. The source checkout remains the development/contributor fallback.
553
-
554
- 1. **Environment check:**
555
- ```bash
556
- "${CLI[@]}" help
557
- ```
558
- 2. **First-use setup (only when config is absent; read-only):**
559
- ```bash
560
- "${CLI[@]}" setup --root "$PROJECT" --json
561
- ```
562
- Follow the mechanical `compactSummary` and `recommendedAnswers` path above;
563
- confirm the bound `setupDigest` once, and skip this step when configuration exists.
564
- 3. **Readiness assessment (read-only):**
565
- ```bash
566
- "${CLI[@]}" assess --root "$PROJECT" --offline --json
567
- ```
568
- 4. **Local snapshot and plan freeze:**
569
- ```bash
570
- "${CLI[@]}" prepare --root "$PROJECT" --offline \
571
- --acknowledge-hook-side-effects \
572
- --acknowledge-gate-side-effects --json
573
- ```
574
- Omit an acknowledgement only when that project config has no corresponding
575
- hook or snapshot gate. Never grant either acknowledgement before reviewing
576
- the configured executable, arguments, working directory, and side effects.
577
- 5. **Human review:** inspect the returned `planPath`, `externalActions`,
578
- `units[].targetVersion`, and `planDigest`. Each unit's snapshot is under
579
- `<evidenceDir>/snapshots/<unit-id>/`. The release-skill pipeline writes its
580
- own data under `.release-skill/`; acknowledged project hooks and gates are
581
- arbitrary project processes without an operating-system sandbox and may
582
- write elsewhere, access the network, and read any credentials, tokens,
583
- keys, and environment variables accessible to the current account.
584
- 6. **Production plan freeze:**
585
- ```bash
586
- PRODUCTION_JSON=$("${CLI[@]}" prepare --root "$PROJECT" --online --production \
587
- --acknowledge-hook-side-effects \
588
- --acknowledge-gate-side-effects --json)
589
- printf '%s\n' "$PRODUCTION_JSON" | jq .
590
- PLAN_PATH=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planPath')
591
- PLAN_DIGEST=$(printf '%s\n' "$PRODUCTION_JSON" | jq -r '.planDigest')
592
- ```
593
- As above, omit only acknowledgements that are not required by the project
594
- config, and review every configured process before granting them.
595
- Review the new plan's externalActions, npm policy, branch/tag, and frozen
596
- digests. `prepare --json` returns the immutable production authority as
597
- `<project>/.release-skill/plans/<planDigest>.json`; always carry that returned
598
- `planPath` forward. `.release-skill/release-plan.json` is only a mutable
599
- convenience alias and must not be passed to production approve/publish/reconcile.
600
- 7. **Approval:**
601
- ```bash
602
- APPROVAL_JSON=$("${CLI[@]}" approve --plan "$PLAN_PATH" \
603
- --digest "$PLAN_DIGEST" --actor "$ACTOR" --json)
604
- printf '%s\n' "$APPROVAL_JSON" | jq .
605
- APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
606
- ```
607
- Returns the immutable production authority as `approvalPath` at
608
- `<project>/.release-skill/approvals/<planDigest>/<approvalDigest>.json`.
609
- `latestApprovalPath` points to `.release-skill/approval-record.json`, which is
610
- only a mutable convenience alias and must not be passed to production
611
- publish/reconcile. Approval expires after 24
612
- hours; a PARTIAL recovery may create a new approval for the same plan while
613
- preserving every earlier approval byte-for-byte. Use the returned
614
- `approvalPath` and `expiresAt` as authority. `--actor` is only an
615
- unauthenticated local audit label: release-skill performs no identity
616
- authentication and provides no digital signature, so it cannot prove that
617
- a real human actually approved — it only records the identity the operator
618
- self-reports.
619
- 8. **Publish (remote writes start here):**
620
- ```bash
621
- PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
622
- --plan "$PLAN_PATH" --approval "$APPROVAL_PATH" \
623
- --confirm-production "$PLAN_DIGEST" --json)
624
- printf '%s\n' "$PUBLISH_JSON" | jq .
625
- PUBLISH_RUN_PATH=$(printf '%s\n' "$PUBLISH_JSON" | jq -r '.runPath')
626
- ```
627
- Save the returned `runPath`. `PUBLISHED` is **not** the terminal state.
628
- 9. **Verify (consumer install check):**
629
- ```bash
630
- "${CLI[@]}" verify --root "$PROJECT" \
631
- --plan "$PLAN_PATH" --run "$PUBLISH_RUN_PATH" \
632
- --acknowledge-gate-side-effects --json
633
- ```
634
- Omit the acknowledgement only when the plan has neither consumer gates nor
635
- a configured npm `smokeBin`. Both execute installed project code without an
636
- OS or network sandbox.
637
-
638
- The handoff example requires `jq`. Without it, copy the same four returned JSON
639
- fields exactly; do not pass the angle-bracket labels shown elsewhere as shell
640
- syntax.
641
-
642
- Production prepare seals a standalone Git commit/tree for every public snapshot
643
- and creates a fixed tarball for every npm unit. Publish globally preflights all
644
- actions, then executes and observes public branch, tag, npm, GitHub Release,
645
- and configured Claude/Codex marketplace installation checkpoints. Kimi Code has
646
- no scriptable install API, so its checkpoint **fails closed** and `publish`
647
- lands in `PARTIAL` after the automated writes, emitting a version-pinned manual
648
- install requirement. The operator then launches Kimi Code with the requirement's
649
- isolated `KIMI_CODE_HOME`, runs the pinned `/plugins install <release-tag URL>`,
650
- writes a trusted attestation (binding the frozen **plan** digest and the
651
- snapshot **payload** digest) into the plan-digest-keyed directory
652
- `.release-skill/kimi-attestations/<planDigest>/<plugin>/`, and re-runs
653
- `reconcile` (→ `PUBLISHED`) and `verify` (→ `VERIFIED`); both read the
654
- attestation from that same stable location. An install into the ordinary
655
- `~/.kimi-code` is not accepted. See `INSTALL.md` for the full procedure and the
656
- attestation JSON fields. `verify`
657
- installs every exact npm `package@version` in an isolated directory; when
658
- `smokeBin` is configured it also runs the CLI and validates output. Only when
659
- all evidence matches does the run reach `VERIFIED`.
660
- Before a real release run `gh auth login`, `gh auth setup-git`, and
661
- `npm login`, and confirm Git HTTPS credentials can access the target repository.
662
- Version branches default to `release/<tag>` and can be configured per unit with
663
- `production.branchTemplate`; any existing remote object stops for human review.
664
-
665
- ### Release-document refresh (optional)
666
-
667
- A release unit can declare `releaseDocuments` so one structured, bilingual
668
- notes source deterministically refreshes the managed README regions and the
669
- current CHANGELOG entry. The core CLI runs entirely offline: it does not use
670
- the network, does not call any large language model, and does not
671
- auto-translate. It only rewrites the declared managed regions, the unique
672
- version marker's machine value, and the current CHANGELOG managed entry;
673
- every byte outside those regions is preserved verbatim. `prepare` only
674
- checks freshness and never writes the working tree.
675
-
676
- ```yaml
677
- # .release-skill/project.yaml (release unit fragment)
678
- releaseUnits:
679
- - id: my-project
680
- source: .
681
- releaseDocuments:
682
- notesSource: release-notes/{version}.yaml
683
- locales: [en, zh-CN]
684
- changelogs:
685
- - path: CHANGELOG.md
686
- locale: en
687
- readmes:
688
- - path: README.md
689
- locale: en
690
- regions: [latest-release]
691
- versionMarkers:
692
- - id: current-version
693
- pattern: '<!-- release-skill:version -->v{version}<!-- /release-skill:version -->'
694
- - path: README.zh-CN.md
695
- locale: zh-CN
696
- regions: [latest-release]
697
- ```
698
-
699
- `notesSource` and every target path are relative to the release unit root.
700
- `versionMarkers[].pattern` must match the README's existing unique version
701
- marker exactly, with `{version}` standing in for the machine value; the
702
- refresh replaces only that value (zero or multiple matches fail closed).
703
-
704
- ```yaml
705
- # release-notes/0.1.6.yaml (structured notes source)
706
- version: 0.1.6
707
- date: 2026-07-21
708
- locales:
709
- en:
710
- summary: Deterministic multilingual release-document refresh.
711
- changes:
712
- added:
713
- - Refresh managed README regions and changelogs from one source.
714
- upgradeNotes: Review and commit refreshed documents before prepare.
715
- zh-CN:
716
- summary: 从同一说明源确定性刷新多语种发布文档。
717
- changes:
718
- added:
719
- - 自动刷新 README 受管区域和 CHANGELOG。
720
- upgradeNotes: prepare 前审阅并提交刷新结果。
721
- ```
722
-
723
- `version` must exactly equal the resolved unit version; every configured
724
- locale appears exactly once with a non-empty `summary` and at least one
725
- change under `security`, `breaking`, `added`, `changed`, `deprecated`,
726
- `removed`, or `fixed`. YAML aliases, duplicate keys, unknown fields, and
727
- locale fallback all fail closed.
728
-
729
- 1. **Read-only drill:**
730
- ```bash
731
- "${CLI[@]}" docs refresh --root "$PROJECT" --unit my-project --json
732
- ```
733
- Prints `status` (`changes` or `clean`), per-file relative `path`,
734
- `locale`, `kind`, old/new digests, the unit `version`, `locales`,
735
- `inputDigest`, and `refreshDigest` — a binding over the protocol
736
- version, the unit, the canonical notes object, the configuration
737
- projection, and the sorted per-file old/new digests. It never binds
738
- time, absolute paths, or display text. `nextCommand.argv` carries the
739
- exact write command.
740
- 2. **Digest-confirmed local write (only after explicit human authorization
741
- of the local release-document write):**
742
- ```bash
743
- "${CLI[@]}" docs refresh --root "$PROJECT" --unit my-project \
744
- --write --confirm-refresh <refreshDigest> \
745
- --ack-local-document-write --json
746
- ```
747
- All three bindings are required; a mismatched digest fails closed with
748
- `RELEASE_DOCS_REFRESH_STALE` and writes nothing. When the candidate is
749
- unchanged the drill reports `clean` and the write performs zero writes.
750
- All targets commit as one transaction; a successful write is followed
751
- by a re-drill that must return `clean`.
752
-
753
- This authorization covers only the declared local document targets. It is
754
- not authorization for hooks, Git commits, pushes, publishes, or installs:
755
- a maintainer must review the refreshed documents, commit them, and rerun
756
- `prepare` — the new bytes change the snapshot, workspace digest, and plan
757
- digest, so an earlier approval cannot authorize the refreshed plan.
758
-
759
- When configured documents drift, `prepare` fails closed with
760
- `RELEASE_DOCS_STALE` before hooks, baseline, snapshot, remote checks, and
761
- plan freeze. Recovery: run the drill, review the shown files/locales/
762
- version/digest, authorize and perform the local write, review and commit
763
- the result, then rerun `prepare`. `RELEASE_DOCS_INVALID` (bad
764
- configuration or notes data), `RELEASE_DOCS_TRANSLATION_MISSING` (a
765
- configured locale absent), and `RELEASE_DOCS_CONFLICT` (unmanaged
766
- same-version content or marker damage) each require fixing the source or
767
- target first; never widen the write scope to resolve them.
768
-
769
- ### Parent workspace with npm + plugin sub-units
770
-
771
- When a monorepo produces both an npm package and a Claude/Codex/Kimi Code
772
- plugin from different directories, define separate release units. Only add a plugin
773
- distribution when the unit actually ships a plugin with manifest, marketplace,
774
- and entry Skill:
775
-
776
- Here `project` is the parent workspace's orchestration container, not a public
777
- release unit. If the workspace root also publishes its own repository or
778
- package, add another release unit with `source: .`.
779
324
  `version.source` is resolved relative to that release unit's `source` directory
780
- (`version.source` 相对于该发布单元的 `source` 目录解析): a unit with
781
- `source: packages/app` therefore writes plain `package.json`, not
782
- `packages/app/package.json`.
325
+ (`version.source` 相对于该发布单元的 `source` 目录解析). A monorepo with
326
+ separate npm and plugin units defines multiple release units:
783
327
 
784
328
  ```yaml
785
329
  apiVersion: release-skill/v1
@@ -787,7 +331,6 @@ kind: ReleaseProject
787
331
  project:
788
332
  name: my-workspace
789
333
  defaultBranch: main
790
-
791
334
  releaseUnits:
792
335
  - id: my-app
793
336
  source: packages/app
@@ -803,28 +346,16 @@ releaseUnits:
803
346
  tag: latest
804
347
  registry: https://registry.npmjs.org
805
348
  publisher: my-npm-username
806
- smokeBin: my-app
807
- smokeArgs: [help, --json]
808
- smokeExpectedJson:
809
- command: help
810
- status: READY
811
349
  publicFiles:
812
- - from: packages/app/README.md
813
- to: README.md
814
- mode: preserve
815
350
  - from: packages/app/package.json
816
351
  to: package.json
817
352
  mode: preserve
818
- - from: packages/app/LICENSE
819
- to: LICENSE
820
- mode: preserve
821
- requiredPublicFiles: [README.md, package.json, LICENSE]
353
+ requiredPublicFiles: [package.json]
822
354
  previousPublicBaseline:
823
355
  mode: none
824
356
  production:
825
357
  branchTemplate: release/{tag}
826
- releaseTitleTemplate: "{unit} {version}"
827
-
358
+ branchStrategy: create-release-branch
828
359
  - id: my-plugin
829
360
  source: packages/plugin
830
361
  publicRepo: owner/my-plugin
@@ -832,166 +363,66 @@ releaseUnits:
832
363
  source: package.json
833
364
  tagTemplate: my-plugin-v{version}
834
365
  distributions:
835
- # Declare plugin consumers only when the unit ships a plugin.
836
- # The CLI smoke is independent; only declare smokeBin when the plugin
837
- # package also exposes a CLI binary.
838
366
  - type: claude-plugin
839
367
  plugin: my-plugin
840
368
  marketplace: my-plugin
841
369
  entrySkill: my-plugin-help
842
- timeoutMs: 300000 # optional; range 30000-900000; default 300000
843
- - type: codex-plugin
844
- plugin: my-plugin
845
- marketplace: my-plugin
846
- entrySkill: my-plugin-help
847
- timeoutMs: 300000 # optional; range 30000-900000; default 300000
848
- - type: kimi-plugin
849
- plugin: my-plugin
850
- entrySkill: my-plugin-help
851
- timeoutMs: 300000 # optional; range 30000-900000; default 300000 (Kimi has no install command; bounds read-only verification)
370
+ marketplaceSourceType: bundled-family
852
371
  publicFiles:
853
- - from: packages/plugin/.claude-plugin/plugin.json
854
- to: .claude-plugin/plugin.json
855
- mode: preserve
856
- - from: packages/plugin/.claude-plugin/marketplace.json
857
- to: .claude-plugin/marketplace.json
858
- mode: preserve
859
- - from: packages/plugin/.codex-plugin/plugin.json
860
- to: .codex-plugin/plugin.json
861
- mode: preserve
862
- - from: packages/plugin/.kimi-plugin/plugin.json
863
- to: .kimi-plugin/plugin.json
864
- mode: preserve
865
- - from: packages/plugin/.agents/plugins/marketplace.json
866
- to: .agents/plugins/marketplace.json
867
- mode: preserve
868
- - from: packages/plugin/skills/my-plugin-help/SKILL.md
869
- to: skills/my-plugin-help/SKILL.md
870
- mode: preserve
871
- - from: packages/plugin/README.md
872
- to: README.md
873
- mode: preserve
874
372
  - from: packages/plugin/package.json
875
373
  to: package.json
876
374
  mode: preserve
877
- - from: packages/plugin/LICENSE
878
- to: LICENSE
879
- mode: preserve
880
- requiredPublicFiles:
881
- - .claude-plugin/plugin.json
882
- - .claude-plugin/marketplace.json
883
- - .codex-plugin/plugin.json
884
- - .kimi-plugin/plugin.json
885
- - .agents/plugins/marketplace.json
886
- - skills/my-plugin-help/SKILL.md
887
- - README.md
888
- - package.json
889
- - LICENSE
375
+ requiredPublicFiles: [package.json]
890
376
  previousPublicBaseline:
891
377
  mode: none
892
378
  production:
893
379
  branchTemplate: release/{tag}
894
- releaseTitleTemplate: "{unit} {version}"
380
+ branchStrategy: create-release-branch
895
381
  ```
896
382
 
897
- Each plugin unit **must** list its Claude/Codex/Kimi Code `plugin.json`, the
898
- Claude/Codex `marketplace.json` files (Kimi Code has no marketplace manifest),
899
- the entry Skill, and all required public files. A CLI smoke (`smokeBin`) is
900
- optional for plugin units and only applies when the published npm package
901
- exposes a CLI binary.
902
-
903
- Plugin distributions may declare `timeoutMs` (range 30,000--900,000 ms; default
904
- 300,000 ms). This sets the subprocess timeout for the marketplace add, plugin
905
- install, and plugin list commands. On real networks these commands can take
906
- 40--105 seconds; the default 300-second timeout avoids false `PARTIAL` failures.
907
- The resolved value is frozen into the plan and approved along with all other
908
- action parameters. Old plans without `timeoutMs` default to 300,000 ms at
909
- execution time for backward compatibility.
910
-
911
- ### PARTIAL recovery and reconcile
912
-
913
- When `publish` succeeds at some checkpoints but fails at others, the run enters
914
- `PARTIAL` status. **Do not restart from scratch and do not delete remote state**
915
- (e.g., do not delete a tag that was already pushed, or unpublish a package).
383
+ Add a gate to the extracted `recommendedAnswers` by editing `verificationGates`
384
+ and binding the same id in `selectedGateIds`:
916
385
 
917
- Instead, use `reconcile` to inspect actual remote state, skip already-consistent
918
- steps, and safely retry incomplete actions:
919
-
920
- ```bash
921
- RECONCILE_JSON=$("${CLI[@]}" reconcile --root "$PROJECT" \
922
- --run "$PUBLISH_RUN_PATH" \
923
- --plan "$PLAN_PATH" \
924
- --approval "$APPROVAL_PATH" \
925
- --confirm-production "$PLAN_DIGEST" --json)
926
- printf '%s\n' "$RECONCILE_JSON" | jq .
927
- RECONCILE_RUN_PATH=$(printf '%s\n' "$RECONCILE_JSON" | jq -r '.runPath')
928
- # Save reconcile's new runPath, then perform the fresh install verification.
929
- "${CLI[@]}" verify --root "$PROJECT" \
930
- --plan "$PLAN_PATH" --run "$RECONCILE_RUN_PATH" \
931
- --acknowledge-gate-side-effects --json
386
+ ```json
387
+ {
388
+ "projectConfig": {
389
+ "apiVersion": "release-skill/v1",
390
+ "kind": "ReleaseProject",
391
+ "project": { "name": "my-project", "defaultBranch": "main" },
392
+ "releaseUnits": [{
393
+ "id": "my-project",
394
+ "source": ".",
395
+ "publicRepo": "owner/my-project",
396
+ "version": { "source": "package.json", "tagTemplate": "v{version}" },
397
+ "distributions": [{
398
+ "type": "npm", "package": "my-project", "access": "public",
399
+ "provenance": false, "tag": "latest",
400
+ "registry": "https://registry.npmjs.org", "publisher": "my-npm-username"
401
+ }],
402
+ "publicFiles": [{ "from": "package.json", "to": "package.json", "mode": "preserve" }],
403
+ "requiredPublicFiles": ["package.json"],
404
+ "previousPublicBaseline": { "mode": "none" },
405
+ "production": { "branchTemplate": "release/{tag}", "branchStrategy": "create-release-branch" }
406
+ }],
407
+ "verificationGates": [{
408
+ "id": "my-project-script-test",
409
+ "phase": "snapshot-verify",
410
+ "scope": { "unit": "my-project" },
411
+ "command": ["node", "-e", "const p=require('./package.json');if(!p.name)process.exit(1)"],
412
+ "cwd": ".",
413
+ "timeoutMs": 30000,
414
+ "envAllowlist": []
415
+ }]
416
+ },
417
+ "selectedGateIds": ["my-project-script-test"]
418
+ }
932
419
  ```
933
420
 
934
- Omit the verify acknowledgement only when the frozen plan has neither consumer
935
- gates nor an npm `smokeBin`. The variables above are the exact values captured
936
- by the main flow; if approval expired during recovery, create a fresh approval
937
- for the same immutable plan and replace `APPROVAL_PATH` before reconcile.
938
-
939
- `reconcile` queries the actual remote state (Git refs, npm version, GitHub
940
- Release, marketplace install), skips any step whose evidence already matches
941
- the frozen plan, and retries only safe and incomplete steps. Remote conflicts
942
- (e.g., an unexpected tag or npm version) require human decision and cannot be
943
- auto-resolved.
944
- Successful reconcile returns `PUBLISHED`, not `VERIFIED`; only the fresh
945
- `verify` run may produce the terminal `VERIFIED` state.
946
-
947
- ## Accepted capabilities
948
-
949
- - validates project configuration and release units;
950
- - reports readiness without changing the project during `assess`;
951
- - copies configured public files into an isolated snapshot;
952
- - discovers first-use candidates read-only and creates a config only once after
953
- exact `setupDigest` confirmation;
954
- - runs human-selected project gates in frozen-snapshot copies and exact
955
- consumer installation roots;
956
- - checks required files, path safety, exact bytes/modes, and obvious leaks;
957
- - records Git/workspace identity and freezes a digest-bound release plan;
958
- - binds approval to the plan digest, expiry, and explicit action allowlist;
959
- - publishes only frozen Git objects and npm tarballs, then checks remote
960
- commit/tree/tag/integrity;
961
- - installs configured Claude/Codex plugins from the frozen Git ref and proves
962
- the entry Skill and payload digest in fresh isolated consumer homes; for Kimi
963
- Code (no scriptable install API) it emits a version-pinned manual install
964
- requirement and proves the entry Skill and payload digest only from a trusted
965
- attestation bound to the frozen plan digest;
966
- - supports an external independent marketplace for Claude/Codex distributions
967
- (`marketplaceRepo`): `prepare --online --production` freezes the external
968
- marketplace HEAD (Codex commit sha / Claude default branch name), validates the
969
- marketplace index entry at that sha, and verifies the installed payload against
970
- the unit's own frozen snapshot whole-tree (`external-marketplace-v1`), while the
971
- install-side CLI list observation fails closed on version drift;
972
- - ships a generated, self-contained CodeBuddy/WorkBuddy adapter
973
- (`adapters/workbuddy/`, manifest `.codebuddy-plugin/plugin.json`, skills
974
- rendered with `${CODEBUDDY_PLUGIN_ROOT}`) alongside the Claude/Codex/Kimi
975
- adapters; because the codebuddy CLI cannot pin a frozen ref there is no
976
- automated marketplace install checkpoint, so it emits a manual install
977
- requirement and proves the entry Skill and payload digest only from a trusted
978
- attestation bound to the frozen plan digest;
979
- - distinguishes `PUBLISHED` (writes completed) from `VERIFIED` (remote and
980
- consumer installation evidence completed);
981
- - stops subsequent checkpoints on failure and writes a separate run record
982
- without mutating the frozen plan or undoing successful remote actions.
983
-
984
- ## Project-specific verification: hooks and gates
985
-
986
- `hooks.docs/build/test/typecheck/lint` run before the snapshot is frozen. Use
987
- them only for work that genuinely needs the parent workspace or generates
988
- source files. They can modify files or access the network, so prepare requires
989
- `--acknowledge-hook-side-effects`.
990
-
991
- Each hook is an object, never a bare command list.
992
- `command` is an executable/argument array, not a shell string
993
- (`command` 是可执行文件/参数数组,不是 shell 字符串). Each hook also declares
994
- `cwd`, `timeoutMs`, and `envAllowlist`:
421
+ ### Hooks and gates
422
+
423
+ `hooks.docs/build/test/typecheck/lint` run before the snapshot is frozen. Each
424
+ hook is an object. `command` is an executable/argument array, not a shell string
425
+ (`command` 是可执行文件/参数数组,不是 shell 字符串):
995
426
 
996
427
  ```yaml
997
428
  hooks:
@@ -1007,118 +438,24 @@ hooks:
1007
438
  envAllowlist: []
1008
439
  ```
1009
440
 
1010
- Hooks still run only after human review of every configured executable,
1011
- argument, working directory, and side effect, and only with
1012
- `prepare --acknowledge-hook-side-effects`.
1013
-
1014
- `verificationGates` are the controlled extension point for release calibration:
1015
-
1016
- ```yaml
1017
- verificationGates:
1018
- - id: package-contract
1019
- phase: snapshot-verify
1020
- scope: { unit: my-project }
1021
- command:
1022
- - node
1023
- - -e
1024
- - "const p=require('./package.json'); if (!p.name) process.exit(1)"
1025
- cwd: .
1026
- timeoutMs: 120000
1027
- envAllowlist: [CI]
1028
- - id: installed-help
1029
- phase: consumer-verify
1030
- scope: { unit: my-project, distribution: npm }
1031
- command: [node, scripts/check-installed-help.mjs]
1032
- cwd: .
1033
- timeoutMs: 30000
1034
- envAllowlist: []
1035
- expectedJson: { status: READY }
1036
- ```
1037
-
1038
- The snapshot example is deliberately self-contained and reads only a mapped
1039
- public file. Any replacement script and every dependency it needs must exist
1040
- in the frozen public snapshot. The consumer script must likewise be present in
1041
- the exact installed distribution; gates cannot borrow tests, development
1042
- dependencies, or `node_modules` from the parent workspace.
1043
-
1044
- `snapshot-verify` runs in a disposable writable copy of the frozen public
1045
- snapshot. `consumer-verify` runs from an exact isolated npm/Claude/Codex/Kimi
1046
- Code install root. Both use executable arrays instead of shell strings; definitions and
1047
- results enter digest-bound evidence, and prepare/verify require
1048
- `--acknowledge-gate-side-effects`. Gates are still project processes without a
1049
- network sandbox, so release-skill cannot promise that they will not write files
1050
- or access the network. Push, tag, default-branch changes, GitHub Releases, and
1051
- npm publish may never be hooks/gates; they remain controlled plan actions.
1052
-
1053
- ## What it does not do yet
1054
-
1055
- <!-- release-skill:capability:unsupported-scope -->
1056
- - no automatic README generation or source-file overwrite;
1057
- - no automatic conflict merge or rollback workflow;
1058
- - no claim that a real production canary has run for marketplace verification;
1059
- - `prepare --online` observes previous public baselines (bound mode) and defers
1060
- remote uniqueness checks to publish global preflight;
1061
- - no overwrite of branches/tags/releases or npm unpublish; create-only refs use
1062
- `--force-with-lease=<ref>:` solely as an atomic compare-and-set assertion that
1063
- the ref is absent, while existing branches use an ordinary non-force push;
1064
- - no automated CodeBuddy/WorkBuddy marketplace install checkpoint — the
1065
- codebuddy CLI cannot pin a frozen ref, so installation is a manual step proven
1066
- by the same attestation closed loop as Kimi Code;
1067
- - no promise of Windows or broad multi-platform native write support;
1068
- - no hidden commit, push, tag, release, or package publication.
1069
-
1070
- ### Write Safety
1071
-
1072
- `setup` is read-only by default and may create a config only once after exact
1073
- digest confirmation. `assess` is read-only unless an explicit report output is requested. `prepare`
1074
- writes local files under `.release-skill/`; it does not write project source
1075
- files or remote services. If hooks are configured, they are arbitrary local
1076
- processes and require `--acknowledge-hook-side-effects`; hooks may have their
1077
- own filesystem or network side effects. Gates are also project processes and
1078
- require `--acknowledge-gate-side-effects`; they may have the same side effects.
1079
- `publish` is the production write entry
1080
- and requires both approval and the current plan digest. Omit hooks and use local
1081
- sandbox targets for the smallest safe rehearsal.
1082
-
1083
- ### If something fails
1084
-
1085
- | Result | What to do |
1086
- |---|---|
1087
- | `NEEDS_INPUT` | Complete setup's repository, tag, channel, baseline, and gate decisions. |
1088
- | `LOCAL_ONLY_DETECTED` | Establish a remote channel or keep only a local configuration design; do not claim production readiness. |
1089
- | `SETUP_DIGEST_MISMATCH` | Facts or answers changed; rerun dry-run, review, and confirm the new digest. |
1090
- | `CONFIG_EXISTS` | Setup never overwrites the existing config; assess it and edit incrementally. |
1091
- | `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. |
1092
- | `CONFIG_INVALID` | Correct `.release-skill/project.yaml`, then rerun `assess`. |
1093
- | `PUBLIC_FILE_MISSING` | Add or correct the configured public file. |
1094
- | `FORBIDDEN_CONTENT_DETECTED` | Remove the leaked/private content, then prepare again. |
1095
- | `SNAPSHOT_FIDELITY_FAILED` | Inspect the source/snapshot path and rerun `prepare`. |
1096
- | `BASELINE_CHANGED` | Keep the human edit, then prepare, review, and approve again. |
1097
- | `GATE_FAILED` during `prepare` | Fix the snapshot gate or frozen public artifact, then run a new `prepare`; the failed plan cannot be approved. |
1098
- | `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. |
1099
- | `PARTIAL` | Do not restart or delete remote state; review the returned `runPath` and run `reconcile` (see above). |
1100
- | `PUBLISHED` | Run `verify --plan <planPath> --run <publishRunPath>`; this is not terminal success. |
1101
- | `VERIFIED` | Remote state, exact npm install, and configured plugin consumer installs all matched the frozen plan. |
441
+ Hooks run only after human review and with
442
+ `prepare --acknowledge-hook-side-effects`. Gates are the controlled extension
443
+ point for release calibration (see `references/02-project-config.md`).
1102
444
 
1103
445
  ## Skills
1104
446
 
1105
447
  - `release-help`: environment check and next-step guidance.
1106
- - `release-setup`: read-only discovery, human calibration, and create-once first-use configuration.
448
+ - `release-setup`: read-only discovery, human calibration, and create-once configuration.
1107
449
  - `release-assess`: read-only release readiness report.
1108
450
  - `release-prepare`: local snapshot and reviewable release plan.
1109
451
  - `release-publish`: approved, digest-confirmed frozen GitHub+npm publishing.
1110
452
  - `release-reconcile`: evidence-based PARTIAL recovery with human intervention on conflicts.
1111
453
  - `release-verify`: post-publish verification; only `VERIFIED` is the happy end.
1112
454
 
1113
- Conflicts still default to human intervention. The npm-installed `release-skill`
1114
- CLI is the supported user entry after v0.1.1 production publication; source
1115
- checkout remains the development/contributor fallback.
1116
-
1117
455
  ## Platform distribution
1118
456
 
1119
457
  One deterministic core engine ships to several targets through build-only adapter
1120
- closures. A release unit declares what reaches users via `distributions`; each
1121
- distribution type maps to a concrete artifact:
458
+ closures. A release unit declares what reaches users via `distributions`:
1122
459
 
1123
460
  | `distributions` type | Physical artifact | Install |
1124
461
  |---|---|---|
@@ -1126,17 +463,29 @@ distribution type maps to a concrete artifact:
1126
463
  | `claude-plugin` | self-contained closure under `adapters/claude/` | automated marketplace checkpoint |
1127
464
  | `codex-plugin` | self-contained closure under `adapters/codex/` | automated marketplace checkpoint |
1128
465
  | `kimi-plugin` | self-contained closure (no scriptable install API) | manual, attestation-bound |
1129
- | `codebuddy-plugin` | generated `adapters/workbuddy/` with `.codebuddy-plugin/plugin.json` (the codebuddy CLI cannot pin a frozen ref) | manual, attestation-bound |
1130
-
1131
- Each adapter closure bundles its own copy of the CLI bundle, skills, and schemas so
1132
- it runs with no external dependency after installation. The Claude/Codex marketplace
1133
- install checkpoints are automated (preflight, execute, observe, verify); the Kimi
1134
- Code checkpoint fails closed and produces a version-pinned manual install
1135
- requirement; the CodeBuddy/WorkBuddy checkpoint likewise fails closed — because
1136
- the codebuddy CLI cannot pin a frozen ref there is no automated install
1137
- checkpoint — and produces a manual install requirement proven by a trusted
466
+ | `codebuddy-plugin` | generated `adapters/workbuddy/` with `.codebuddy-plugin/plugin.json` | manual, attestation-bound |
467
+
468
+ Each adapter closure bundles its own CLI, skills, and schemas for zero external
469
+ dependency after installation. `publish` only publishes frozen Git objects and
470
+ npm tarballs, then checks remote commit/tree/tag integrity. Claude/Codex
471
+ verification is automated; Kimi Code and CodeBuddy/WorkBuddy require a trusted
1138
472
  attestation bound to the frozen plan digest.
1139
473
 
474
+ <!-- release-skill:capability:unsupported-scope -->
475
+ - no automatic README generation or source-file overwrite;
476
+ - no automatic conflict merge or rollback workflow;
477
+ - no claim that a real production canary has run for marketplace verification;
478
+ - `prepare --online` observes previous public baselines (bound mode) and defers
479
+ remote uniqueness checks to publish global preflight;
480
+ - no overwrite of branches/tags/releases or npm unpublish; create-only refs use
481
+ `--force-with-lease=<ref>:` solely as an atomic compare-and-set assertion that
482
+ the ref is absent, while existing branches use an ordinary non-force push;
483
+ - no automated CodeBuddy/WorkBuddy marketplace install checkpoint — the
484
+ codebuddy CLI cannot pin a frozen ref, so installation is a manual step proven
485
+ by the same attestation closed loop as Kimi Code;
486
+ - no promise of Windows or broad multi-platform native write support;
487
+ - no hidden commit, push, tag, release, or package publication.
488
+
1140
489
  ## License
1141
490
 
1142
491
  MIT. See [LICENSE](LICENSE).