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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +4 -2
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +41 -0
- package/CONTRIBUTING.md +27 -0
- package/INSTALL.md +95 -139
- package/INSTALL.zh-CN.md +70 -121
- package/README.md +265 -916
- package/README.zh-CN.md +222 -537
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/claude/schemas/release-plan.schema.json +137 -0
- package/adapters/claude/schemas/release-project.schema.json +93 -0
- package/adapters/claude/schemas/release-run.schema.json +70 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/codex/schemas/release-plan.schema.json +137 -0
- package/adapters/codex/schemas/release-project.schema.json +93 -0
- package/adapters/codex/schemas/release-run.schema.json +70 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/kimi/schemas/release-plan.schema.json +137 -0
- package/adapters/kimi/schemas/release-project.schema.json +93 -0
- package/adapters/kimi/schemas/release-run.schema.json +70 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/workbuddy/schemas/release-plan.schema.json +137 -0
- package/adapters/workbuddy/schemas/release-project.schema.json +93 -0
- package/adapters/workbuddy/schemas/release-run.schema.json +70 -2
- package/bin/release-skill.bundle.mjs +18710 -16694
- package/package.json +1 -1
- package/schemas/release-plan.schema.json +137 -0
- package/schemas/release-project.schema.json +93 -0
- package/schemas/release-run.schema.json +70 -2
- package/src/adapters/plugin-marketplace.mjs +1602 -605
- package/src/commands/prepare.mjs +441 -32
- package/src/commands/publish.mjs +107 -75
- package/src/commands/reconcile.mjs +92 -327
- package/src/commands/setup.mjs +148 -20
- package/src/commands/verify.mjs +315 -25
- package/src/core/baseline.mjs +21 -1
- package/src/core/checkpoints.mjs +50 -7
- package/src/core/config.mjs +15 -0
- package/src/core/errors.mjs +2 -0
- package/src/core/installation-contract.mjs +341 -0
- package/src/core/plan.mjs +307 -6
- package/src/platforms/codebuddy.mjs +191 -238
- package/src/platforms/codex.mjs +369 -0
- package/src/platforms/kimi.mjs +164 -119
- package/src/platforms/registry.mjs +180 -4
- 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.
|
|
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.
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
17
|
+
**0.2.4** (2026-07-28)
|
|
15
18
|
|
|
16
|
-
v0.2.
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
30
|
-
-
|
|
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.
|
|
35
|
-
> published status before the
|
|
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.
|
|
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
|
-
##
|
|
72
|
+
## Table of contents
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
100
|
+
**Plugin (Claude Code / CodeBuddy / WorkBuddy / Codex):**
|
|
160
101
|
|
|
161
|
-
|
|
162
|
-
release-skill
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
120
|
+
### Main workflow
|
|
208
121
|
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
--
|
|
259
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
243
|
+
## Release workflow
|
|
279
244
|
|
|
280
|
-
|
|
281
|
-
|
|
245
|
+
release-skill models the release lifecycle as a strict state machine
|
|
246
|
+
(normative definition: `references/01-state-machine.md`):
|
|
282
247
|
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
320
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
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
|
-
|
|
394
|
-
|
|
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
|
-
|
|
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
|
|
310
|
+
mode: none
|
|
424
311
|
distributions:
|
|
425
312
|
- type: npm
|
|
426
313
|
package: my-project
|
|
427
|
-
access: public
|
|
428
|
-
provenance: false
|
|
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` 目录解析)
|
|
781
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
380
|
+
branchStrategy: create-release-branch
|
|
895
381
|
```
|
|
896
382
|
|
|
897
|
-
|
|
898
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
"
|
|
930
|
-
|
|
931
|
-
|
|
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
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
|
1011
|
-
|
|
1012
|
-
`
|
|
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
|
|
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
|
|
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`
|
|
1130
|
-
|
|
1131
|
-
Each adapter closure bundles its own
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
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).
|