---
title: "Release Management"
description: "Automates GitHub releases with semantic versioning, changelog generation from merged PRs, and gh CLI integration. Supports draft, prerelease, and standard release workflows with task-tracked multi-phase execution. Use when creating releases, tagging versions, or publishing changelogs."
---

<span className="badge badge-gray">Reference</span> <span className="badge badge-green">low</span>

> **Auto-activated** — this skill loads automatically when Claude detects matching context.

<ContextualSkillSidebar slug="release-management" />


# Release Management

Automate releases with `gh release`, semantic versioning, and changelog generation.

> **CC ≥ 2.1.118 (M122):** `claude plugin tag &lt;version&gt;` validates the plugin manifest hierarchy (marketplace.json, plugin.json, package.json, version.txt) before tagging. Runs `--dry-run` in CI before release-please opens its release PR; catches manifest drift early. See `src/skills/chain-patterns/references/plugin-tag.md`.

## CRITICAL: Task Management is MANDATORY (CC 2.1.16)

**BEFORE doing ANYTHING else, create tasks to track progress:**

```python
# 1. Create main task IMMEDIATELY
TaskCreate(
  subject="Release: {version}",
  description="Creating release with semantic versioning and changelog",
  activeForm="Releasing {version}"
)

# 2. Create subtasks for each release phase
TaskCreate(subject="Version & changelog", activeForm="Determining version and generating changelog")
TaskCreate(subject="Create release", activeForm="Creating GitHub release")
TaskCreate(subject="Verify & announce", activeForm="Verifying release and announcing")

# 3. Set dependencies for sequential phases
TaskUpdate(taskId="3", addBlockedBy=["2"])
TaskUpdate(taskId="4", addBlockedBy=["3"])

# 4. Before starting each task, verify it's unblocked
task = TaskGet(taskId="2")  # Verify blockedBy is empty

# 5. Update status as you progress
TaskUpdate(taskId="2", status="in_progress")  # When starting
TaskUpdate(taskId="2", status="completed")    # When done
```

## Quick Reference

### Create Release

```bash
# Auto-generate notes from PRs
gh release create v1.2.0 --generate-notes

# With custom title
gh release create v1.2.0 --title "Version 1.2.0: Performance Update" --generate-notes

# Draft release (review before publishing)
gh release create v1.2.0 --draft --generate-notes

# Pre-release (beta, rc)
gh release create v1.2.0-beta.1 --prerelease --generate-notes

# With custom notes
gh release create v1.2.0 --notes "## Highlights
- New auth system
- 50% faster search"

# From notes file
gh release create v1.2.0 --notes-file RELEASE_NOTES.md
```

### List & View Releases

```bash
# List all releases
gh release list

# View specific release
gh release view v1.2.0

# View in browser
gh release view v1.2.0 --web

# JSON output
gh release list --json tagName,publishedAt,isPrerelease
```

### Verify Releases (gh CLI 2.86.0+)

```bash
# Verify release attestation (sigstore)
gh release verify v1.2.0

# Verify specific asset
gh release verify-asset v1.2.0 ./dist/app.zip

# Verify with custom trust policy
gh release verify v1.2.0 --owner myorg
```

### Manage Releases

```bash
# Edit release
gh release edit v1.2.0 --title "New Title" --notes "Updated notes"

# Delete release
gh release delete v1.2.0

# Upload assets
gh release upload v1.2.0 ./dist/app.zip ./dist/app.tar.gz
```

---

## Semantic Versioning

```text
MAJOR.MINOR.PATCH
  │     │     │
  │     │     └── Bug fixes (backwards compatible)
  │     └──────── New features (backwards compatible)
  └────────────── Breaking changes

Examples:
  1.0.0 → 1.0.1  (patch: bug fix)
  1.0.1 → 1.1.0  (minor: new feature)
  1.1.0 → 2.0.0  (major: breaking change)

Pre-release:
  2.0.0-alpha.1  (early testing)
  2.0.0-beta.1   (feature complete)
  2.0.0-rc.1     (release candidate)
```

---

## Release Workflows

Standard and hotfix release procedures using git tags and `gh release`.

Load Read("$\{CLAUDE_SKILL_DIR\}/references/release-workflows.md") for step-by-step standard and hotfix release workflows.

---

## Changelog Generation

Auto-generated from PRs, custom `.github/release.yml` templates, and manual CHANGELOG.md format.

Load Read("$\{CLAUDE_SKILL_DIR\}/references/changelog-generation.md") for changelog template examples and Keep-a-Changelog format.

---

## Release Automation & Checklist

GitHub Actions workflow for tag-triggered releases, version bumping script, and pre/post-release checklist.

Load Read("$\{CLAUDE_SKILL_DIR\}/references/release-automation.md") for CI workflow, bump script, and release checklist.

