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