l-make-release
Release @takazudo/zudo-sg and create-zudo-sg end-to-end from ONE invocation. By default it judges both packages and releases every one that has something to ship, engine first and initializer second. ...
/l-make-release
End-to-end release orchestrator for @takazudo/zudo-sg, the styleguide engine package at packages/styleguide, and the create-zudo-sg initializer at packages/create-zudo-sg. The engine path bumps the version, writes an English changelog page, regenerates packages/, runs the local quality gate, commits + pushes to main, waits for CI, then pushes the v<version> tag — which triggers . (build + verify + pnpm
publish) — watches that run to success, and creates the GitHub Release. The initializer path is documented in Releasing create-zudo-sg. One invocation takes every package in the selected proposal all the way to npm.
Selecting the packages
The package set is decided before Step 1 and applies to the entire invocation:
Default (no package named, or both named) — combined run. Judge both packages in Steps 1–2 and put every package that has something to ship into one proposal. Release the engine first, then the initializer.
engine(or a request naming only@takazudo/zudo-sg) restricts the run to the styleguide engine and itsvX.Y.Ztag workflow.create-zudo-sgorinitializerrestricts the run topackages/create-zudo-sgand itscreate-zudo-sg-vX.Y.Ztag workflow.
In a combined run, each package is judged on its own:
Engine ships when the range since its last
v*tag has engine-relevant commits (Step 2). If it has none, leave it out of the proposal rather than proposing an empty patch.Initializer ships when any of these holds: no
create-zudo-sg-v*tag exists yet (first release); commits since its last tag touchpackages/create-zudo-sg/**, the template sync/gate scripts, or its publish workflow — not counting a previous engine release's generated template range rewrite; or this run's engine bump is a minor or major, because the template's^0.Y.Zrange cannot resolve the new engine line and fresh scaffolds would stay on the old one. An engine patch alone does not need an initializer release: the existing caret range already resolves it.If neither package has anything to ship, say so and stop.
Order is fixed: engine, then initializer. The initializer's --published-engine gate installs the template's engine range from npm, and the engine release rewrites that range, so the new engine version must be live before the initializer's gates can pass. The two releases stay separate commits, tags, changelogs, and publish workflows — a combined run sequences them, it never merges them.
If the engine release does not reach npm, do not start the initializer release. Report the engine failure via Failure Recovery and stop; the initializer can be released later with /.
Each selected package's manifest is the only version source this skill bumps for it. The root package.json is never a release version source.
What the engine package is
Single npm package:
@takazudo/zudo-sg, atpackages/styleguide/. Version source of truth ispackages/'sstyleguide/ package. json versionfield. The rootpackage.jsonstaysprivate: trueand is never touched by this skill — the styleguide host site,apps/demo,doc/, andpackages/demo-uiall have their own lifecycle and are not part of this release.Stable only (ADR
docs/decision 12). There is noadr/ styleguide- engine. md nextdist-tag, no prerelease suffix, nonext/stableargument for this package — unlike the zdtl/zudo-doc release skills this one is modeled on.v*.*.*always publishes to npmlatest.Pre-1.0 version judgement is Scheme B, not standard SemVer conventional commits: a breaking-change commit bumps minor (not major — the major stays at
0through the whole0.xline), everything else bumps patch. There is nofeat: -> minorrule here.
Invocation & autonomy
This skill is model-invocable: a rough natural-language request like "bump version", "cut a release", or "release zudo-sg" may trigger it. By default, the invocation itself authorizes the full end-to-end flow — version and changelog changes, pushes, tags, npm publication, and GitHub Releases — for every package selected by Steps 1–2. Print the proposal for visibility, then continue without asking "go?", "publish?", or an equivalent confirmation.
--confirm is the opt-in interactive mode. When present, Steps 1–2 remain read-only, print the combined proposal, and wait for explicit confirmation before Step 3. Once confirmed, the rest of the flow is autonomous; do not ask again before tags, publishing, or the initializer half.
A build/test/pack failure (Step 4), CI failure (Step 6), or publish-workflow failure (Step 8) must be resolved before advancing — see Failure Recovery.
Cancel mode. / (or "cancel/abort the release") does NOT bump anything — it jumps straight to Cancelling a release to undo a not-yet-published release.
How publishing works (read before changing anything)
/l-make-release -> analyze + print proposal -> bump + changelog + commit + push main -> CI green
│
skill pushes: git push origin v<version>
│
.github/workflows/publish-zudo-sg.yml fires
│
builds + verifies + `pnpm publish` -> npm
│
skill watches the run, then creates the GitHub Release The publish workflow triggers on a pushed v*.*.* tag — NOT on a GitHub Release. The skill creates the GitHub Release after the publish run succeeds, or after recovery verifies that the exact version reached npm despite a failed run. The irreversible step is the tag push; invoking the skill without --confirm, or confirming its proposal with --confirm, is what authorizes it.
Boundaries
The skill never runs
npm publish/pnpm publishdirectly — the selected package's publish workflow does that after the tag push.npm cannot re-publish a version. If the publish workflow fails after the tag is pushed (Step 8), check the registry before deciding whether the unchanged run can be retried — see Failure Recovery. Never move a pushed release tag to another commit.
The engine path never mutates the root
package.json,apps/demo,doc/, orpackages/demo-ui; the initializer path additionally mutates onlypackages/, its changelog, and its release-generated artifacts.create- zudo- sg/ package. json
Step 1: Preconditions
Verify ALL of the following. If any check fails, stop with a clear message.
Current branch is
main(git branch --show-current).ghCLI is authenticated (gh auth status).Local
mainis up to date withorigin/main(git fetch origin && git status -sb). If behind, pull first.Fetch tags so the changelog base is correct:
git fetch --tags origin.Latest CI on
mainis green — this repo has no single "test" workflow that runs on push tomain; check every workflow run at the currentmainHEAD:gh run list --branch main --limit 5 --json name,status,conclusion,headShaAll runs at the current head SHA must show
conclusion: success(most commonlyProduction Deploy,main-deploy.yml). If anything is stillin_progressor non-success, stop and report it rather than releasing against unverifiedmain.Do not pause for an unverifiable credential confirmation. The publish workflow owns npm authentication through its configured Trusted Publisher or repository secret. If authentication fails, follow Failure Recovery; do not pre-emptively ask the operator to inspect npm account settings.
Select first release, normal bump, or resume candidate
A previous run may have committed the version bump without pushing the tag (e.g. CI on the bump was still running when the prior run ended). Check for any v* tag before treating a missing current-version tag as a resume. The seeded 0.1.0 was introduced with the package; that commit is not a release. Every path requires a clean tree: if git status --porcelain is non-empty, stop and ask the user to commit, stash, or discard before re-running.
CUR=$(node -p "require('./packages/styleguide/package.json').version")
BASE_TAG=$(git tag -l 'v*' --sort=-v:refname | head -1)
if [ -z "$BASE_TAG" ]; then
RELEASE_MODE=first
elif git show-ref --verify --quiet "refs/tags/v$CUR"; then
RELEASE_MODE=bump
else
RELEASE_MODE=resume-candidate
fifirst— nov*tag exists at all: always go to Step 2, even if a previous attempt prepared a release commit. Analyze the package history, propose the first version, and follow the normal autonomous/--confirmbehavior. Never offer to tag the package-introduction commit or skip the changelog, quality gate, or release commit. Non-release tags such as_attachmentsdo not change this decision.bump—v$CURexists: proceed to Step 2 for a normal bump. If this is recovery from a failed publish, use Failure Recovery first; a tag alone does not prove the version reached npm.resume-candidate— at least onev*tag exists, butv$CURis absent: identify the commit that introduced the current version (not necessarilyHEAD):BUMP_SHA=$(git log -1 --format=%H -S"\"version\": \"$CUR\"" -- packages/styleguide/package.json) git show --stat "$BUMP_SHA" git show "$BUMP_SHA:packages/styleguide/package.json" git show "$BUMP_SHA:doc/src/content/docs/changelog/zudo-sg/$CUR.mdx" git show "$BUMP_SHA:packages/styleguide/CHANGELOG.md"Resume only if the commit is identifiable as this skill's completed preparation: subject
chore(release): @takazudo/zudo-sg v<current>, the matching manifest version, a dated changelog page with nounreleasedplaceholder, and the generated CHANGELOG entry. If any evidence is missing, take Step 2's proposal path instead. Also check the exact version withnpm view "@takazudo/; a published version or an inconclusive query goes to Failure Recovery, never a new tag push.zudo- sg@$CUR" version - - registry= https: / / registry. npmjs. org/ Present a resume proposal naming the version and
$BUMP_SHA. In default mode continue automatically; with--confirm, wait unless that exact resume is already authorized in this session. Re-run Step 4 on that tree, ensure the selected commit is onorigin/main, then continue at Step 6, tagging only the verified SHA. If$BUMP_SHAis notHEAD, surface the later commits and let the user choose the original release tree or a fresh proposal including them. Do not validateHEADand then tag a different, unverified tree.
Step 2: Determine the next version and propose — THE GATE
Find the changelog base
git tag -l 'v*' --sort=-v:refname | head -1A
v*tag exists -> base = that tag. Normal case.No
v*tag exists at all -> this is the first release. There is no bootstrap tag to fall back to (unlike the zdtl model, which assumes an initialv1.0.0was created by hand — this repo does not: the strayv0.1.0tag was deliberately deleted, see #179). Instead, the base is the commit that introduced the package itself:BASE_SHA=$(git log --diff-filter=A --format=%H --follow -- packages/styleguide/package.json | tail -1)This finds the first commit that ever added
packages/— i.e. "all of history since the engine was born" — with no dependency on knowing which PR merged the epic. Include that introduction commit itself in the first-release analysis (styleguide/ package. json git show "$BASE_SHA");BASE_SHA..HEADotherwise omits it.Verify that the registry agrees this is a first release:
npm view @takazudo/zudo-sg versions --json --registry=https://registry.npmjs.org/A confirmed package-not-found
E404is expected. If versions already exist, stop and reconcile the missing tag history using Failure Recovery; do not publish a new "first" version. Authentication, network, or registry errors are inconclusive, not proof of absence.
Analyze commits since the base
git log "${BASE_TAG:-$BASE_SHA}..HEAD" --oneline # human-readable proposal list (subject + hash)
git log "${BASE_TAG:-$BASE_SHA}..HEAD" # full form, read commit bodies too — a
# `BREAKING CHANGE:` footer can appear under a
# plain `feat:`/`fix:` subject with no `!`Categorize each commit (read both subject and body — do not rely on --oneline alone):
Breaking Changes:
!before the colon (feat!:,fix(scope)!:) OR aBREAKING CHANGEfooter in the body.Features:
feat:prefix (includingfeat(scope):).Bug Fixes:
fix:prefix (includingfix(scope):).Other Changes: everything else (
docs:,chore:,refactor:,ci:,test:,style:,perf:, …).
Only commits that touch packages/styleguide/**, ., or other engine-relevant paths are release-worthy for this package's own changelog entry — but the version-level judgement below still runs over the full commit range (a breaking commit anywhere in that range is a signal worth surfacing even if narrowly scoped; use judgement when a change is obviously unrelated to the engine, e.g. a pure apps/demo or doc/ content commit, and exclude it from both the level judgement and the changelog draft).
Determine the version (Scheme B, pre-1.0)
Every argument sets which component bumps; there is no channel argument at all — everything lands on latest.
First release (RELEASE_MODE=first) — normally propose 0.1.0, the seeded unreleased entry, even though package.json already says 0.1.0. Finalizing that version is a release; do not manufacture 0.1.1 just because the initial manifest already has a version. Still analyze all the engine history and show the proposal. A Scheme B breaking-change judgement may propose 0.2.0, but state the concrete reason in the proposal. An explicit major / minor / patch argument overrides this default as below.
If a prior attempt already prepared another version with no v* tags, stay on this cold-start path, explain whether that unpublished version is being retained or increased. With --confirm, obtain fresh confirmation. Never silently lower it back to 0.1.0 or skip Steps 2–5.
Later releases, no argument — judge the level from the categorization above:
any Breaking Change -> minor bump (
0.Y.Z->0.(Y+1).0) — the major stays at0, per Scheme B; a breaking0.xchange does NOT jump to1.0.0.else (any
feat:,fix:, or onlyOther Changes) -> patch bump (0.Y.Z->0.Y.(Z+1)).No-qualifying-commits case: if the range has nothing above
docs:/chore:/refactor:/ci:/test:/etc. (nofeat:,fix:, or breaking commit at all), the "else -> patch" rule still applies, but flag it explicitly at the proposal: "No feat:/fix:/breaking commits found since<base>— proceeding with the Scheme B patch bump." With--confirm, ask the user to confirm that patch.
major / minor / patch argument — force that exact bump on the current version's triple, ignoring the commit judgement:
major:{X+1}.0.0— the explicit escape hatch for a deliberate jump to1.0.0(or any other major), since Scheme B's auto-judgement never proposes one on its own.minor:X.{Y+1}.0patch:X.Y.{Z+1}
There is no next, stable, or any other argument — this package has no prerelease channel to opt into.
Validation
The computed version must be strictly greater than the current version (packages/'s version) except on the cold-start first-release path, where retaining the unpublished current version is allowed. A lower version is never allowed. Show an error with both versions if validation fails.
Before proposing any version, check it with npm view "@takazudo/. Only a confirmed not-found response permits publishing it. If it resolves, the version is already live: use Failure Recovery and do not re-publish or move a tag. If the query fails for any other reason, stop until its result can be established.
Present the proposal
Proposed release: @takazudo/zudo-sg <current> -> <new> (<first release|breaking|feature|fix|patch>, Scheme B)
Breaking Changes:
- description (hash)
Features:
- description (hash)
Bug Fixes:
- description (hash)
Other Changes:
- description (hash)In a combined run, present both packages in this one proposal: the engine block above, then a second block in the same shape headed Proposed release:
create-zudo-sg <current> -> <new> (judged per Releasing create-zudo-sg), then the line Order:
engine first, then create-zudo-sg against the published engine. If one package has nothing to ship, state that in one line instead of its block. Run the registry not-found check for each proposed version.
Only show sections with entries. In default mode the proposal is for visibility: proceed directly to Step 3. With --confirm, wait for explicit user confirmation; that confirmation authorizes the full flow through npm publication and the GitHub Releases.
For the default bootstrap, say Proposed first release:. An unchanged version number does not skip the proposal analysis.
Step 3: Bump + changelog
3a. Bump the version
Update version in packages/ to the selected new version (no v prefix). For the default first release, retain 0.1.0 and continue with the changelog and checks. The root package.json is NOT touched.
After changing the engine version, regenerate the committed initializer template and keep that generated change in the same release commit:
pnpm sync:create-template
pnpm check:create-templateThe sync writes the template dependency as the caret range ^<engine version>. That is a range, not an exact pin: a later compatible engine patch may resolve from npm without another initializer release. Do not hand-edit it to remove the caret. If the sync changes more than the expected generated template files, stop and inspect the diff before continuing.
3b. Write the English changelog page
Create doc/. On the very first real release, finalize the seeded doc/ placeholder in place (its Released: unreleased line and the sentence "Finalized by / when the first version actually ships" both get replaced). If the confirmed first version is different, rename that seed to <version>.mdx, update its title and description, and retain sidebar_position: 1; do not leave a fictional unreleased 0.1.0 entry behind. Stage both sides of the rename in Step 5. On a restarted first attempt, edit the prepared page for that version instead of duplicating it.
Frontmatter (match the existing file's shape):
---
title: <version>
description: <one-line summary of the release>
sidebar_position: <computed — see below>
pagination_next: null
---
Released: <YYYY-MM-DD>
<one or two sentence intro>
### Features ← omit section if empty
- entry
### Bug Fixes ← omit section if empty
- entrysidebar_position: standard ascending order (rootCLAUDE.md: "Sidebar order is driven bysidebar_position") — read every existing file underdoc/and use one past the current highest value for a new release page (the first release keeps the seed'ssrc/ content/ docs/ changelog/ zudo- sg/ 1, the second release is2, and so on). Note this is independent ofpackages/'s own ordering — that file is generated by sorting thestyleguide/ CHANGELOG. md titlefields as semver, newest first, regardless ofsidebar_position(doc/-> thesrc/ content/ docs/ changelog/ zudo- sg/ *. mdx changelogbuild plugin), so gettingsidebar_positionslightly wrong would only affect the doc site's sidebar, not the generated CHANGELOG.Avoid bare
<Uppercase...>generics in prose — the MDX sanitizer strips them even inside inline code (e.g. write "a Preact component", not<Component>, unless it's inside a real fenced code block).Prose only needs Features / Bug Fixes sections that actually contain engine-relevant entries from Step 2's categorization; drop
Breaking Changes/Other Changesheadings if nothing landed there for this package specifically.
3c. Regenerate packages/styleguide/CHANGELOG.md
No dedicated pnpm gen:changelog script exists in this repo — the changelog is a postBuild side effect of the doc site's build (doc/'s changelogs entry, sourceDir: -> outputFile:):
pnpm build:docConfirm packages/ now contains the new version's entry before proceeding.
Step 4: Local quality gate
pnpm b4pushThis runs the full local gate (format, lint:tokens, codegen drift, typecheck, unit tests, build, demo build, link check, HTML validate, Playwright smoke, and the doc site — see scripts/). If anything fails, stop and tell the user. Do not commit. This is the last halt point before anything reaches the remote.
Also run the release-specific artifact gate directly, since b4push does not include it:
pnpm --filter @takazudo/zudo-sg check
pnpm verify:styleguide-install
bash scripts/check-pack.shStep 5: Commit + push
git add packages/styleguide/package.json \
packages/styleguide/CHANGELOG.md \
doc/src/content/docs/changelog/zudo-sg/<version>.mdx \
packages/create-zudo-sg/templates/default
git commit -m "chore(release): @takazudo/zudo-sg v<version>"
git push origin main
BUMP_SHA=$(git rev-parse HEAD)Step 6: Wait for CI on the bump commit
gh run list --branch main --limit 5 --json name,status,conclusion,headShaPoll until every run at headSha == $BUMP_SHA reports conclusion: success. If CI fails, fix, commit, push, and re-watch — refresh BUMP_SHA to the commit whose CI actually passed before tagging:
BUMP_SHA=$(git rev-parse HEAD) # only after CI on THIS commit is greenDo not advance to the tag push until CI on the bump commit is green.
Step 7: Push the tag (triggers the publish)
git tag "v<version>" "$BUMP_SHA"
git push origin "v<version>"This fires .. Do NOT ask "push the tag now?" — the invocation already authorized this (or --confirm already did).
Step 8: Watch the publish workflow
Match the run by its head commit, not headBranch (often empty for tag events):
PUBLISH_RUN=""
for i in $(seq 1 12); do
PUBLISH_RUN=$(gh run list --workflow publish-zudo-sg.yml --limit 15 \
--json databaseId,headSha \
-q "[.[] | select(.headSha==\"$BUMP_SHA\")][0].databaseId")
[ -n "$PUBLISH_RUN" ] && break
sleep 5
done
if [ -z "$PUBLISH_RUN" ]; then
echo "ERROR: could not find the publish-zudo-sg.yml run for v<version> (commit $BUMP_SHA)." >&2
exit 1
fi
gh run watch "$PUBLISH_RUN" --exit-statusIf it fails, first run the exact-version registry check in Failure Recovery. The job may have failed after npm accepted the version. Continue to Step 9 only after publication is confirmed; otherwise inspect the logs and follow the applicable recovery path.
Step 9: Create the GitHub Release
The publish succeeded, or Failure Recovery confirmed the exact version is live. Keep the existing release tag; if it is missing or disagrees with the published commit, stop and reconcile that history rather than re-tagging. Check gh release view "v<version>" first and reuse an existing Release. Otherwise extract the changelog body (everything after the frontmatter) as release notes:
awk 'f; /^---$/{c++; if(c==2) f=1}' doc/src/content/docs/changelog/zudo-sg/<version>.mdx > /tmp/zudo-sg-release-notes.md
gh release create "v<version>" --verify-tag --title "@takazudo/zudo-sg <version>" \
--notes-file /tmp/zudo-sg-release-notes.mdNo --prerelease flag ever applies — this package has no prerelease channel.
Step 10: Verify + report, then continue or STOP
npm view "@takazudo/zudo-sg@<version>" version
npm dist-tag ls @takazudo/zudo-sg<version> should show under latest. There is no next dist-tag to check for staleness — this package never publishes one. Print a final report (published version, npm package URL https:, the publish workflow run URL, the GitHub Release URL).
After the first successful publish, recommend configuring Trusted Publishing (OIDC) for Takazudo/zudo-sg and publish-zudo-sg.yml in the package's npm settings, allowing direct publishing for this workflow. It already has id-token: write and --provenance; the npm-side trust still has to be configured and verified before retiring NPM_TOKEN. Follow the migration notes in packages/. This is a recommendation, not an automatic account-setting change.
If the selected proposal also lists the initializer, continue straight into Releasing create-zudo-sg without a new prompt — starting from its local gates, since its release was already authorized — and fold both packages into one final report. Otherwise STOP.
Releasing create-zudo-sg
This path runs as the second half of a combined run (after the engine is live on npm), or on its own when the request names create-zudo-sg or the initializer. It is independent of the engine's v*.*.* release stream:
The version source of truth is
packages/. Bump that package only; do not bump the root manifest orcreate- zudo- sg/ package. json packages/.styleguide/ package. json The release changelog is
packages/. Add the selected version's entry there before the release commit. On the first release, the seededcreate- zudo- sg/ CHANGELOG. md 0.1.0may be retained and finalized rather than automatically becoming0.1.1; still print the proposal and create the changelog entry.This package is stable-only. Use a clean
X.Y.Zand the exact tagcreate-zudo-sg-vX.Y.Z; there is no prerelease ornextchannel. The proposal considers initializer tags (create-zudo-sg-v*), not engine tags (v*).
For Steps 1–2, apply the same clean-tree, registry, Scheme B, and autonomous default/--confirm rules as the engine path, but substitute the initializer's package and tag namespace throughout. Judge the release against commits that touch packages/create-zudo-sg/**, the template sync/gate, or its publish workflow. If no create-zudo-sg-v* tag exists, this is the initializer's first release: normally propose the seeded 0.1.0 and finalize its changelog entry; do not use an existing engine v* tag as the initializer's base. For a later release, use the latest create-zudo-sg-v* tag and the same breaking-change minor / otherwise patch judgement unless the user explicitly chooses a bump.
The template's engine dependency must remain the caret range ^X.Y.Z, not an exact pin. A create-zudo-sg release does not replace that range with the initializer's version. Before committing, run the package check and both initializer gates:
pnpm --filter create-zudo-sg check
pnpm check:create-template
pnpm verify:create-zudo-sg --published-enginecheck:create-template proves the committed template matches the fixture and that its shipped engine dependency is the intended caret range. The --published-engine verifier packs the initializer, scaffolds an unmodified template, installs that range from the npm registry, and builds the foreign host. Do not replace it with the normal verifier's local engine tarball for a release gate; if the registry cannot resolve the published engine, stop and resolve that release dependency first.
After the proposal is printed (and confirmed only when --confirm is active) and the local gates pass, commit the package manifest and changelog, push main, and wait for CI on that commit. Push only the verified commit as the package tag:
git tag "create-zudo-sg-v<version>" "$BUMP_SHA"
git push origin "create-zudo-sg-v<version>"That tag triggers .. Watch the run by workflow and the tagged commit, then wait for success before creating or reusing the matching GitHub Release:
gh run list --workflow publish-create-zudo-sg.yml --limit 15 \
--json databaseId,headSha
gh run watch <publish-run-id> --exit-statusThe workflow is deliberately guarded: a manual dispatch must target an exact create-zudo-sg-vX.Y.Z tag, and it validates that tag against packages/ before spending time on the build. If a publish run fails after the tag is pushed, check the exact version first:
npm view "create-zudo-sg@<version>" version --registry=https://registry.npmjs.org/If npm returns that version, treat it as published: never re-tag, delete the tag, or re-run the publish job. If npm confirms the version is absent and the failure is transient or credential-only, correct the cause and retry the unchanged tag through the guarded manual dispatch:
gh workflow run publish-create-zudo-sg.yml --ref "create-zudo-sg-v<version>"The workflow's dispatch guard must reject branch refs; do not bypass it. A code fix requires a new initializer version through the proposal process, never moving an existing release tag. Registry/network errors that do not establish absence are inconclusive, so stop and recheck rather than retrying blindly.
After the first successful create-zudo-sg publish, perform the one-time follow-up from #739: remove the documentation note that the initializer is “available from the first release.” Keep that docs chore separate from this release workflow and do not repeat it on later releases.
Cancelling a release
Use this for / or a mid-release problem. The tag push (Step 7) is the irreversible boundary. For an initializer release, substitute the create-zudo-sg-v<version> tag and initializer package/changelog files in the cancellation steps below; never cancel it by touching an engine v* tag.
The tag has NOT been pushed yet (before Step 7)
Delete a local, unpushed tag if one was minted by mistake:
git tag -d v<version>.Decide whether to undo the bump commit:
git rev-list --count <BUMP_SHA>..HEAD0(bump is still HEAD) -> revert it (one commit reverts the version bump,CHANGELOG.md, and the changelog MDX page together):git revert --no-edit <BUMP_SHA> git push origin main>0(buried under later commits) -> leave it. The stale version number is harmless; the next release simply supersedes it.
The tag HAS been pushed (Step 7 done)
Treat the version as live. Do NOT delete the remote tag and do NOT attempt to re-publish a live version. If the publish failed, check the registry and follow Failure Recovery. If it succeeded and needs retracting, that's a manual npm unpublish / npm deprecate decision for the user, outside this skill.
Failure Recovery
First, for any failure after tagging, check whether npm accepted the version:
npm view "@takazudo/zudo-sg@<version>" version --registry=https://registry.npmjs.org/Resolves to
<version>— the version is live, even if the workflow is red. Never re-tag, delete its tag, re-publish, or re-run its publish job. Keep the published commit and continue at Step 9 to finish or reuse the GitHub Release, then verify/report in Step 10. A missing or mismatched tag requires reconciliation before creating the Release.Confirmed not found (
E404, including no matching version) — inspectgh run view "$PUBLISH_RUN" --log-failedand use the unpublished recovery paths below. Repeat the registry check immediately before any retry.Authentication/network/registry error or ambiguous output — publication status is unknown. Stop and recheck when the registry is reachable; never infer "unpublished" from a failed command or a failed Actions job.
Build/test/pack failure (Step 4) — stop and report. Do not commit. Fix and re-run. Nothing reached the remote.
CI fails on the bump commit (Step 6) — fix, commit, push, re-watch. Do not push the tag until CI is green.
Wrong version proposed — caught at the Step 2 gate. If already committed but the tag was NOT pushed, use Cancelling a release to revert and re-run.
Publish workflow fails and the version is confirmed absent (Step 8):
Transient (registry hiccup, runner eviction) — retry the unchanged workflow at the same tag:
gh run rerun "$PUBLISH_RUN", then return to Step 8. Retry only while the version is still confirmed absent.Needs a code fix — leave the pushed tag on its original commit. Fix the code on
main, then re-run/for a new version through the proposal process. Never delete/recreate a release tag to point it at different content.l- make- release OTP /
EOTP/ 2FA or token-permission error — check the granular token's expiry, publish rights covering@takazudo, and Bypass 2FA setting at npmjs.com (or the configured Trusted Publisher). Correct the credential and updateNPM_TOKENif needed; this alone does not need a code change or a moved tag. Recheck npm, then retry the unchanged run only if the version remains absent.
See packages/ for the same recovery paths without the skill's step numbering — useful when recovering by hand.