release-skill
v0.9.22
Published
Safe preparation and frozen GitHub/npm production publishing with full happy end verification
Maintainers
Readme
release-skill
简体中文 · Installation: English / 简体中文
Release preparation for Claude Code, CodeBuddy, WorkBuddy, Codex, and Kimi Code, with human-edited files kept intact.
release-skill helps a maintainer answer three questions: what will be released,
which checks still fail, and which exact bytes will reach users. release-skill
does not regenerate or rewrite project source files. prepare copies each
configured public file into an isolated snapshot and verifies the copied bytes —
it freezes the reviewed artifacts first and publishes those same artifacts later.
Setup surfaces only the deterministic compactSummary review view; the full
report stays in a temporary session directory.
0.9.22 (2026-09-17)
0.9.22 is a local source candidate that fixes two failures found during the real-host rollout of 0.9.21. CodeBuddy and WorkBuddy keep the same frozen Hub identity and installed-payload checks. Foundation dependencies remain pinned to 0.21.0. This note is not evidence of publication, real-host acceptance, consumer installation verification, or independent acceptance.
Security
- Hub publication remains a compare-and-swap operation. A partial Hub publication is never rolled back or force-pushed automatically.
- A Hub marketplace reported as github is accepted only on the Hub-backed CodeBuddy/WorkBuddy path. The updater still verifies the checkout origin, branch, HEAD, host-specific index, installed revision, and complete frozen payload.
- WorkBuddy reuses the existing strict residual_process_group envelope only when the child exited successfully and Foundation reports the exact cleanup evidence. Any field mismatch fails closed before later writes.
Changed
- Skill Family Hub remains the central marketplace, and its release entry continues to use the existing GitHub Git Data API after release verification.
Fixed
- CodeBuddy 2.148.0 can now update an existing Hub plugin when its official marketplace list reports type github instead of type git.
- WorkBuddy can complete the same existing-plugin update when its embedded CodeBuddy CLI exits successfully but leaves a process group that Foundation has already terminated and verified.
Upgrade Notes
Upgrade from 0.9.21 before running local host finishing for CodeBuddy or WorkBuddy. No configuration migration is required. Remove the old release-skill standalone marketplace; use the ifoohoo/skill-family-hub repository and Hub-qualified plugin id release-skill@skill-family-hub. Reload or restart each updated host before claiming that 0.9.22 is loaded.
Current boundary: v0.9.22 is the current source candidate. This README records intended scope and verification boundaries; it is not evidence of publication, consumer-installation verification, or independent acceptance. Release availability must be established from the corresponding release records and post-publish verification results. v0.4.1 was an earlier published milestone; v0.2.2 previously held published status before the platform verification convergence fix was added. v0.1.1 completed a real production release to GitHub and npm — the first production-verified milestone — followed by exact npm installation and Claude/Codex consumer installation verification from the frozen Git ref; "current release" and "first production-verified milestone" are two distinct facts and must not be conflated. The same workflow also has a local production-equivalent protocol suite using the real release-skill CLI and frozen artifacts, local bare Git remotes, and protocol fakes for
gh,npm, Claude, and Codex. The suite does not provide OS-level network isolation, and it does not prove that another project's credentials, permissions, rate limits, or eventual-consistency behavior will match this release. Treat each project's first production run as a monitored canary.prepare --onlineobserves bound previous-public baselines and fails closed on drift; remote uniqueness checks run during publish global preflight.
Production path verified since the v0.1.1 milestone; v0.9.22 is the current source candidate. Its README does not establish publication, consumer-installation verification, or independent acceptance. The npm-installed CLI is the supported user entry. Source checkout is the development/contributor fallback.
Start here:
- npm install:
npm install -g release-skill→release-skill help- source checkout:
node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs" help
0.8.0 scope and verification boundaries
For prepare --workflow config, public-byte comparison selects the branch:
- Branch A (
no-publish-needed) has no external actions and nosourceAuthority, even with a configured public source receipt. It ends without approve/publish/verify. - Branch B (
publish-needed) must freezeplan.sourceAuthorityin production. Docs production has the same requirement; publish/reconcile/verify retain frozen-source and remote consistency checks.
Marketplace delegation to the target workspace is unchanged.
Old production plans missing sourceAuthority are rejected before external writes.
Plans with no external writes must be prepared again and receive approval for the
new digest; do not patch old plans or migrate approvals. Existing PARTIAL
checkpoints remain intact and use matching-version recovery. Evidence v1 stays
read-only; v2 uses a closed top level with phase extensions in details.
Summaries and recovery suggestions are diagnostic, never publication authority.
The current 0.9.22 candidate includes the narrow R-05 Hook cache v2 consumer
path, the public postverify path, and the stable isolated install-tree record
path (A2/A3). It also adds the explicit-input verify-records command and a
self-contained Qoder projection, first-class Cursor packaging, and the
Foundation-backed host verification bridge. Skill Family Hub remains the single
marketplace source. The candidate
also makes the release-prepare Skill organize existing assessment, refresh,
focused-check, and prepare entry points without adding another execution
engine. prepare --help and prepare -h now return before configuration,
locks, runs, or hooks can observe the request. The release-finish Skill can
also report the source branch and continue into a configured setup entry after
the selected host has loaded the released plugin. This candidate still excludes
R-02 safe full-tree inventory. Marketplace installation and live invocation
remain host-level post-release checks; Cursor is distributed through its
supported local-plugin directory rather than a Hub marketplace entry.
Foundation dependencies are pinned to the three released 0.21.0 packages.
For Kimi/CodeBuddy plans that still use the bundled-family distribution form,
verify calls the released
runPluginVerification entry with the complete frozen payload and records a
minimal install-only observation receipt. Kimi maps to kimi-code; CodeBuddy
maps to the compatible workbuddy host. observed and payloadMatches are
mechanism facts, not remote publication or release-domain VERIFIED facts.
Real marketplace installation and host invocation remain post-release host
operations.
This scope summary is not a remote publication record or a consumer upgrade instruction.
Distribute maturity v1.0 (W2 closure): postPublish distribution is available as a separate
distributecommand for mirror and marketplace-index actions. Afterpublishreaches PUBLISHED status, runship distributeor manually invokerelease-skill distribute --plan <path> --approval <path> --json. The distribute action implements fail-closed semantics: NO_CHANGE on empty diff (no commit/tag/push), REMOTE_CONFLICT on tag move attempts (never force-push), AUTH_MISSING when external writes are not authorized. Supported modes:--dry-run(local commit+tag only),--json(structured output),--help(command reference). Tag commitment guarantees version consistency — distributed plugin.json.version must match input tag. See docs/design/2026-08-17-postpublish-distribution.md for detailed architecture.
postPublish hooks v2:
releaseUnit.postPublishaccepts a declarativehooksarray alongside the existingtargets. Each hook is either a named preset or a custom command hook; custom command hooks follow the same rules as prepare hooks — executable/argument arrays, relative cwd, timeout, and an environment allowlist, with shell strings rejected — and, like all configured hooks and gates, they run as unsandboxed processes. Every declaration is normalized into the frozen plan, so a hook change changes the plan digest and invalidates existing approvals. A hook withrequiresApproval: true(the default for public-write presets; projects may tighten this, never loosen it) needs its own checkpoint-level approval before execution: mint one withrelease-skill approve --plan <plan> --hook <hookId> --actor <name>, then consume it withdistribute --hook-approval <record>orship --hook-approval <record>(24-hour expiry, bound to the plan digest and the hook id). Git credentials come only from the host credential helper / OS keychain; release-skill never reads, stores, or prints them.
postPublish presets: six built-in presets ship with the bundle and are enumerated by
release-skill distribute --list-presets:git-mirrorandmarketplace-index-render(declared intargetsform), plusproposal-inbox,marketplace-registry-entry,docs-refresh, andnotify-handoff(declared inhooksform).proposal-inboxdelivers a machine-readable update proposal through a git-push or local-file transport and records the delivery result. The receiver applies, renders, and publicly synchronizes the proposal under the receiver's own runbook and governance; delivery does not prove those receiver-side steps. Without a target it degrades tonotify-handoffbehavior instead of failing.notify-handoffis the zero-write floor: it only renders a deterministic manual sync checklist.
postVerify stage: hooks declared with
phase: postVerifyrun only after the main run reaches VERIFIED;shiproutes them into an independent postVerify run whose hook context carries the verification evidence (verifyEvidence, present in this phase only). A failed postVerify run stays PARTIAL and can be reconciled, but it never rolls the main run back from VERIFIED — the failure is recorded prominently in evidence, never silently.
Safe defaults: the recommended path is
help → assess → prepare --offline → human review.helpandassessare read-only. The release-skill-owned part ofprepare --offlinewrites only under.release-skill/and does not call remote publish adapters, but configured hooks and gates are unsandboxed processes: they may write outside the project, access credentials, make network calls, or publish. Treatprepareas local-only only when those processes are absent or separately audited and explicitly acknowledged. Production publishing usesship --target-version <ver> → ship --approve --actor <name>. Frozen-plan approval is the only normal release-level human gate. Here, “only human gate” means the only normal release-level approval. A postPublish hook whose effectiverequiresApprovalis true still needs a separate checkpoint approval bound to the plan digest and hook id; that approval expires after at most 24 hours. For bundled-family Kimi/CodeBuddy releases, Foundation first observes the complete frozen payload in a fresh local installation. Real marketplace installation and host invocation remain non-blocking manual follow-up tasks.
Table of contents
Quick start
A release unit is a release object with independently configured version, public files, and distribution targets.
Selecting a release scope
Multi-unit projects can repeat --unit <id> before the plan is frozen. Omitting
the option keeps the existing full-scope behavior:
release-skill prepare --root "$PROJECT" --offline \
--unit runtime --unit plugin --json
release-skill ship --root "$PROJECT" --target-version 1.2.3 \
--unit runtime --unit plugin --jsonThe selected IDs are normalized to project configuration order. Unit-scoped version, documentation, snapshot, distribution, and verification-gate work runs only for that scope. Full configuration validation, generated-artifact freshness checks, and top-level hooks still cover the whole project.
An explicit scope that includes any unit named by
publicSourceAuthorityReceipt must include its coordinator and every subject.
release-skill reports the missing units and stops before hooks or plan writes; it
does not expand the scope automatically. A successful explicit selection reports
releaseScope.selectedUnitIds, releaseScope.deferredUnitIds, and the exact next
prepare command for the deferred units.
After freeze, plan.units is the only release-scope authority. Approval covers
every action in that plan. publish, reconcile, verify, and distribute do
not accept --unit; a partial external write remains PARTIAL and resumes
forward within the same frozen plan. A resumed ship state may omit --unit and
reuse its saved request, but cannot change that request or add it to a legacy
full-scope state.
Install
- Node.js 22+, Git 2.30+, a target Git repository with at least one commit.
npm (recommended):
npm install -g release-skill
release-skill helpOr run directly without installing:
npx release-skill helpPlugin (Claude Code / CodeBuddy / WorkBuddy / Codex):
Claude Code, CodeBuddy, WorkBuddy, and Codex install from the central Skill Family Hub marketplace:
/plugin marketplace add ifoohoo/skill-family-hub
/plugin install release-skill@skill-family-hubPrerequisite: GitHub access. The
owner/reposhorthand makes Claude Code clone via SSH. If you do not use SSH, pass the full HTTPS URL —/plugin marketplace add https://github.com/ifoohoo/skill-family-hub— or setCLAUDE_CODE_PLUGIN_PREFER_HTTPS=1.
Kimi Code: release-skill currently invokes no scriptable install API for Kimi Code. It uses the version-pinned interactive TUI path described in INSTALL.md.
Qoder: the package ships .qoder-plugin/plugin.json and the self-contained
adapters/qoder/ projection. A user-scope marketplace install is available only
after the central Hub's root marketplace.json has accepted the release-skill
Qoder entry; the generated adapter alone does not prove Hub consumption. See
INSTALL.md for the bounded install and
post-release update contract.
Cursor: adapters/cursor/ is a complete Local plugin. After release verification,
release-finish can install or atomically upgrade its frozen tree on macOS while
Cursor is closed. It requires an explicit --cursor-plugins-root and preserves
the displaced version outside local/. Restart and real Skill invocation remain
separate checks. See the Cursor instructions.
See INSTALL.md for CodeBuddy, Codex, Kimi Code, Qoder, and Cursor commands.
Read-only governance diagnosis
Use the existing Skills and choose only the check the request needs:
- adoption status:
setup --assess-adoption --root <path> --json; - local configuration and release-readiness gaps:
assess --root <path> --offline --json; - explicit historical plan, approval, and run consistency:
verify-records, using the full input form in the next section.
These entry points run release-skill's read-only analyzers. Governance diagnosis does not run target Skills, hooks, builds, or release actions, and it does not treat prepare, verify, or release-finish as static governance checks. When a maintainer asks to set up or publish instead, continue through the existing product Skill with the authorization and safety contract for that action.
Offline historical record verification
Use the public command below when a consumer already has an explicit plan, approval, target run, and every predecessor run referenced by that target:
release-skill verify-records --plan <path> --approval <path> --target-run <path> --source-run <path>... --unit <id> --target-version <version> --jsonThe command reads only those paths and emits one JSON object. CONSISTENT
exits 0, CONTRADICTED exits 1, and INSUFFICIENT exits 2. It does not scan
.release-skill, follow paths embedded in a record, contact a remote, write a
file, or advance release state. A consistent historical chain does not prove
that its records are globally latest or that remote objects still exist.
Main workflow
For routine releases, use the durable fast path. It persists authoritative
paths and resumes safely. Frozen plan approval is the only normal
release-level approval. ship runs configured hooks and verification gates
automatically. A gated postPublish hook still needs its independent checkpoint
approval; plan approval does not include it. For bundled-family Kimi/CodeBuddy releases, Foundation first
observes the complete frozen payload in a fresh local installation. Real
marketplace installation and host invocation remain non-blocking post-release
manual tasks; the system does not verify their completion.
A release plan is a record for review before approval, linking target versions, frozen artifacts, and proposed external actions; see the human review and approval steps.
release-skill ship --root "$PROJECT" --target-version 1.2.3 --json
release-skill ship --root "$PROJECT" --approve --actor "$ACTOR" --jsonDuring development, release-skill hooks validate runs declared hooks and
writes the same content-bound cache receipts that prepare consumes.
Run these steps in order. Steps 1-3 are read-only. In step 4, release-skill's own prepare pipeline is local-only and never invokes remote publish adapters; configured hooks and gates remain arbitrary unsandboxed processes and can perform local or remote side effects. Step 4 is therefore local-only only when those processes are absent or separately audited and explicitly acknowledged. Steps 5-9 require explicit human gates.
CLI=(release-skill) # or: CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
PROJECT=/absolute/path/to/my-project
ACTOR=your-namehelp — check the environment:
"${CLI[@]}" helpsetup — first-use only (read-only discovery, then create-once config):
SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")" REPORT="$SETUP_SESSION/discovery.json" ANSWERS="$SETUP_SESSION/answers.json" printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT" "${CLI[@]}" setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2If
proposalConflictsis non-empty, stop and let a human correct the conflicting repository or mapping authority. With no conflicts, extractrecommendedAnswersmechanically (never hand-write complete answers):SETUP_SESSION='<session-directory-absolute-path-printed-above>' 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"Confirm the bound
setupDigestonce, then create the config:SETUP_SESSION='<session-directory-absolute-path-printed-above>' PROJECT='<project-absolute-path-printed-above>' ANSWERS="$SETUP_SESSION/answers.json" CREATED_REPORT="$SETUP_SESSION/created.json" POST_REPORT="$SETUP_SESSION/post-setup.json" ASSESS_REPORT="$SETUP_SESSION/assess.json" "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" \ --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT" "${CLI[@]}" setup --root "$PROJECT" --json > "$POST_REPORT" set +e "${CLI[@]}" assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT" ASSESS_EXIT=$? set -e [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT" 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" node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"The write must return
CONFIG_CREATED; the next setup must returnALREADY_CONFIGURED. Existing configuration is never regenerated — make only reviewed incremental edits. For npm units, setup also reports each concretebin/main/module/types/typings/exportstarget and legacynpmRequiredPackagePathsas tracked, untracked, ignored, missing, or non-regular. These are review candidates only: setup never copies them intopublicFilesorrequiredPublicFiles. Discovered scripts areSIDE_EFFECTS_UNPROVEN. Add a project-specific hook or gate only after human review: editprojectConfig.hooks, or editverificationGatesand add the same id toselectedGateIds, then rerun the bound dry-run. Downstream postPublish guidance is also read-only until confirmed:setup --discover-downstreamenumerates downstream candidates (git remotes, neighboring marketplace/docs repositories,artifact-graph.config.yaml, and a foundation profile when present), andsetup --propose-hooksturns those cues into postPublish hook declaration drafts bound by asetupDigest. For an already-configured project,setup --propose-hooks --write --confirm-setup <digest>appends only the target release unit'spostPublish.hooksblock and never regenerates or rewrites any other part of the configuration (create-once is untouched). A foundation profile is one proposal input among several and never auto-applies. See INSTALL.md for the full multi-step flow. Adoption assessment (read-only): for an already-configured project,setup --assess-adoptionreports satisfied items, mandatory gaps, optional suggestions, and not-applicable items without writing anything; a not-yet-configured project returnsNOT_CONFIGUREDwith a pointer to first-time setup. Hook-duration suggestions are derived only from events produced by the current version; the tool never guesses or writescacheInputsfor a project.gateSuggestionscontains only actionable, not-yet-configured drafts; other discoveries remain ingateDiagnosticswithout changing adoption status or the routine summary.assess — read-only readiness:
"${CLI[@]}" assess --root "$PROJECT" --offline --jsonprepare — local snapshot and plan freeze:
"${CLI[@]}" prepare --root "$PROJECT" --offline --jsonHuman review: inspect
planPath,externalActions,targetVersion, andplanDigest.prepare --production — freeze the production plan:
PLAN_JSON=$("${CLI[@]}" prepare --root "$PROJECT" --online --production --json) PLAN_PATH=$(printf '%s\n' "$PLAN_JSON" | jq -r '.planPath') PLAN_DIGEST=$(printf '%s\n' "$PLAN_JSON" | jq -r '.planDigest')approve — human approval (digest auto-read from plan, 24-hour expiry):
APPROVAL_JSON=$("${CLI[@]}" approve --plan "$PLAN_PATH" \ --actor "$ACTOR" --json) APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')Approval binds the plan digest (
planDigest), each release unit's target version, and the approved action set. Changes to these bindings require a new approval.planVersion: 2binds the frozen artifacts, configuration, and actions through the digest. Workspace baseline drift is record-layer audit data and does not by itself invalidate approval; frozen artifacts must still be reverified before publishing.planVersion: 1retains the legacy binding, including the workspace baseline.--actoris an unauthenticated local audit string. It is not identity verification, a signature, or proof that a particular human approved the plan; use an external authenticated approval system when that assurance is required.publish — remote writes start here:
PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \ --plan "$PLAN_PATH" --approval "$APPROVAL_PATH" --json) PUBLISH_RUN_PATH=$(printf '%s\n' "$PUBLISH_JSON" | jq -r '.runPath')A valid approved production plan may enter
publishdirectly;routeis only a workflow suggestion.publishstill requires the plan, approval, frozen digest and artifact identity, remote preflight, and fail-closedPARTIALcheckpoint rules.PUBLISHEDis not the terminal state.verify — consumer install check:
"${CLI[@]}" verify --root "$PROJECT" \ --plan "$PLAN_PATH" --run "$PUBLISH_RUN_PATH" --json
The handoff example requires jq. Without it, copy the returned JSON fields
exactly; do not pass angle-bracket labels as shell syntax.
PARTIAL recovery and reconcile
When publish succeeds at some checkpoints but fails at others, the run enters
PARTIAL status. Do not restart from scratch and do not delete remote state.
RECONCILE_JSON=$("${CLI[@]}" reconcile --root "$PROJECT" \
--run "$PUBLISH_RUN_PATH" \
--plan "$PLAN_PATH" \
--approval "$APPROVAL_PATH" --json)
RECONCILE_RUN_PATH=$(printf '%s\n' "$RECONCILE_JSON" | jq -r '.runPath')
"${CLI[@]}" verify --root "$PROJECT" \
--plan "$PLAN_PATH" --run "$RECONCILE_RUN_PATH" --jsonreconcile queries the actual remote state, skips already-consistent steps,
and retries only safe incomplete actions. Remote conflicts require human
decision. Successful reconcile returns PUBLISHED, not VERIFIED.
Release workflow
release-skill models the release lifecycle as a strict state machine
(normative definition: references/01-state-machine.md):
DISCOVERED -> ASSESSED -> PREPARED -> APPROVED -> PUBLISHING -> PUBLISHED -> VERIFIED
exception states: NEEDS_INPUT / BLOCKED / PARTIALEach CLI command maps to one transition. PUBLISHED is not the terminal
state — only a fresh verify that confirms remote state and consumer installs
match the frozen plan reaches VERIFIED.
Preservation contract: release-skill does not regenerate or rewrite project
source files. prepare copies each configured public file into an isolated
snapshot and verifies the copied bytes. A later prepare reads the current file
again; it never rebuilds from a template. Only files listed in publicFiles are
copied. prepare never refreshes or rewrites human docs — maintainers update
README, INSTALL, and CHANGELOG first, then prepare, review, and approve.
Workspace source authority: production config names the workspace source
repository with project.sourceRepository and its real remote default branch
with project.defaultBranch. Prepare binds the content and Git mode of every
expanded publicFiles.from input plus each version.source; publish compares
that frozen closure with the remote default branch before the first adapter
write. The check accepts merge, squash, and rebase when the bytes still match,
but blocks a lost or reverted README by path. It never merges, switches
branches, pushes, or creates a PR.
Write safety: setup is read-only by default (create-once after digest
confirmation). Release-skill's own prepare pipeline writes only under
.release-skill/ and does not invoke remote publish adapters. Project hooks and
gates are acknowledged processes without an OS sandbox; they may write outside the
project, access credentials, make network calls, or publish. publish is the
release-skill-owned production write entry and requires both approval and the current
plan digest.
Documentation
| Document | Description |
|---|---|
| INSTALL.md / INSTALL.zh-CN.md | Full installation guide: npm, plugin, source checkout, setup flow, branch strategies |
| CHANGELOG.md | Release history |
| CONTRIBUTING.md | How to contribute (includes generated-artifact rules) |
| SECURITY.md | Security policy |
| references/01-state-machine.md | Normative state machine definition |
| references/02-project-config.md | Project configuration schema reference |
| references/05-evidence-and-errors.md | Evidence format and error codes |
| references/06-adapter-contract.md | Adapter and marketplace contract details |
| GitHub Issues | Bug reports and feature requests |
Configuration
A minimal human-authored configuration (see INSTALL.md for the full schema and setup flow):
apiVersion: release-skill/v1
kind: ReleaseProject
project:
name: my-project
defaultBranch: main
sourceRepository: owner/my-workspace
releaseFinish:
sourceBranchCheck: remind
setupSkill: release-setup
releaseUnits:
- id: my-project
source: .
publicRepo: owner/my-project
version:
source: package.json
tagTemplate: v{version}
publicFiles:
- from: README.md
to: README.md
mode: preserve
- from: package.json
to: package.json
mode: preserve
requiredPublicFiles: [README.md, package.json]
previousPublicBaseline:
mode: none
distributions:
- type: npm
package: my-project
access: public
provenance: false
tag: latest
registry: https://registry.npmjs.org
publisher: my-npm-username
production:
branchTemplate: release/{tag}
branchStrategy: create-release-branchreleaseFinish controls only local post-release work. sourceBranchCheck
defaults to remind; set it to skip to suppress the read-only source-branch
reminder. setupSkill is optional and names an already installed and loaded
setup entry such as release-setup or plugin:skill; it is never executed as
a shell command. After host updates, release-finish runs that setup entry
against the current release project root passed through --root; it does not
request a separate project directory. The two settings are independent.
Use release-skill post-release --finish for the complete local finish. It
always reports merge, host update, actual host loading, setup, and source-branch
steps. Exit 0 means every applicable step completed or was explicitly skipped;
exit 2 means caller work remains; exit 1 means a deterministic failure. Pass
agent observations back through an absolute --finish-feedback JSON file bound
to the reported plan digest, config digest, and canonical project root. Feedback
is data only: the command does not execute its strings or treat it as new write
authority. --skip-local-hosts explicitly skips host update, loading, and setup
and cannot be combined with --hosts or --update-local-hosts.
distributions is required but may be an explicit empty array. Use
distributions: [] when release-skill should publish only the unit's GitHub
snapshot, tag, and GitHub Release, with no npm publish or marketplace consumer
action. A postPublish declaration, including hooks whose phase is
postVerify, remains active. This is the intended shape for a plugin that is
added to a central Hub only after the release has reached VERIFIED.
version.source is resolved relative to that release unit's source directory
(version.source 相对于该发布单元的 source 目录解析). A monorepo with
separate npm and plugin units defines multiple release units:
apiVersion: release-skill/v1
kind: ReleaseProject
project:
name: my-workspace
defaultBranch: main
sourceRepository: owner/my-workspace
releaseUnits:
- id: my-app
source: packages/app
publicRepo: owner/my-app
version:
source: package.json
tagTemplate: my-app-v{version}
distributions:
- type: npm
package: my-app
access: public
provenance: false
tag: latest
registry: https://registry.npmjs.org
publisher: my-npm-username
publicFiles:
- from: packages/app/package.json
to: package.json
mode: preserve
requiredPublicFiles: [package.json]
previousPublicBaseline:
mode: none
production:
branchTemplate: release/{tag}
branchStrategy: create-release-branch
- id: my-plugin
source: packages/plugin
publicRepo: owner/my-plugin
version:
source: package.json
tagTemplate: my-plugin-v{version}
distributions:
- type: claude-plugin
plugin: my-plugin
marketplace: my-plugin
entrySkill: my-plugin-help
marketplaceSourceType: bundled-family
publicFiles:
- from: packages/plugin/package.json
to: package.json
mode: preserve
- from: packages/plugin/.claude-plugin/marketplace.json
to: .claude-plugin/marketplace.json
mode: preserve
- from: packages/plugin/.claude-plugin/plugin.json
to: .claude-plugin/plugin.json
mode: preserve
- from: packages/plugin/skills/my-plugin-help/SKILL.md
to: skills/my-plugin-help/SKILL.md
mode: preserve
requiredPublicFiles:
- package.json
- .claude-plugin/marketplace.json
- .claude-plugin/plugin.json
- skills/my-plugin-help/SKILL.md
previousPublicBaseline:
mode: none
production:
branchTemplate: release/{tag}
branchStrategy: create-release-branchThis example places the plugin at the snapshot root. Create the four mapped source files
under packages/plugin before preparing:
- In
.claude-plugin/marketplace.json, name the marketplacemy-pluginand set itsmy-pluginentry'ssourceto"./". - In
.claude-plugin/plugin.json, setnametomy-plugin, matchversiontopackage.json, and setskillsto"./skills/". - The entry file is
skills/my-plugin-help/SKILL.md, with frontmattername: my-plugin-help.
This minimal Skill references no other files; map any required resources explicitly if references are added.
Add a gate to the extracted recommendedAnswers by editing verificationGates
and binding the same id in selectedGateIds:
{
"projectConfig": {
"apiVersion": "release-skill/v1",
"kind": "ReleaseProject",
"project": {
"name": "my-project",
"defaultBranch": "main",
"sourceRepository": "owner/my-workspace"
},
"releaseUnits": [{
"id": "my-project",
"source": ".",
"publicRepo": "owner/my-project",
"version": { "source": "package.json", "tagTemplate": "v{version}" },
"distributions": [{
"type": "npm", "package": "my-project", "access": "public",
"provenance": false, "tag": "latest",
"registry": "https://registry.npmjs.org", "publisher": "my-npm-username"
}],
"publicFiles": [{ "from": "package.json", "to": "package.json", "mode": "preserve" }],
"requiredPublicFiles": ["package.json"],
"previousPublicBaseline": { "mode": "none" },
"production": { "branchTemplate": "release/{tag}", "branchStrategy": "create-release-branch" }
}],
"verificationGates": [{
"id": "my-project-script-test",
"phase": "snapshot-verify",
"scope": { "unit": "my-project" },
"command": ["node", "-e", "const p=require('./package.json');if(!p.name)process.exit(1)"],
"cwd": ".",
"timeoutMs": 30000,
"envAllowlist": []
}]
},
"selectedGateIds": ["my-project-script-test"]
}Hooks and gates
hooks.docs/build/test/typecheck/lint run before the snapshot is frozen. Each
hook is an object. command is an executable/argument array, not a shell string
(command 是可执行文件/参数数组,不是 shell 字符串):
hooks:
build:
command: [node, scripts/build.mjs]
cwd: .
timeoutMs: 120000
envAllowlist: [CI]
test:
command: [node, --test, test/]
cwd: .
timeoutMs: 300000
envAllowlist: []Hooks run when prepare is invoked. Configuring the command and invoking the
workflow authorizes execution without an extra confirmation point. The legacy
--acknowledge-hook-side-effects and --acknowledge-gate-side-effects flags
remain accepted as no-effect compatibility inputs. Gates are the controlled
extension point for release calibration (see references/02-project-config.md).
postPublish hooks
postPublish.hooks declares downstream delivery actions that run after
publish; each entry is a named preset or a custom command hook and follows
the same command rules as above (executable/argument arrays, not shell
strings). Presets receive a read-only projection of the frozen plan; custom
command hooks run in the frozen tag worktree. The materialize hook is
optional: when it is omitted, distribute stages the payload from the
release unit's frozen publicFiles mapping through a Foundation managed
projection — a fresh payload root with full preflight, zero-write refusal,
and complete closure rollback — and live project configuration is never
re-read after the plan is frozen:
postPublish:
materialize:
command: [node, scripts/materialize-payload.mjs]
cwd: .
timeoutMs: 600000
outputMarker: "payload dir: "
commitIdentity:
name: release-bot
email: [email protected]
hooks:
- id: mirror-downstream
preset: git-mirror
config:
target:
remoteUrl: https://gitlab.example.internal/team/my-project.git
branch: main
- id: hub-entry-proposal
preset: proposal-inbox
phase: postVerify
requiresApproval: true
config:
delivery: git-push
target:
remoteUrl: https://github.com/example/hub.git
branch: main
- id: custom-notify
command: [node, scripts/notify-downstream.mjs]
timeoutMs: 300000
envAllowlist: [CI]A hook with requiresApproval: true parks at AWAITING_APPROVAL until a
checkpoint approval is minted and consumed. The approval record is bound to
the plan digest and the hook id, and expires after 24 hours. Frozen plan
approval never includes this checkpoint approval:
release-skill approve --plan "$PLAN_PATH" --hook hub-entry-proposal --actor "$ACTOR" --json
release-skill ship --root "$PROJECT" --hook-approval "$HOOK_APPROVAL_PATH" --jsonFor an already-configured project, setup --discover-downstream and
setup --propose-hooks draft these declarations for human review; the
append-only incremental flow is described in the setup step above and in
INSTALL.md.
Skills
release-help: environment check and next-step guidance.release-setup: read-only discovery, human calibration, create-once configuration, and read-only adoption assessment (setup --assess-adoption).release-assess: read-only release readiness report.release-prepare: organizes existing project prerequisites and focused repairs, then freezes a local snapshot and reviewable release plan through the formal prepare entry.release-publish: approved frozen GitHub+npm publishing; the internal digest is checked automatically.release-reconcile: evidence-based PARTIAL recovery with human intervention on conflicts.release-verify: post-publish verification; onlyVERIFIEDis the happy end.
Platform distribution
One deterministic core engine ships to several targets through build-only adapter
closures. A release unit declares what reaches users via distributions:
| distributions type | Physical artifact | Install |
|---|---|---|
| npm | npm package with CLI entry | npm install -g release-skill |
| claude-plugin | self-contained closure under adapters/claude/ | automated marketplace checkpoint |
| codex-plugin | self-contained closure under adapters/codex/ | automated marketplace checkpoint |
| kimi-plugin | self-contained closure (release-skill invokes no scriptable install API) | non-blocking post-release manual task |
| codebuddy-plugin | generated adapters/workbuddy/ with .codebuddy-plugin/plugin.json | non-blocking post-release manual task |
Qoder is intentionally outside this distributions table: it is a build-only
public projection at adapters/qoder/, with root manifest
.qoder-plugin/plugin.json. It does not add a qoder-plugin plan type or an
automated publish, reconcile, or verify checkpoint.
Each adapter closure bundles its own CLI, skills, and schemas for zero external
dependency after installation. publish only publishes frozen Git objects and
npm tarballs, then checks remote commit/tree/tag integrity. Claude/Codex
verification is automated. Kimi Code and CodeBuddy/WorkBuddy are returned as
manualFollowUps with verifiedBySystem: false; their completion is not a
condition for the automated release to reach VERIFIED.
After VERIFIED, the optional release-finish workflow can update Claude and
Codex from the frozen marketplace identity. When either marketplace needs
rebinding, a read-only remote check must prove the frozen repository, ref, and
commit before that host's first write; a failed check leaves that host unchanged
without stopping other selected hosts. The workflow can also migrate or update
Kimi through one controlled TUI session and verify its real managed payload, or update an existing
bundled-family CodeBuddy/WorkBuddy entry when the frozen tag and mutable branch
both resolve to the frozen commit. For an explicitly selected Qoder host, it can
update only an existing user-scope installation after proving the configured Hub
source and frozen payload; it never adds a marketplace or performs first install.
An UPDATED installation is not treated as loaded: reload or start a new Qoder
session, then complete one read-only release-skill business call before claiming
the new version is active. It requires explicit user confirmation and
does not change release status. Kimi uses one effective configuration root:
explicit kimiHome, then KIMI_CODE_HOME, then ~/.kimi-code. The TUI and
post-operation observation share that root, and the TUI working directory is the
resolved release project root passed through --root. After the user confirms
the frozen plan and explicitly selects Kimi for update, release-finish accepts
only the initial standard Trust this folder? dialog with No, exit selected
and Trust this folder available. It moves to that target, verifies the new
selection, confirms it, and waits for the known command prompt before sending
the plugin command. An unknown row or selection, a failed move, timeout, early
exit, or any folder-trust dialog after the plugin command fails closed. The
frozen plugin identity and Trust and install checks remain unchanged. Missing, standalone, inaccessible, or ambiguous
CodeBuddy/WorkBuddy targets remain manual and receive no host mutation.
WorkBuddy local updates are macOS-only; on other platforms they are skipped as
unsupported. When a plan declares postVerify hooks, release-finish must receive
the completed postVerify run produced by ship, rather than the earlier verify
run. The core prepare, publish, and verify workflow remains cross-platform.
A release unit may also declare postPublish.localHostUpdate for a plugin that
is delivered through a project-selected Hub by a postVerify hook. After that
hook reaches DISTRIBUTED, ship, verify, post-release, and
release-finish display the declared Hub, plugin, and hosts. Version 0.9.22
makes Claude, Codex, Kimi, CodeBuddy, WorkBuddy, and Qoder executable after
explicit host selection and plan-digest confirmation. Claude and Codex require
an existing matching Hub marketplace and verify its host-specific entry before
plugin writes. CodeBuddy and WorkBuddy only upgrade existing installations,
checking the Hub branch again after refresh. Kimi uses the frozen GitHub Release
and controlled terminal interaction, preserving all three proxy variables;
its repository-only Hub file is not tag or commit evidence. Every successful
update verifies the installed payload against the frozen snapshot. Claude can
verify an exact current installation offline; Codex reinstalls when its list
does not expose an installation root. Existing frozen installation actions
and the Qoder and Cursor paths remain supported without duplicate execution.
Production prepare also performs one best-effort retention pass after its new
run evidence exists and before expensive snapshot and hook work. It removes
only superseded complete run lineages and sealed failed prepare directories
that have no valid plan or external-write trace. The current plan, the newest
complete lineage, and every non-terminal, partial, corrupt, ambiguous, or
out-of-bounds lineage remain protected. Retention failures never change the
prepare result.
New codebuddy-plugin project configurations must declare marketplace
explicitly so product families cannot silently inherit another family's Hub.
marketplaceSource, the URL consumers use to add that marketplace, remains
optional. Only already-frozen historical plans that lack marketplace retain
the legacy artifact-skill-set interpretation so reconcile can replay them.
For every npm distribution, prepare statically checks the exact packed
tarball against concrete package.json entry targets. publish and
reconcile repeat the same check on the frozen tarball before any remote
action, and verify repeats it against the exact installed package before
allowing VERIFIED. smokeBin remains optional: when configured it adds an
authorized runtime smoke test; when absent, the static entry-closure check is
still mandatory. Wildcard exports and fallback arrays are deliberately outside
the first minimal semantic boundary, so the static gate fails closed until the
declaration is narrowed to concrete targets.
- not in the current version: full consumer install-tree scanning (R-02) — the current implementation handles only the declared public surface and the stable isolated install-tree record; and a real-host (Kimi/WorkBuddy/Qoder) verification gate — host verification and Qoder activation stay outside release
PASS/VERIFIEDevidence; - no automatic README generation or source-file overwrite;
- no automatic conflict merge or rollback workflow;
- no claim that a real production canary has run for marketplace verification;
prepare --onlineobserves previous public baselines (bound mode) and defers remote uniqueness checks to publish global preflight;- no overwrite of branches/tags/releases or npm unpublish; create-only refs use
--force-with-lease=<ref>:solely as an atomic compare-and-set assertion that the ref is absent, while existing branches use an ordinary non-force push; - no Kimi, CodeBuddy/WorkBuddy, or Qoder marketplace install checkpoint in the release state machine — optional release-finish can drive and re-check Kimi locally, or update an existing CodeBuddy/WorkBuddy entry under strict frozen-identity checks, or update an existing user-scope Qoder entry after equivalent source and payload checks, but those results do not become publication evidence;
- no promise of Windows or broad multi-platform native write support;
- no hidden commit, push, tag, release, or package publication.
