Release Process¶
This document describes how basecoat releases are created — whether by the release-manager agent or by a human following the same steps manually. It is the single source of truth for the release workflow.
Overview¶
basecoat follows Semantic Versioning 2.0.0 and Keep a Changelog 1.1.0. Every release produces:
- An updated
version.json - A new section in
CHANGELOG.md - A git tag (
vMAJOR.MINOR.PATCH) - A GitHub release with notes extracted from the changelog
Version Bump Rules¶
| Increment | When | Examples |
|---|---|---|
| MAJOR | Breaking change to consuming repo contract — file moves, schema changes, removed required files | Renaming instructions/ to standards/, changing version.json schema |
| MINOR | New agents, skills, instructions, templates, or non-breaking additions | Adding agents/basecoat-60-workflow-release-manager.agent.md, new skill folder |
| PATCH | Bug fixes, typos, CI tweaks, documentation corrections | Fixing a broken link, correcting a regex in a workflow |
When in doubt, prefer the lower bump. A feat that adds content without changing contracts is minor, not major.
Prerequisites¶
Before starting a release, verify:
- [ ] All PRs for the release are merged to
main - [ ] CI is green on
main— no failing checks - [ ] No secrets, tokens, or PII in any merged content
- [ ]
ghCLI is installed and authenticated (gh auth status) - [ ] Working tree is clean (
git statusshows no changes) - [ ]
PRODUCTION_REPO_TOKENpreflight is green (gh workflow run token-preflight.yml --repo IBuySpy-Shared/basecoatthengh run watch)
Release Steps¶
1. Identify What Changed¶
List merged PRs since the last release tag:
CURRENT_VERSION=$(jq -r '.version' version.json)
LAST_TAG="v${CURRENT_VERSION}"
gh pr list \
--state merged \
--base main \
--search "merged:>=$(git log -1 --format=%aI ${LAST_TAG})" \
--json number,title,labels \
--limit 200
Review the list. Determine the appropriate bump level using the rules above.
2. Bump version.json¶
Update the version, releaseDate, and optionally the notes field:
{
"name": "base-coat",
"version": "X.Y.Z",
"releaseDate": "YYYY-MM-DD",
"notes": "Brief summary of what this release includes."
}
2a. Optional per-asset version bumps¶
If you changed versioned assets and want independent asset SemVer bumps:
# Preview version bumps
pwsh scripts/bump-asset-versions.ps1 -BaseRef origin/main
# Apply bumps
pwsh scripts/bump-asset-versions.ps1 -BaseRef origin/main -Apply
Then regenerate the asset manifest:
3. Update CHANGELOG.md¶
Add a new section immediately after the preamble, above existing entries. Follow this format:
## X.Y.Z - YYYY-MM-DD
### Added
- Description of new feature (#PR)
### Changed
- Description of change (#PR)
### Fixed
- Description of bug fix (#PR)
Rules:
- Use the heading format
## X.Y.Z - YYYY-MM-DD(novprefix — matches existing convention) - Group entries under
Added,Changed,Fixed,Removedper Keep a Changelog - Omit empty groups — do not include
### Removedif nothing was removed - Each entry references its PR number:
(#42) - Preserve all existing content below the new section unchanged
- If a single-line-per-PR style is already in use (as in prior releases), maintain that style for consistency
When a changelog section is unavailable for the target version, the release workflow
falls back to grouped merged-PR notes. Those notes are organized by PR type/category
(Added, Changed, Fixed, Removed, plus supporting groups like Documentation,
Testing, CI, and Maintenance) before committing to a commit-history fallback.
4. Commit the Version Bump¶
5. Choose: Direct Tag or PR Review¶
Option A — Direct Tag (for maintainers)¶
Option B — Release PR (for team review)¶
git checkout -b release/vX.Y.Z
git push origin release/vX.Y.Z
gh pr create \
--base main \
--head release/vX.Y.Z \
--title "chore: release vX.Y.Z" \
--body "Bumps version to X.Y.Z and adds CHANGELOG entry. Once merged, tag with:
\`\`\`
git tag vX.Y.Z && git push origin vX.Y.Z
\`\`\`"
After the PR is merged, pull main and create the tag:
6. Publish GitHub Release¶
Pushing a v*.*.* tag triggers the release.yml and package-basecoat.yml workflows automatically. These workflows:
- Build a source archive (
basecoat-vX.Y.Z.zip) from the tagged commit - Overlay
version.jsonin the archive so payload metadata matches the release tag - Attach
sync.ps1andsync.shto the GitHub release for consumer upgrades - Extract release notes from
CHANGELOG.md(prefers## X.Y.Z, then falls back to## Unreleased) - If no changelog section exists, generate grouped release notes from merged PRs since the last tag
- Create or update the GitHub release with the archive, sync scripts, and notes
Release artifact and version alignment contract¶
Each release should keep these three values aligned:
- Git tag:
vX.Y.Z - Root metadata:
version.json->"version": "X.Y.Z" - Distributed payload metadata:
version.jsoninsidebasecoat-vX.Y.Z.zip
Use this verification command after release publish:
Expected output: X.Y.Z (same as the tag without the v prefix).
Release notes should explicitly call out any consumer-impacting sync changes and whether a full or scoped sync is recommended.
If neither changelog section nor merged PR list is available, the workflow auto-generates release notes from commit history as a final fallback.
If you need to create the release manually (e.g., workflows are disabled), create
it in the canonical source repository (IBuySpy-Shared/basecoat). The
production mirror (ivegamsft/basecoat) is synchronized by
publish-to-production.yml and should never be targeted directly for manual
release creation.
gh release create vX.Y.Z \
--title "vX.Y.Z" \
--notes-file release-notes.md \
--repo IBuySpy-Shared/basecoat
7. Post-Release Verification¶
After the release is published:
- [ ] GitHub release page shows correct notes and artifacts
- [ ]
package-basecoat.ymlworkflow completed successfully - [ ]
release.ymlworkflow completed successfully - [ ] Tag
vX.Y.Zappears in the repository's tag list - [ ]
version.jsononmainreflects the released version - [ ]
version.jsoninsidebasecoat-vX.Y.Z.zipmatchesX.Y.Z
Using the Release Manager Agent¶
The release-manager agent (agents/basecoat-60-workflow-release-manager.agent.md) automates Steps 1–6 above. Invoke it with:
- Default (direct tag): The agent identifies merged PRs, bumps the version, writes the changelog, commits, tags, and publishes.
- With PR review: Pass the PR-based review flag to have the agent open a release PR instead of tagging directly.
- Dry run: Preview what the release would contain without making any changes.
The agent follows the same rules documented here. It does not invent its own conventions.
Hotfix Releases¶
For urgent fixes that cannot wait for a full sprint cycle:
- Branch from the latest release tag:
git checkout -b fix/<issue>-<desc> vX.Y.Z - Apply the fix, commit with
fix(<scope>): <description> - Open a PR to
main - After merge, follow the normal release steps with a
patchbump
Rollback¶
If a release introduces a critical issue:
- Revert the problematic commit(s) on
main - Follow the release steps with a new
patchversion - Do not delete or move existing tags — they are immutable history
- Note the rollback in the CHANGELOG entry for the new version
FAQ¶
Q: Can I skip the CHANGELOG?
No. Every tagged release must have a corresponding CHANGELOG entry. The version-check.yml workflow enforces that version.json and CHANGELOG headings stay in sync.
Q: What if I need to re-release the same version? Don't. Increment the patch version instead. Semver versions are immutable.
Q: What if the release workflow fails?
Check the Actions tab. The release.yml workflow uses gh release create on first publish and gh release upload + gh release edit if the release already exists. You can re-run the workflow or create the release manually.
Q: Who can cut a release?
Per docs/reference/governance.md, CI/CD changes (including releases) are decided by the repo owner. In practice, anyone with push access to main and tag permissions can follow this process.
Related Documents¶
| Document | Purpose |
|---|---|
docs/reference/governance.md |
Versioning policy, sprint process, decision-making |
CONTRIBUTING.md |
Branch naming, commit format, PR process |
version.json |
Machine-readable current version |
CHANGELOG.md |
Human-readable release history |
.github/workflows/release.yml |
Tag-triggered release automation |
.github/workflows/package-basecoat.yml |
Tag-triggered packaging and artifact upload |
.github/workflows/version-check.yml |
CI check for version/CHANGELOG consistency |
agents/basecoat-60-workflow-release-manager.agent.md |
Agent that automates this process |