| name | entwurf-release |
| description | Operate entwurf SemVer releases through four explicit modes: land, prepare, make, and publish. Use for pre-version exact-SHA CI landing, CHANGELOG and package preparation, static and LIVE gates, prepared-HEAD CI, exact artifact acceptance, tag and GitHub release creation, repair-dist-tag publication, and post-publish registry proof. Each mode is a separate authority boundary. Triggers: release land, prepare-release, make-release, publish release, release cut, prerelease, repair release. |
| user_invocable | true |
entwurf-release
Repository: ~/repos/gh/entwurf.
This skill is the shared release-operation SSOT that replaces the former
.pi/prompts/prepare-release.md and .pi/prompts/make-release.md files.
Claude Code discovers it natively under .claude/skills/; pi discovers the same
file through .pi/settings.json and its "skills": ["../.claude/skills"]
entry.
Invocation
# Claude Code - stable example
/entwurf-release land 0.12.8
/entwurf-release prepare 0.12.8
/entwurf-release make 0.12.8
/entwurf-release publish 0.12.8 /absolute/path/to/candidate.tgz latest
# pi - prerelease/repair example
/skill:entwurf-release land 0.12.8-repair.1
/skill:entwurf-release prepare 0.12.8-repair.1
/skill:entwurf-release make 0.12.8-repair.1
/skill:entwurf-release publish 0.12.8-repair.1 /absolute/path/to/candidate.tgz repair
Natural-language requests map to the same four modes.
land pushes an already reviewed pre-version HEAD and waits for the required
exact-SHA CI jobs. It never edits, versions, tags, or publishes.
prepare edits release records, runs deterministic and LIVE gates, and creates
the release-prep commit. It never pushes, tags, or publishes.
make pushes the prepared HEAD, waits for exact-SHA CI, creates and accepts one
preserved candidate, then tags, stamps, and creates the GitHub release. It
never runs npm publish.
publish publishes only the already accepted preserved candidate under an
explicitly supplied dist-tag and proves the registry-installed result.
The invocation authorizes only the named mode. prepare is not land
authorization. make is not publish authorization. If the mode, version, or a
mode-specific required argument is missing, ask for it and stop.
Shared version contract
Accept a normal SemVer release or prerelease. Reject a leading v.
VERSION="<user argument>"
case "$VERSION" in
"") echo "ABORT: version required (for example 0.12.8 or 0.12.8-repair.0)"; exit 1 ;;
v*) echo "ABORT: drop the leading 'v'"; exit 1 ;;
esac
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "ABORT: version must be SemVer, optionally with a prerelease suffix"
exit 1
fi
Valid examples: 0.12.8, 0.12.8-repair, and 0.12.8-repair.0.
npm version remains the final package-version validator.
Exact-SHA CI oracle
The shared helper is the only release instruction that classifies the required
GitHub Actions run:
CI_ORACLE=".claude/skills/entwurf-release/scripts/verify-exact-ci.sh"
bash "$CI_ORACLE" "$(git rev-parse HEAD)" wait
It selects only a push-triggered ci.yml run whose headSha is the supplied
full SHA, waits when requested, and requires these exact jobs to conclude
success:
check
install-surface
artifact-consumer
Use mode verify instead of wait when a prior run must already be complete.
Never replace this with a branch-level green badge or the newest unrelated run.
LAND
land exists for a narrower release contract that requires a pre-version
implementation HEAD to receive its own CI run before release metadata changes.
It is not required for every ordinary release.
A land <version> invocation is explicit authorization for one ordinary push of
main. It is not authorization for version edits, tags, GitHub releases, or npm
publication.
L0. Establish the landing boundary
- Read
AGENTS.md, NEXT.md, and VERIFY.md completely.
- Read the
commit skill because its push and post-push stamp rules remain in
force.
- Confirm that the current narrower contract actually requires a pre-version
CI checkpoint. If it does not, stop and direct the operator to
prepare.
- Inspect and require a clean, non-diverged
main:
git status --short --branch
git diff-index --quiet HEAD --
test "$(git branch --show-current)" = main
git fetch origin main
read -r BEHIND AHEAD < <(git rev-list --left-right --count origin/main...HEAD)
test "$BEHIND" = 0
test "$AHEAD" -gt 0
Confirm from the diff and log that HEAD contains only the reviewed landing set.
Do not absorb an unrelated local commit into a release push.
For a required pre-version checkpoint, the package must not already equal the
target version:
test "$(node -p "require('./package.json').version")" != "$VERSION"
L1. Prove pushability and push main
SHA="$(git rev-parse HEAD)"
git push --dry-run origin main
git push origin main
test "$(git ls-remote origin refs/heads/main | cut -f1)" = "$SHA"
Never force and never bypass verification.
L2. Stamp the pushed commit
Stamp only after the push succeeds, following the commit skill. If the stamp
fails, report the exact error and stop; do not write the agenda target by hand.
L3. Require exact-SHA CI
CI_ORACLE=".claude/skills/entwurf-release/scripts/verify-exact-ci.sh"
bash "$CI_ORACLE" "$SHA" wait
The oracle requires four axes on one run at that exact SHA: the workflow
conclusion, the three job conclusions, and the check job's
./run.sh check-gate-qualification step concluding success. A skipped body is
not evidence, so it fails the same way a red one does.
If the oracle names the qualification step -- absent or skipped -- the body did
not run at this SHA. Force it, wait, and re-run the oracle. gh workflow run
takes a branch, never a SHA, so the branch must still point at $SHA when it is
dispatched; the oracle re-checks headSha afterwards and refuses a run whose
branch moved.
gh workflow run --ref runs the REMOTE branch head, so check that head, not the
local one. And do not sleep: until the dispatch run is registered, the oracle's
newest-run rule would pick the already-finished push run and ABORT on the same
axis. Wait for the run to appear, then hand it to the oracle.
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
git fetch origin "$BRANCH"
test "$(git rev-parse FETCH_HEAD)" = "$SHA"
count_dispatch_runs() {
gh run list --workflow ci.yml --event workflow_dispatch --commit "$SHA" \
--limit 20 --json databaseId --jq length
}
BEFORE="$(count_dispatch_runs)"
gh workflow run ci.yml --ref "$BRANCH" -f qualify=true
for _ in $(seq 1 12); do
sleep 5
if [ "$(count_dispatch_runs)" -gt "$BEFORE" ]; then break; fi
done
test "$(count_dispatch_runs)" -gt "$BEFORE" || {
echo "ABORT: no dispatch run appeared for $SHA within 60s" >&2
exit 1
}
bash "$CI_ORACLE" "$SHA" wait
Report the SHA, workflow URL, the run event, all three job conclusions, and the
qualification-step conclusion. End with:
Landing checkpoint complete. Ready for /entwurf-release prepare <version>.
PREPARE
prepare edits, verifies, and commits. It does not push, tag, create a GitHub
release, stamp a release, notify, or publish.
P0. Establish the release boundary
- Read
AGENTS.md, NEXT.md, and VERIFY.md completely. A narrower current
release contract in those files overrides a generic instruction in this
skill.
- Read the
commit skill before creating any commit.
- Inspect the current state:
git status --short --branch
git diff --check
Do not mix pre-existing implementation or review fixes into the release-prep
commit. If a completed, clearly scoped fix is present and GLG has approved its
commit, close it as a separate atomic commit first. If scope is ambiguous or
unrelated, stop and ask.
If the current contract requires a pre-version landing checkpoint, verify it
before making any edit:
git fetch origin main
SHA="$(git rev-parse HEAD)"
test "$(git rev-parse origin/main)" = "$SHA"
bash .claude/skills/entwurf-release/scripts/verify-exact-ci.sh "$SHA" verify
A missing landing run is not a prepare failure to work around. Stop with the
exact next command: /entwurf-release land <version>.
Prepare may commit only release-prep files such as CHANGELOG.md,
package.json, pnpm-lock.yaml, and an evidence handoff explicitly required by
the current release contract.
Forbidden in prepare mode:
- pushes
- tags
- GitHub releases
- release agenda stamps
- notifications
- npm publication
- final candidate creation for a contract that requires post-commit CI first
P1. Audit changes since the last release
LAST_TAG=$(git tag --sort=-version:refname | head -1)
printf 'baseline=%s\n' "$LAST_TAG"
git log "${LAST_TAG}..HEAD" --oneline
Compare the commit range and closed NEXT.md work with the existing
CHANGELOG.md ## Unreleased section. Record only verified changes. Do not
rewrite historical release sections.
P2. Promote the release section
Use the current KST date and transform the top of the changelog into this shape:
## Unreleased
## <VERSION> - YYYY-MM-DD
Keep a fresh empty ## Unreleased section above the promoted release body.
Preserve the repository's existing heading punctuation if it uses an em dash.
Release-gate paths and summaries may live in the release section or in an
explicit durable operator handoff, following the repository's current
convention. The paths and actual MUST/BEHAVIOR counts must not be lost.
P3. Update package version and lockfile
npm version "$VERSION" --no-git-tag-version
pnpm install --lockfile-only
Inspect the resulting diff. Do not manufacture a lockfile change when the
resolver produced none.
P4. Run the deterministic floor
pnpm run check:full
The FULL tier is the candidate floor; the everyday pnpm check core alone is
never release evidence. Do not summarize the aggregate as a fixed number of
gates. The current package.json check:* scripts are the SSOT. If any check
fails, stop at that axis, fix it, and rerun the complete aggregate.
The check chains carry only the qualification HEAD (check-gate-manifests) and
deliberately exclude the mutant-executing body check-gate-qualification: the
LIVE release gate (P5) runs the body as its own MUST step, and the exact-SHA CI
check job (M2) requires it on the release commit. Do not add a manual
qualification rerun here.
Pushing the release tag creates no run, so the exact-SHA oracle never reads a
tag run. It reads whichever run at that commit carries the body: the branch push
when the push touched the qualification surface, otherwise the dispatch run the
recovery above creates.
P5. Run the LIVE release gate from fresh scratch
Use the tmux skill because this command is long-running. Preserve the scratch
directory and complete log.
SCRATCH=$(mktemp -d "/tmp/entwurf-release-gate-${VERSION}.XXXXXX")
LOG="$SCRATCH/release-gate.log"
set -o pipefail
LIVE=1 ./run.sh release-gate "$SCRATCH" --cut 2>&1 | tee "$LOG"
The release gate has two tiers:
MUST is release-blocking and owns the exit code. FAIL must be zero, and
--cut enforces the other half: any MUST SKIP makes the run red, so a
release run can no longer hide required LIVE work behind a skip. Each step is
invoked and reports its own outcome (exit 0 PASS / 97 SKIP / else FAIL); a
[entwurf:skip] line names the prerequisite that was missing. Drop --cut
only for an unattended diagnostic pass, which is not acceptance.
BEHAVIOR is advisory model-in-loop evidence. A failure does not block the
release, but its PASS/FAIL counts and artifact path must be recorded.
Do not expect a fixed PASS count. Record actual output. Do not waive a MUST
failure without diagnosing and explicitly classifying the failing axis. Do not
hide a BEHAVIOR failure.
Create no commit while the gate is running. check-gate-qualification pins the origin HEAD it
started on and aborts with origin HEAD changed during qualification if that moves, discarding a
~50-minute run. Editing the working tree is harmless; moving HEAD is not. Queue any commit request
that arrives mid-run (including one from GLG) until the gate reports its verdict.
P6. Apply release-specific pre-commit acceptance
NEXT.md and VERIFY.md may require gates beyond pnpm run check:full and the LIVE
release gate. Apply every requirement that belongs before the release-prep
commit.
For #51-style repair releases, do not create the final candidate here. The exact
candidate must be created from the clean prepared HEAD only after that exact SHA
has been pushed and all three CI jobs are green. make owns that post-CI
acceptance. A checkout pack-once result is not release-artifact evidence.
Never claim an unrun gate as passed.
P7. Create the release-prep commit
Stage only release-prep files. Never pull preceding implementation changes into
this commit.
git status --short
git diff --check
git diff --cached --check
git commit -m "chore(release): prepare v${VERSION}"
Do not bypass hooks. A commit request does not authorize a push.
P8. Final preparation check
test "$(node -p "require('./package.json').version")" = "$VERSION"
grep -qE "^## ${VERSION}([[:space:]]|$)" CHANGELOG.md
git diff-index --quiet HEAD --
Report:
- prepared version and commit SHA
pnpm run check:full result
- release-gate scratch, log, and artifact paths
- actual
MUST: PASS=n FAIL=0 SKIP=n (includes the check-gate-qualification MUST step)
- actual
BEHAVIOR: PASS=n FAIL=n
- release-specific work deliberately deferred to
make
- clean-tree result
End with both harness forms:
Ready for /entwurf-release make <version>.
Ready for /skill:entwurf-release make <version>.
MAKE
make operates only on an already prepared clean HEAD. It pushes that HEAD,
requires exact-SHA CI, creates and accepts the final candidate, then tags,
stamps, creates the GitHub release, and notifies. It does not edit release files
or run npm publish.
A make <version> invocation is explicit authorization for ordinary main and
tag pushes plus the GitHub release sequence. Read both the commit and
tag-release skills before proceeding so their push, safety, and stamp rules
remain active.
M0. Preflight
Abort on the first failed check.
Clean tree, version, changelog, gate evidence, and tag absence
git diff-index --quiet HEAD --
test "$(git branch --show-current)" = main
test -z "$(git tag -l "v${VERSION}")"
test -z "$(git ls-remote --tags origin "v${VERSION}")"
grep -qE "^## ${VERSION}([[:space:]]|$)" CHANGELOG.md
test "$(node -p "require('./package.json').version")" = "$VERSION"
pnpm run check:full
pnpm run check:full here is the full deterministic floor without
qualification; do not duplicate a manual check-gate-qualification run in
preflight. The exact-SHA
CI check job (M2) is the axis that requires it.
Confirm that a fresh release-gate scratch/log path and its actual MUST/BEHAVIOR
summary are present in the changelog or durable operator handoff. Do not proceed
when MUST has a failure, evidence is missing, or NEXT.md names an unresolved
pre-release blocker.
GitHub identity and target
gh auth status
REMOTE=$(git remote get-url origin)
EXPECTED_REPO=$(printf '%s\n' "$REMOTE" | sed -E 's#^git@github(-[a-z]+)?\.com:##; s#^https://github.com/##; s#\.git$##')