Releasing
How a version reaches npm: the release skill, the tag-triggered publish workflow, the rulesets that gate it, and the drift check.
A release goes from a version bump on main to an npm publish with provenance. Each step is gated, and a person makes every decision.
The release skill
maintainer-skills/release (originally skills/release in v1.0.0) runs the maintainer side. It checks for a clean, up-to-date main, a repository.url, no file: dependencies and a passing npm test. Then it bumps the version with npm version --no-git-tag-version and moves the [Unreleased] changelog entries under the new version. The bump lands through a PR because main is protected. After the merge, the maintainer tags the merged commit and pushes the tag.
The generic /release skill reads its project facts from .claude/release-profile.md, which is tracked in this repository even though the rest of .claude/ is ignored. The profile names the bump sites, the preconditions (including the website generator check), the npm-publish environment and the registry checks that prove a publish landed.
Before releasing: the readiness audit
/release-audit is the question asked before the mechanical cut: is the tree in a shippable state? It is a cross-harness project skill under maintainer-skills/release-audit/ (mirrored into .claude/skills/ and .agents/skills/ for local repository development, never shipped in the plugin), ported from adlc and reshaped for this repository. It gives every shipped surface its own read-only agent (the CLI, the scheduler, the ADLC gates, the PreToolUse policy guard, the MCP server, install and migrate, the vendored adlc, the shipped skills, the sidecar), adds suite-level agents for cross-surface drift, documentation accuracy and supply chain, shards the open issue backlog across sweep agents, and sends a refute agent after every blocker candidate.
The verdict is GO, GO-WITH-RISK or NO-GO, and it is computed by scripts/release-audit-synthesize.mjs, not summarised by a model: a finding is a blocker only when its quoted evidence appears verbatim in the cited file, its three-part blocker test is fully asserted, and the refute pass failed to break it. Anything else is demoted with the reason named, never deleted. A surface that produced no report, a report that examined no files, a red or uncaptured npm test, or an omitted suite log each force NO-GO.
Before any agent runs, scripts/release-audit-collect.mjs runs the mechanical probes this repository already owns: the plugin.json, package.json and lockfile versions agree (D11), the vendored adlc matches the digests pinned in lib/adlc-bridge.mjs, the cached bootstrap tarball hashes to the lockfile integrity, a fresh build leaves dist/ and vendor/ unchanged, the website partials are current, and the previous release is not stranded (the same classifier release-drift.yml runs daily). A probe that cannot run is named in the verdict, never counted as clean.
The publish workflow
Pushing a v* tag is the only trigger for publish.yml. It has no workflow_dispatch, so it always runs the workflow file from the tagged commit. The job:
- Runs in the
npm-publishprotected environment (publish.yml#L27), which needs a reviewer to approve it. - Refuses a tag whose commit is not an ancestor of
origin/main(publish.yml#L41). - Refuses a tag that does not match the
package.jsonversion (publish.yml#L82). - Upgrades to npm 11 for OIDC trusted publishing (publish.yml#L70), then runs
npm ciandnpm test. - Publishes with
npm publish --provenance --access public(publish.yml#L101), using theid-token: writepermission (publish.yml#L16) instead of a long-lived token.
Rulesets
docs/github-rulesets/ keeps the publish gates in version control. The release-tag-ruleset.json file lets only admins create, delete or move refs/tags/v* tags. apply.sh sets up the npm-publish environment and applies that ruleset. Branch protection for main is configured by hand in GitHub and is not in this directory.
Release drift
release-drift.yml runs once a day and on demand. It runs scripts/release-drift.mjs (release-drift.yml#L50) to catch a version that reached main but never reached npm: either a publish waiting for an approval nobody gave, or a bump with no tag pushed. A grace window (90 minutes) keeps a healthy release in progress from being reported.