Release orchestration
Runs the whole release documented in RELEASING.md, composing /update-changelog and
/test-mcp-server. The flow is changelog-first: the audited changelog draft is the
evidence for the version decision, and the release ticket and branch are created only
after the human approves the version, so they are born with the right number.
Publishing to Artifactory and DockerHub is irreversible, so the human gates below are
mandatory, never skipped.
Usage
/release takes no arguments. The version always comes from the suggest-and-approve
gate in Phase 3.
Re-entry
Releases fail mid-flight more than anywhere else. On re-entry, detect completed work
from the repo and skip it: stamped changelog already on origin/main → go to Phase 5
or 6; release workflow already succeeded → go to Phase 7. The workflow itself is safe
to re-dispatch because it reuses an existing vX.Y.Z tag at HEAD.
Phase 1 — Preflight (read-only)
git fetch origin --tags -q. Confirm the working tree is clean and HEAD matches
origin/main. If not, stop and tell the human.
- Verify the
pr-tools plugin is available (check for /pr-tools:create-pr in the
skill list). Phase 4 depends on it. If missing, stop and tell the human to
install it per the CLAUDE.md "Required Plugins" section.
- Confirm CI is green:
gh run list --branch main --workflow build.yml --limit 1 --json conclusion,headSha and check the run covers the current origin/main
HEAD. Red or stale CI stops the release by default. The human may explicitly
override for a known-flaky failure, record the override in the final report.
- Find the last release tag:
git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1.
- Enumerate the release content:
git log --oneline --merges vLAST..origin/main,
plus the same without --merges for direct commits. If the range is empty, there
is nothing to release, stop.
- If every change in the range is internal (CI config, dependabot, refactors), ask
the human whether to ship a dependency-bump release anyway. If yes, the Phase 2
draft must include a section summarizing the dependency updates (drawn from the
dependabot PR titles, house style) so the stamped section is never empty.
Phase 2 — Changelog draft (deferred landing)
Run /update-changelog in its deferred-landing mode: it drafts, audits, and gets
the human's approval of the [Unreleased] content, but commits nothing, the landing
branch does not exist yet. It returns the approved draft and its suggested next
version, computed under the semver definition that lives in that skill.
For a dependency-bump-only release (the Phase 1 ask), pass guidance overriding the
usual internal classification, e.g. treat the dependabot bumps as a Security entry, this is a dependency-only release. Without it the skill classifies dependabot
changes as internal and returns an empty draft.
Phase 3 — Version gate
Present the version decision with AskUserQuestion: three options, each showing the
computed number and a one-line rationale citing the draft evidence, e.g.
Minor → 2.5.0 (Recommended), Patch → 2.4.1, Major → 3.0.0. The changelog
skill's suggestion is the recommended option. The human's choice is final for
naming, only the Phase 6 loop-back can reopen it.
Phase 4 — Release ticket, branch, and landing
- Create the release Jira ticket via the
jira-workflow skill: AIML Task,
component Contrast MCP Server, summary Release mcp-contrast X.Y.Z, assigned
to the current user, transitioned to In Progress. Create the matching bead per
bead-workflow (title prefix, external-ref, Jira URL comment).
- Create the release branch from
origin/main: AIML-<id>-release-X.Y.Z.
- Land two commits: the approved changelog draft
(
AIML-<id> Update changelog for X.Y.Z release), then the stamp, insert
## [X.Y.Z] - <today> directly below ## [Unreleased], moving the unreleased
content under the new heading and leaving [Unreleased] empty
(AIML-<id> Stamp vX.Y.Z release heading). Two commits, matching the
established pattern (e.g. v2.4.0).
- Push, open the PR with
pr-tools:create-pr, and wait for the human to merge.
The PR is the human's second look at the changelog.
- Verify the stamp reached
origin/main before continuing.
Phase 5 — Smoke test
Sync the local checkout to the merged origin/main first, the test builds the jar
from the current checkout and it must exercise the exact content that gets tagged.
Then run /test-mcp-server smoke (always pass the mode explicitly, the skill asks
otherwise). Show the human the report. It is purely advisory, the smoke test drives
a live org and flaky data is expected, so no failure class blocks by itself. The
human decides at the next gate.
Phase 6 — Go/no-go and dispatch
Guard: re-fetch and confirm origin/main HEAD is still the commit the stamped
changelog covers. If another PR merged in between, the tag would ship
undocumented changes. On this loop-back, this skill owns folding the new entries
into the stamped [X.Y.Z] section (/update-changelog treats stamped versions
as settled and will not reopen them). If the new entries escalate the version
class, re-confirm the version with the human, retitle the Jira ticket, restamp,
and keep the branch name, it is scaffolding with a merged PR hanging off it.
Ask the human for an explicit go/no-go.
Dispatch with the version spelled out, never blank, so the tag cannot diverge
from the stamped heading:
gh workflow run gradle-release.yml --ref main -f release_version=X.Y.Z
gh workflow run returns no run id. Fetch it with gh run list --workflow=gradle-release.yml --limit 1 --json databaseId,createdAt,status and
check createdAt is after the dispatch (guards against grabbing a stale run).
Monitor with gh run watch <id> --exit-status as a background task
(run_in_background), the run exceeds the foreground ceiling. Do not poll.
On failure, gh run view <id> --log-failed, diagnose, report, and wait for the
human. Never re-dispatch on your own, the workflow publishes to three external
registries and a partial-publish state needs a human to see which steps
completed first.
Phase 7 — Post-release verification
- Release assets:
gh release view vX.Y.Z --json assets must show five files, the
jar plus four SBOMs (jar and Docker image, CycloneDX and SPDX each).
- Attestation:
gh release download vX.Y.Z --pattern '*.jar' to a temp dir, then
gh attestation verify mcp-contrast-X.Y.Z.jar --repo Contrast-Security-OSS/mcp-contrast.
- DockerHub:
curl -sf https://hub.docker.com/v2/repositories/contrast/mcp-contrast/tags/X.Y.Z.
- Artifactory:
curl -sfI https://contrastsecurity.jfrog.io/artifactory/contrast-mcp-release/com/contrast/labs/ai/mcp/contrast-mcp-core/X.Y.Z/contrast-mcp-core-X.Y.Z.pom.
If anonymous read is refused, note it and rely on the workflow's publish step
having succeeded.
For any failed check, report it together with a suggested remediation path (which
workflow step to inspect, whether re-dispatch is safe). Do not attempt remediation
yourself, a half-published release is exactly where automation causes damage.
Phase 8 — Release notes
Replace the workflow's auto-generated GitHub release notes with the stamped
changelog section, one source of truth for what users read:
gh release edit vX.Y.Z --notes-file <file containing the [X.Y.Z] section>
Phase 9 — Tracker sweep
- Derive shipped AIML ids from two sources, union and deduplicate the results.
First,
gh pr list --state merged --json number,title,mergeCommit --limit 200,
keep PRs whose merge SHA appears in git rev-list vLAST..vX.Y.Z, extract
AIML-\d+ from their titles. Second, scan every commit subject in
vLAST..vX.Y.Z for AIML-\d+ to catch direct commits and collapsed stack
merges. Skip dependabot PRs.
- Show the human the full list with each ticket's current status and let them pick
the set to transition. Transition the chosen tickets to Closed (
81) via the
jira-workflow skill, the release ticket included. Closed is reserved for code
released to production, which is now true.
- Close the matching beads per
bead-workflow after one batch confirmation showing
the full list, then br sync --flush-only.
Phase 10 — Announce
Only after Phase 7 passed, never for a failed or abandoned release: draft a Slack
post for #_aiml-team from the stamped changelog section plus the GitHub Release
link (which links onward to everything else). Show the human the draft and send it
only on their approval.
Report
Close with the version, tag, GitHub Release URL, each verification result, any CI
override recorded in Phase 1, the tickets and beads transitioned, and the Slack
post status.
Notes
- Never commit to
main directly, everything lands via the release-branch PR.
make verify is not repeated here, the workflow runs the full validation build
including integration tests before tagging.
- This skill does not modify the Gradle Release workflow or RELEASING.md steps, it
automates around them. If the workflow changes, update this skill to match.
1---2name: release3description: Orchestrate a full release of mcp-contrast end to end, changelog-first. Preflight, deferred-landing changelog draft, version gate, release ticket and branch, smoke test, Gradle Release dispatch and monitoring, artifact verification, release-notes sync, tracker sweep, and Slack announcement. Run when a human asks to cut, make, or ship a release. Human gates at changelog approval, version choice, changelog PR merge, go/no-go, tracker sweep, and the Slack post.4---56# Release orchestration78Runs the whole release documented in RELEASING.md, composing `/update-changelog` and9`/test-mcp-server`. The flow is changelog-first: the audited changelog draft is the10evidence for the version decision, and the release ticket and branch are created only11after the human approves the version, so they are born with the right number.12Publishing to Artifactory and DockerHub is irreversible, so the human gates below are13mandatory, never skipped.1415## Usage1617`/release` takes no arguments. The version always comes from the suggest-and-approve18gate in Phase 3.1920## Re-entry2122Releases fail mid-flight more than anywhere else. On re-entry, detect completed work23from the repo and skip it: stamped changelog already on `origin/main` → go to Phase 524or 6; release workflow already succeeded → go to Phase 7. The workflow itself is safe25to re-dispatch because it reuses an existing `vX.Y.Z` tag at HEAD.2627## Phase 1 — Preflight (read-only)28291. `git fetch origin --tags -q`. Confirm the working tree is clean and HEAD matches30 `origin/main`. If not, stop and tell the human.312. Verify the `pr-tools` plugin is available (check for `/pr-tools:create-pr` in the32 skill list). Phase 4 depends on it. If missing, stop and tell the human to33 install it per the CLAUDE.md "Required Plugins" section.343. Confirm CI is green: `gh run list --branch main --workflow build.yml --limit 135 --json conclusion,headSha` and check the run covers the current `origin/main`36 HEAD. Red or stale CI stops the release by default. The human may explicitly37 override for a known-flaky failure, record the override in the final report.384. Find the last release tag: `git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1`.395. Enumerate the release content: `git log --oneline --merges vLAST..origin/main`,40 plus the same without `--merges` for direct commits. If the range is empty, there41 is nothing to release, stop.426. If every change in the range is internal (CI config, dependabot, refactors), ask43 the human whether to ship a dependency-bump release anyway. If yes, the Phase 244 draft must include a section summarizing the dependency updates (drawn from the45 dependabot PR titles, house style) so the stamped section is never empty.4647## Phase 2 — Changelog draft (deferred landing)4849Run `/update-changelog` in its **deferred-landing mode**: it drafts, audits, and gets50the human's approval of the `[Unreleased]` content, but commits nothing, the landing51branch does not exist yet. It returns the approved draft and its suggested next52version, computed under the semver definition that lives in that skill.5354For a dependency-bump-only release (the Phase 1 ask), pass guidance overriding the55usual internal classification, e.g. `treat the dependabot bumps as a Security entry,56this is a dependency-only release`. Without it the skill classifies dependabot57changes as internal and returns an empty draft.5859## Phase 3 — Version gate6061Present the version decision with AskUserQuestion: three options, each showing the62computed number and a one-line rationale citing the draft evidence, e.g.63`Minor → 2.5.0 (Recommended)`, `Patch → 2.4.1`, `Major → 3.0.0`. The changelog64skill's suggestion is the recommended option. The human's choice is final for65naming, only the Phase 6 loop-back can reopen it.6667## Phase 4 — Release ticket, branch, and landing68691. Create the release Jira ticket via the `jira-workflow` skill: AIML Task,70 component `Contrast MCP Server`, summary `Release mcp-contrast X.Y.Z`, assigned71 to the current user, transitioned to In Progress. Create the matching bead per72 `bead-workflow` (title prefix, external-ref, Jira URL comment).732. Create the release branch from `origin/main`: `AIML-<id>-release-X.Y.Z`.743. Land two commits: the approved changelog draft75 (`AIML-<id> Update changelog for X.Y.Z release`), then the stamp, insert76 `## [X.Y.Z] - <today>` directly below `## [Unreleased]`, moving the unreleased77 content under the new heading and leaving `[Unreleased]` empty78 (`AIML-<id> Stamp vX.Y.Z release heading`). Two commits, matching the79 established pattern (e.g. v2.4.0).804. Push, open the PR with `pr-tools:create-pr`, and wait for the human to merge.81 The PR is the human's second look at the changelog.825. Verify the stamp reached `origin/main` before continuing.8384## Phase 5 — Smoke test8586Sync the local checkout to the merged `origin/main` first, the test builds the jar87from the current checkout and it must exercise the exact content that gets tagged.88Then run `/test-mcp-server smoke` (always pass the mode explicitly, the skill asks89otherwise). Show the human the report. It is purely advisory, the smoke test drives90a live org and flaky data is expected, so no failure class blocks by itself. The91human decides at the next gate.9293## Phase 6 — Go/no-go and dispatch94951. Guard: re-fetch and confirm `origin/main` HEAD is still the commit the stamped96 changelog covers. If another PR merged in between, the tag would ship97 undocumented changes. On this loop-back, this skill owns folding the new entries98 into the stamped `[X.Y.Z]` section (`/update-changelog` treats stamped versions99 as settled and will not reopen them). If the new entries escalate the version100 class, re-confirm the version with the human, retitle the Jira ticket, restamp,101 and keep the branch name, it is scaffolding with a merged PR hanging off it.1022. Ask the human for an explicit go/no-go.1033. Dispatch with the version spelled out, never blank, so the tag cannot diverge104 from the stamped heading:105106 ```bash107 gh workflow run gradle-release.yml --ref main -f release_version=X.Y.Z108 ```1091104. `gh workflow run` returns no run id. Fetch it with `gh run list111 --workflow=gradle-release.yml --limit 1 --json databaseId,createdAt,status` and112 check `createdAt` is after the dispatch (guards against grabbing a stale run).1135. Monitor with `gh run watch <id> --exit-status` as a background task114 (`run_in_background`), the run exceeds the foreground ceiling. Do not poll.1156. On failure, `gh run view <id> --log-failed`, diagnose, report, and wait for the116 human. Never re-dispatch on your own, the workflow publishes to three external117 registries and a partial-publish state needs a human to see which steps118 completed first.119120## Phase 7 — Post-release verification1211221. Release assets: `gh release view vX.Y.Z --json assets` must show five files, the123 jar plus four SBOMs (jar and Docker image, CycloneDX and SPDX each).1242. Attestation: `gh release download vX.Y.Z --pattern '*.jar'` to a temp dir, then125 `gh attestation verify mcp-contrast-X.Y.Z.jar --repo Contrast-Security-OSS/mcp-contrast`.1263. DockerHub: `curl -sf https://hub.docker.com/v2/repositories/contrast/mcp-contrast/tags/X.Y.Z`.1274. Artifactory: `curl -sfI https://contrastsecurity.jfrog.io/artifactory/contrast-mcp-release/com/contrast/labs/ai/mcp/contrast-mcp-core/X.Y.Z/contrast-mcp-core-X.Y.Z.pom`.128 If anonymous read is refused, note it and rely on the workflow's publish step129 having succeeded.130131For any failed check, report it together with a suggested remediation path (which132workflow step to inspect, whether re-dispatch is safe). Do not attempt remediation133yourself, a half-published release is exactly where automation causes damage.134135## Phase 8 — Release notes136137Replace the workflow's auto-generated GitHub release notes with the stamped138changelog section, one source of truth for what users read:139140```bash141gh release edit vX.Y.Z --notes-file <file containing the [X.Y.Z] section>142```143144## Phase 9 — Tracker sweep1451461. Derive shipped AIML ids from two sources, union and deduplicate the results.147 First, `gh pr list --state merged --json number,title,mergeCommit --limit 200`,148 keep PRs whose merge SHA appears in `git rev-list vLAST..vX.Y.Z`, extract149 `AIML-\d+` from their titles. Second, scan every commit subject in150 `vLAST..vX.Y.Z` for `AIML-\d+` to catch direct commits and collapsed stack151 merges. Skip dependabot PRs.1522. Show the human the full list with each ticket's current status and let them pick153 the set to transition. Transition the chosen tickets to Closed (`81`) via the154 `jira-workflow` skill, the release ticket included. Closed is reserved for code155 released to production, which is now true.1563. Close the matching beads per `bead-workflow` after one batch confirmation showing157 the full list, then `br sync --flush-only`.158159## Phase 10 — Announce160161Only after Phase 7 passed, never for a failed or abandoned release: draft a Slack162post for `#_aiml-team` from the stamped changelog section plus the GitHub Release163link (which links onward to everything else). Show the human the draft and send it164only on their approval.165166## Report167168Close with the version, tag, GitHub Release URL, each verification result, any CI169override recorded in Phase 1, the tickets and beads transitioned, and the Slack170post status.171172## Notes173174- Never commit to `main` directly, everything lands via the release-branch PR.175- `make verify` is not repeated here, the workflow runs the full validation build176 including integration tests before tagging.177- This skill does not modify the Gradle Release workflow or RELEASING.md steps, it178 automates around them. If the workflow changes, update this skill to match.