---

## Best Practices

1. **Use semantic versioning** - Communicate change impact
2. **Draft releases first** - Review notes before publishing
3. **Generate notes from PRs** - Accurate, automatic history
4. **Close milestone on release** - Track completion
5. **Tag main only** - Never tag feature branches
6. **Announce breaking changes** - Prominent in release notes

## Related Skills

- `ork:github-operations`: Milestones, issues, and CLI reference
- `ork:github-operations`: Branch management and git operations

## References

Load on demand with `Read("$\{CLAUDE_SKILL_DIR\}/references/&lt;file&gt;")`:

| File | Content |
|------|---------|
| `semver.md` | Semantic versioning rules and decision tree |
| `release-workflows.md` | Standard and hotfix release procedures |
| `changelog-generation.md` | Auto-generated, template, and manual changelog formats |
| `release-automation.md` | GitHub Actions workflow, bump script, and checklist |


---

## Rules (2)

### Changelog must document all breaking changes with migration instructions — HIGH


# Changelog Must Include All Breaking Changes

## Why

Developers read changelogs before upgrading. If a breaking change is missing from the changelog, they upgrade without migration steps and hit runtime errors. After one bad experience, teams stop upgrading and fall behind on security patches.

## Rule

Before publishing a release:
1. Cross-reference all commits with the changelog
2. Every commit with `!` or `BREAKING CHANGE` must appear in the changelog
3. Breaking changes must include migration instructions
4. The Breaking Changes section must appear first in the changelog

## Incorrect — incomplete changelog missing breaking changes

```markdown
# Changelog

## [2.0.0] - 2026-03-10

### Added
- Bulk export endpoint (#234)
- Dark mode support (#236)

### Fixed
- Date parser null handling (#235)
```

**Problems:**
- Version is 2.0.0 (major) but no breaking changes listed
- The commit `feat!: remove /v1/users endpoint (#230)` is missing
- The commit `refactor!: rename User model to Account (#232)` is missing
- No migration instructions for breaking changes

## Correct — complete changelog with migration instructions

```markdown
# Changelog

## [2.0.0] - 2026-03-10

### BREAKING CHANGES

- **Removed `/v1/users` endpoint** (#230): The deprecated v1 users
  API has been removed. Migrate to `/v2/users` which supports
  cursor-based pagination.

  ```diff
  - GET /v1/users?page=2&limit=20
  + GET /v2/users?cursor=abc123&limit=20
  ```

- **Renamed `User` model to `Account`** (#232): All database tables
  and API responses now use `Account`. Run the migration:

  ```bash
  npm run migrate:up
  ```

  Update imports:
  ```diff
  - import \{ User \} from "./models/user";
  + import \{ Account \} from "./models/account";
  ```

### Added
- Bulk export endpoint (#234)
- Dark mode support (#236)

### Fixed
- Date parser null handling (#235)
```

## Verification Script

```bash
#!/bin/bash
LAST_TAG=$(gh release view --json tagName -q .tagName)

# Find all breaking change commits
BREAKING=$(git log "$LAST_TAG"..HEAD --oneline --format="%s" \
  | grep -E '!:|BREAKING CHANGE')

if [ -z "$BREAKING" ]; then
  echo "No breaking changes found"
  exit 0
fi

echo "Breaking changes to verify in CHANGELOG.md:"
echo "$BREAKING" | while IFS= read -r commit; do
  # Extract PR number if present
  PR=$(echo "$commit" | grep -oE '#[0-9]+' | head -1)
  if [ -n "$PR" ] && ! grep -q "$PR" CHANGELOG.md; then
    echo "  MISSING: $commit"
  else
    echo "  OK: $commit"
  fi
done
```

## Changelog Section Order

| Section | Required When | Position |
|---------|--------------|----------|
| BREAKING CHANGES | Any `!` or `BREAKING CHANGE` commit | First |
| Deprecated | Deprecation notices | Second |
| Added | New features | Third |
| Changed | Behavior changes | Fourth |
| Fixed | Bug fixes | Fifth |
| Security | CVE patches | Sixth |


### Validate that semantic version bump matches the actual scope of changes — HIGH


# Validate Version Bump Matches Change Scope

## Why

Semantic versioning is a contract with consumers. A breaking API change shipped as a patch version (1.2.3 -> 1.2.4) causes runtime failures for anyone pinned to `^1.2.3`. A bug fix shipped as major (1.2.3 -> 2.0.0) forces unnecessary migration work.

## Rule

Before tagging a release:
1. List all commits since the last release
2. Classify each commit as patch/minor/major based on its impact
3. The version bump must match the highest-impact commit
4. Breaking changes require major bump — no exceptions

## Incorrect — bump version without checking changes

