Release conductor
Gate the open release-please PR before merging: verify CI green, feature PRs landed, no release blockers, changelog reflects intent. One-word approval, then merge. Reports the resulting release.yml run URL. release-please owns the changelog/bump/tag; release.yml owns OIDC npm publish — this skill only gates the merge in between. Triggers on: "ship a release", "cut a release", "merge the release PR", "is the release ready", "verify the release".From its SKILL.md
npx -y skills add kaiohenricunha/dotbabel --skill release-conductorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
10.5 KB, as published. Nobody here has run it
Gate the open release-please PR before merging. release-please drafts the changelog and version bump; release.yml handles OIDC npm publish on tag push. This skill is the human-in-the-loop step between them: verifies the PR is safe to merge, then merges it.
Trigger: when the user says "ship a release", "cut a release", "merge the release PR", "is the release ready", or invokes /release-conductor. Also /release-conductor verify <tag> for post-publish smoke test.
Arguments: $ARGUMENTS — optional subcommand:
- (empty) — full gate + merge flow on the currently open release-please PR.
verify <tag>— post-publish smoke test for a published tag (e.g.verify v2.8.0).
Lifecycle:
feature PRs merged → release-please opens release PR → /release-conductor (gate + merge) → release.yml (OIDC publish) → /release-conductor verify <tag>
Steps
0. Refresh local refs, then branch on subcommand
If $ARGUMENTS starts with verify, jump to the verify subcommand section below — it reads only from gh and needs no local git state.
Otherwise refresh before anything reads local git state:
git fetch origin main --tags --prune-tags
This is load-bearing — do not skip it. Steps 4 and 5 derive LAST_TAG from local tags and diff against origin/main. On a clone whose tags are stale, every downstream number is wrong and still looks plausible: the bump comparison, the "PRs merged since last tag" list, and the commit-type check all describe the wrong release window, and nothing blocks — the gate reports READY on a false picture.
Observed in practice: a clone three tags behind reported the bump as 2.10.0 → 2.13.0 with 23 landed PRs, when the truth was 2.12.0 → 2.13.0 with 3.
Then continue with the gate + merge flow.
1. Find the open release-please PR
gh pr list --state open --search "head:release-please--" \
--json number,title,headRefName,mergeable,mergeStateStatus,labels,body
-
Zero matches. Check what's accumulated since the last tag:
# Requires the step 0 fetch — stale tags here mean the wrong commit window. LAST_TAG=$(git tag --list 'v*' --sort=-version:refname | head -1) [ -n "$LAST_TAG" ] || { echo "no v* tag found — run: git fetch origin --tags"; exit 1; } git log "$LAST_TAG"..origin/main --oneline --no-mergesIf the log contains
feat:/fix:/perf:/refactor:commits but no PR is open, suggest:gh run list --workflow=release-please.yml --limit 3release-please may have failed or not run. STOP and surface the most recent run.
If the log contains only
chore:/style:/test:/build:/ci:/docs:commits, report:no shippable changes — release-please correctly held off.STOP. -
One match. Record
RELEASE_PR=$pr_numberand continue. -
Multiple matches. STOP and ask the user which to ship — this signals a release-please config issue (multi-package monorepo without
include-component-in-tag, etc.).
2. Show what's being shipped
gh pr view $RELEASE_PR --json title,body | jq -r '.title, .body'
gh pr diff $RELEASE_PR -- CHANGELOG.md | head -120
gh pr view $RELEASE_PR --json files --jq '.files[].path'
Report:
- PR title (release-please format:
chore(main): release X.Y.Z) - The changelog section being added (first ~120 lines of the diff)
- Files changed by the PR (should be
CHANGELOG.md,package.json,.release-please-manifest.json, and any post-bump regen —index/,docs/stamps)
3. Verify CI on the release PR
gh pr checks $RELEASE_PR
- Any check
failing→ STOP. Print the failing check names; tell the user to address them before re-running. - Any check
pending→ STOP. Tell the user to wait for CI to finish. - All checks pass → continue.
Sanity check on main:
gh run list --branch main --limit 5 --json conclusion,name,createdAt
If the most recent run on main is failing, surface as WARNING (not blocking) — release.yml will run against the tag, not main, so a stale main failure won't break publish, but it's worth knowing.
4. Verify no release blockers and feature PRs landed
# Requires the step 0 fetch. A stale tag here silently shifts the whole
# release window: wrong bump, wrong landed-PR list, wrong commit-type check.
LAST_TAG=$(git tag --list 'v*' --sort=-version:refname | head -1)
[ -n "$LAST_TAG" ] || { echo "no v* tag found — run: git fetch origin --tags"; exit 1; }
LAST_TAG_DATE=$(git log -1 --format=%cI "$LAST_TAG")
# Feature PRs merged since last tag
gh pr list --state merged --search "merged:>$LAST_TAG_DATE base:main" \
--limit 50 --json number,title,headRefName,labels
# Open PRs flagged as blockers
gh pr list --state open --search "label:release-blocker,do-not-release,wip" \
--json number,title,labels
- Report count of feature PRs merged since
$LAST_TAG. - Any open PR with
release-blockerordo-not-releaselabel → STOP. Surface the PR numbers. - Any merged PR with those labels (mistakes happen) → surface as WARNING.
5. Sanity-check the bump
PREV_VERSION=${LAST_TAG#v}
NEW_VERSION=$(gh pr view $RELEASE_PR --json title --jq .title \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
echo "Bump: $PREV_VERSION → $NEW_VERSION"
Inspect conventional-commit types since last tag:
git log "$LAST_TAG"..origin/main --pretty=format:'%s%n%b' --no-merges \
| grep -E '^(feat|fix|perf|refactor|docs)(\(.+\))?!?:|BREAKING CHANGE:' \
| sort -u
Expected bump (post-1.0):
feat!:orBREAKING CHANGE:anywhere → majorfeat:(no breaking) → minorfix:/perf:/refactor:only → patch
For pre-1.0 packages, read release-please-config.json — bump-minor-pre-major promotes major→minor; bump-patch-for-minor-pre-major promotes minor→patch.
If release-please's bump diverges from expectation, surface as WARNING with the divergence (likely config). Do not block — release-please's config is the source of truth.
6. Go/no-go summary
Release-conductor: PR #$RELEASE_PR — $PREV_VERSION → $NEW_VERSION
Step 2 — Files: CHANGELOG.md, package.json, .release-please-manifest.json, …
Step 3 — CI release PR: ✓ green
| ✗ failing: <names> (BLOCKED)
| ⏳ pending (BLOCKED — wait)
Step 3 — CI main: ✓ green
| ⚠ recent failures (not blocking publish)
Step 4 — Blockers: none
| ✗ open: PR #N (label) (BLOCKED)
Step 4 — Landed PRs: N feature PRs since $LAST_TAG
Step 5 — Bump: ✓ matches commit types
| ⚠ release-please chose $NEW_VERSION, expected $EXPECTED (<reason>)
Status: READY — say "ship" to merge PR #$RELEASE_PR (squash) and trigger release.yml.
| BLOCKED — <reason>. Fix and re-run /release-conductor.
7. Merge on explicit approval
Wait for the user to say ship, merge, go, or lgtm. CI green alone is not authorization.
On approval:
gh pr merge $RELEASE_PR --squash --delete-branch
If the repo overrides merge method (check .github/settings.yml or repo settings UI), follow the override and note it in the report.
8. Report the publish pipeline run
release-please's GitHub Action creates the v$NEW_VERSION tag immediately after merge; the tag push fires release.yml. Report the most recent runs so the user can track:
TAG="v$NEW_VERSION"
gh run list --workflow=release.yml --limit 3 \
--json url,headBranch,status,conclusion,createdAt
Do not poll. Print:
Merged PR #$RELEASE_PR ($PREV_VERSION → $NEW_VERSION)
Tag should appear shortly: $TAG
Track release.yml above. Once it shows ✓ success:
/release-conductor verify $TAG
verify <tag> subcommand
Post-publish smoke test. No gating, no merge — just three checks.
TAG=$(echo "$ARGUMENTS" | awk '{print $2}')
VERSION=${TAG#v}
PKG=$(node -p "require('./package.json').name")
# 1. release.yml conclusion for this tag
gh run list --workflow=release.yml --branch "$TAG" --limit 1 \
--json conclusion,url,databaseId
# 2. npm has the version
npm view "$PKG@$VERSION" version dist.tarball
# 3. GitHub release exists
gh release view "$TAG" --json url,publishedAt,name
Report PASS / FAIL per check. Specifics:
- release.yml conclusion —
success= PASS.in_progress= "still running, retry in ~60s."failure= FAIL, print URL. - npm view — exit 0 = PASS. 404 = "not yet propagated (npm CDN lag, up to ~60s)." Other error = FAIL.
- gh release view — exit 0 = PASS.
release not found= FAIL.
Rules
- Never reimplement release-please. This skill does not write CHANGELOG.md, bump versions, or create tags. release-please owns that; this skill only gates the merge of release-please's PR.
- Never publish to npm directly. OIDC publish happens in
release.ymlon tag push. The skill stops atgh pr merge. - Require explicit verbal approval.
ship/merge/go/lgtm. CI green alone is not authorization. - One release PR at a time. Multiple open
release-please--*PRs → stop and ask. Likely a config issue. - Squash merge by default. Matches
/merge-prconvention. Repo override (if any) wins, but record it. - No polling. Print the run URL and exit. Use
verify <tag>later. - Pre-1.0 bump checks read config. Don't assume semver —
release-please-config.jsonmay havebump-minor-pre-majorflags that alter the expected bump. - Always fetch tags before reading them.
LAST_TAGcomes from the local tag list; a stale clone yields a wrong release window that still reports READY. Step 0's fetch is not optional.