```bash
# Blindly bump patch without reviewing commits
CURRENT="1.2.3"
NEW="1.2.4"  # Assumed patch

git tag -a "v$NEW" -m "Release v$NEW"
git push origin "v$NEW"
gh release create "v$NEW" --generate-notes

# But commits since v1.2.3 include:
# - "feat!: remove deprecated /v1/users endpoint" (BREAKING)
# - "feat: add /v2/users with pagination" (MINOR)
# - "fix: correct date parsing" (PATCH)
# Correct bump should have been MAJOR (2.0.0)
```

## Correct — classify commits then determine bump

```bash
#!/bin/bash
LAST_TAG=$(gh release view --json tagName -q .tagName)
echo "Commits since $LAST_TAG:"

# Classify commits
MAJOR=0; MINOR=0; PATCH=0

while IFS= read -r msg; do
  if echo "$msg" | grep -qE '^(feat|fix|refactor)(\(.+\))?!:|^BREAKING'; then
    MAJOR=$((MAJOR + 1))
    echo "  MAJOR: $msg"
  elif echo "$msg" | grep -qE '^feat(\(.+\))?:'; then
    MINOR=$((MINOR + 1))
    echo "  MINOR: $msg"
  else
    PATCH=$((PATCH + 1))
    echo "  PATCH: $msg"
  fi
done < <(git log "$LAST_TAG"..HEAD --oneline --format="%s")

# Determine correct bump
if [ "$MAJOR" -gt 0 ]; then
  echo "Required bump: MAJOR ($MAJOR breaking changes)"
elif [ "$MINOR" -gt 0 ]; then
  echo "Required bump: MINOR ($MINOR new features)"
else
  echo "Required bump: PATCH ($PATCH fixes)"
fi
```

## Classification Guide

| Commit Pattern | Bump | Example |
|----------------|------|---------|
| `fix:` | PATCH | `fix: handle null in date parser` |
| `docs:`, `chore:`, `ci:` | PATCH | `docs: update API reference` |
| `feat:` | MINOR | `feat: add bulk export endpoint` |
| `feat!:` or `BREAKING CHANGE:` | MAJOR | `feat!: remove v1 API endpoints` |
| `refactor!:` | MAJOR | `refactor!: rename User to Account` |

## Pre-Release Validation

```bash
# Before creating the release, verify:
echo "=== Pre-Release Checklist ==="
echo "1. Last release: $LAST_TAG"
echo "2. Commits since: $(git rev-list "$LAST_TAG"..HEAD --count)"
echo "3. Breaking changes: $MAJOR"
echo "4. New features: $MINOR"
echo "5. Fixes/chores: $PATCH"
echo "6. Proposed version: v$NEW"
echo ""
if [ "$MAJOR" -gt 0 ] && ! echo "$NEW" | grep -qE '^[0-9]+\.0\.0'; then
  echo "ERROR: Breaking changes found but version is not a major bump"
  exit 1
fi
```



---

## References (4)

### Changelog Generation

# Changelog Generation

## Auto-Generated (from PRs)

```bash
# GitHub auto-generates from merged PRs
gh release create v1.2.0 --generate-notes

# Output includes:
# ## What's Changed
# * feat: Add user auth by @dev in #123
# * fix: Login redirect by @dev in #124
# * docs: Update README by @dev in #125
```

## Custom Changelog Template

Create `.github/release.yml`:

```yaml
changelog:
  categories:
    - title: "Breaking Changes"
      labels:
        - "breaking"
    - title: "New Features"
      labels:
        - "enhancement"
        - "feature"
    - title: "Bug Fixes"
      labels:
        - "bug"
        - "fix"
    - title: "Documentation"
      labels:
        - "documentation"
    - title: "Other Changes"
      labels:
        - "*"
```

## Manual CHANGELOG.md

```markdown
# Changelog

## [1.3.0] - 2026-01-15

### Added
- User authentication system (#123)
- Dark mode support (#125)

### Changed
- Improved search performance (#126)

### Fixed
- Login redirect loop (#124)

### Security
- Updated dependencies for CVE-2026-1234
```


### Release Automation

# Release Automation

## GitHub Actions Release Workflow

```yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build

      - name: Create Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release create ${{ github.ref_name }} \
            --generate-notes \
            ./dist/*.zip
```

## Version Bumping Script

```bash
#!/bin/bash
# bump-version.sh

CURRENT=$(gh release view --json tagName -q .tagName | sed 's/v//')
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"

case $1 in
  major) NEW="$((MAJOR + 1)).0.0" ;;
  minor) NEW="$MAJOR.$((MINOR + 1)).0" ;;
  patch) NEW="$MAJOR.$MINOR.$((PATCH + 1))" ;;
  *) echo "Usage: $0 [major|minor|patch]"; exit 1 ;;
esac

echo "Bumping $CURRENT -> $NEW"
git tag -a "v$NEW" -m "Release v$NEW"
git push origin "v$NEW"
gh release create "v$NEW" --generate-notes
```

Usage:

```bash
./bump-version.sh patch  # 1.2.3 -> 1.2.4
./bump-version.sh minor  # 1.2.4 -> 1.3.0
./bump-version.sh major  # 1.3.0 -> 2.0.0
```

## Release Checklist

```markdown
## Release v1.3.0 Checklist

### Pre-Release
- [ ] All PRs merged to main
- [ ] CI/CD passing on main
- [ ] Version numbers updated in package.json/pyproject.toml
- [ ] CHANGELOG.md updated
- [ ] Documentation updated
- [ ] Milestone closed

### Release
- [ ] Tag created and pushed
- [ ] GitHub release created
- [ ] Release notes reviewed
- [ ] Assets uploaded (if applicable)

### Post-Release
- [ ] Deployment verified
- [ ] Announcement posted (if applicable)
- [ ] Next milestone created
```


### Release Workflows

# Release Workflows

## Standard Release

```bash
# 1. Ensure main is up to date
git checkout main
git pull origin main

# 2. Determine version bump
# Check commits since last release
gh release view --json tagName -q .tagName  # Current: v1.2.3
git log v1.2.3..HEAD --oneline

# 3. Create and push tag
git tag -a v1.3.0 -m "Release v1.3.0"
git push origin v1.3.0

# 4. Create GitHub release
gh release create v1.3.0 \
  --title "v1.3.0: Feature Name" \
  --generate-notes

# 5. Close milestone if used
gh api -X PATCH repos/:owner/:repo/milestones/5 -f state=closed
```

## Hotfix Release

```bash
# 1. Branch from release tag
git checkout -b hotfix/v1.2.4 v1.2.3

# 2. Fix and commit
git commit -m "fix: Critical security patch"

# 3. Tag and release
git tag -a v1.2.4 -m "Hotfix: Security patch"
git push origin v1.2.4
gh release create v1.2.4 --title "v1.2.4: Security Hotfix" \
  --notes "Critical security fix for authentication bypass"

# 4. Merge fix to main
git checkout main
git cherry-pick <commit-sha>
git push origin main
```


### Semver

# Semantic Versioning Guide

Standard versioning for software releases.

## Version Format

```
MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]

Examples:
1.0.0
2.1.3
3.0.0-alpha.1
3.0.0-beta.2+build.123
```

## When to Bump

### PATCH (x.x.X)
Bug fixes, backwards compatible:
```
1.0.0 -> 1.0.1
```
- Fix typo in error message
- Fix edge case bug
- Security patch (no API change)
- Performance improvement (no API change)

### MINOR (x.X.0)
New features, backwards compatible:
```
1.0.1 -> 1.1.0
```
- Add new function/method
- Add new optional parameter
- Deprecate (but don't remove) feature
- Add new event/hook

### MAJOR (X.0.0)
Breaking changes:
```
1.1.0 -> 2.0.0
```
- Remove function/method
- Change function signature
- Change return type
- Rename public API
- Change default behavior

## Pre-release Versions

```
2.0.0-alpha.1   # Early development
2.0.0-alpha.2
2.0.0-beta.1    # Feature complete
2.0.0-beta.2
2.0.0-rc.1      # Release candidate
2.0.0-rc.2
2.0.0           # Final release
```

### Precedence
```
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0
```

## Decision Tree

```
Is it a breaking change?
├── Yes → MAJOR bump
└── No
    └── Is it a new feature?
        ├── Yes → MINOR bump
        └── No → PATCH bump
```

## Examples

| Change | Version Bump | Reason |
|--------|--------------|--------|
| Fix null pointer crash | 1.0.0 → 1.0.1 | Bug fix |
| Add `sort` parameter | 1.0.1 → 1.1.0 | New feature |
| Change `sort` to required | 1.1.0 → 2.0.0 | Breaking |
| Improve performance 2x | 1.0.0 → 1.0.1 | No API change |
| Remove deprecated method | 1.5.0 → 2.0.0 | Breaking |
| Add new endpoint | 1.0.0 → 1.1.0 | New feature |

## Commands

```bash
# View current version
cat package.json | jq '.version'

# Bump with npm
npm version patch  # 1.0.0 -> 1.0.1
npm version minor  # 1.0.1 -> 1.1.0
npm version major  # 1.1.0 -> 2.0.0

# With git tag
npm version patch -m "Release %s"

# Manual
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
```

## 0.x.x Versions

For pre-1.0 development:
- API is unstable
- MINOR can include breaking changes
- PATCH for any fixes/features

```
0.1.0  Initial development
0.2.0  Breaking changes OK
0.9.0  Approaching stable
1.0.0  First stable release
```

