| name | release |
| description | Release packages to GitHub via uvr. Use when user says "release", "publish packages", "cut a release", or wants to publish new package versions. |
| disable-model-invocation | true |
Releasing Packages
Prerequisites: uvr (uv tool install uv-release-monorepo) and gh.
For first-time setup, scaffold the workflow with uvr workflow init (see references/cmd-init.md). To install the Claude skills into your project, see references/cmd-skill-init.md.
If the project has existing CI checks (tests, linting, etc.) that aren't yet wired into the release workflow, see references/custom-jobs.md before your first release.
0. Self-hosted runners
Before dispatching, ensure all runners are up:
uv run quicksand-runners start
This starts the local macOS runner and the cloud runners (x64, arm64, win). The command is idempotent — safe to run if runners are already up.
Troubleshooting:
- "A session for this runner already exists": Another runner process is already running. Kill it first:
pkill -f Runner.Listener then retry.
- "file already exists" error in Set up job: Stale
_diag/ files from a previous run. Clean with rm -rf gha-runners/local/_diag.
- NEVER start multiple runner processes. Each
run.sh invocation creates a persistent process. Check with ps aux | grep Runner.Listener before starting a new one.
1. Branch
You must not be on main. If you are, create a release branch and switch to it.
The working tree must be clean. Run git status. If dirty, ask the user whether to stash, commit, or abort.
2. Preview Changes
uvr release
This prints the plan and prompts Dispatch release? [y/N]. Decline (N) to preview without dispatching. See references/cmd-release.md for all flags.
Present the output to the user. For each changed package, show:
- The package name and its new version
- Why it changed (summarize the relevant commits)
Ask the user whether any packages need a minor bump instead of patch. Patch is the default — bump minor for new features, new public API, or breaking changes:
uv version --bump minor --directory packages/<package-name>
3. Review
For each changed package, verify its public API against its docs:
- Read the current public API
- Check each item on this list:
- Do the package's docstrings accurately reflect the exported types and internal functionality? Audit natural language descriptions, argument lists, returns notes, code examples, and references.
- Do all docs (READMEs, docs/, etc) accurately reflect the public API and internal functionality of the package? Audit natural language descriptions, code examples, and references.
- Fix any discrepancies before continuing
4. CHANGELOG.md and Release Notes
Update CHANGELOG.md on the release branch before committing. Format:
For each package, review the commits since the baseline. Use the DIFF FROM column from the uvr release --dry-run output — this is the actual tag the planner diffs against, which may differ from the PREVIOUS version (e.g. a -base tag for dev cycles vs a release tag):
git log --oneline <DIFF_FROM_TAG>..HEAD -- packages/<pkg>
Example entry:
## [v0.3.4] - 2026-03-18
### Fixed
- CIFS directory listing filename truncation
Section headers: Added, Changed, Deprecated, Removed, Fixed, Security.
For each changed package, also write release notes to .uvr/release-notes/<pkg>/<version>.md. This directory is gitignored — notes are ephemeral and consumed at release time.
Review the commits since the last release:
git log --oneline <pkg>/v<last-version>..HEAD -- packages/<pkg>
Draft user-facing release notes — don't dump commit messages, write prose that helps users understand what's new, changed, or fixed. Present the draft to the user for approval before writing the file.
mkdir -p .uvr/release-notes/my-lib
cat > .uvr/release-notes/my-lib/1.2.0.md << 'EOF'
Dashboard support and stability improvements.
- New `Widget` class for building dashboards
- Parser no longer crashes on empty input
EOF
If no --release-notes flag is provided, the release gets a minimal header only.
5. Dispatch
git add -A
git commit -m "Release v<VERSION>"
git push -u origin "$(git branch --show-current)"
uvr release
Or with custom release notes:
uvr release \
--release-notes my-lib @.uvr/release-notes/my-lib/1.2.0.md
When prompted Dispatch release? [y/N], answer y.
If uvr release says dependency pins were updated, commit those first and re-run:
git add -A
git commit -m "chore: update dep pins"
git push
uvr release
6. Monitor
gh run list --workflow=release.yml --limit=1
gh run watch <RUN_ID> --exit-status
If the workflow fails, check which job failed:
gh run view <RUN_ID> --log-failed
If the failure is early (build broke), fix the issue and re-dispatch from scratch:
git add <files>
git commit -m "Fix: <description>"
git push
uvr release
If a later job failed but earlier jobs succeeded, use --skip-to and --reuse-* flags to resume without re-running what already passed. See references/troubleshooting.md#resuming-a-partially-failed-release for the full decision tree.
7. Verify
gh release list --limit 15
If something goes wrong, see references/troubleshooting.md.
8. Merge
ALWAYS merge stable release branches back to main:
git checkout main
git pull --rebase
git fetch origin <release-branch>
git merge --no-ff origin/<release-branch> -m "Merge <release-branch>"
git push
Why git fetch and origin/<release-branch>: the post-release bump job runs in CI and pushes a chore: bump to next dev versions commit (and creates the -base baseline tag) on the remote release branch after uvr release returns. Your local copy of the branch does not have that commit. Merging your local tip leaves main stuck at the released version instead of the next dev version, and uvr status will then resolve DIFF FROM to the previous release tag instead of the new baseline. Fetch and merge the remote tip.
NEVER merge pre-release branches back to main. Stay on the branch through the pre-release cycle, then merge after the stable release.
TAKE CARE merging post-release branches back to main — they branch from an old tag, so pyproject.toml versions will conflict. You may need to accept main's versions or cherry-pick just the fix commits. See references/post-releases.md.
9. Finally
If packages were released successfully, clean up release notes:
rm -rf .uvr/release-notes/
Example
User says: "Let's release the new changes"
- Verify not on main, create a release branch
- Run
uvr release, decline the prompt — shows my-lib is dirty (2 commits: added export, fixed parser)
- Present to user: "my-lib will bump 0.2.1 -> 0.2.2 (patch). It has a new public export — should this be a minor bump instead?"
- User says "yes, bump minor" — run
uvr bump --package my-lib --minor
- Review docstrings and docs against current API — new
Parser class exported but not documented. Fix docs.
- Draft release notes: "Added
Parser class for structured input handling. Fixed crash on empty input." Present to user for approval.
- Write approved notes to
.uvr/release-notes/my-lib/0.3.0.md
- Commit, push, run
uvr release --release-notes my-lib @.uvr/release-notes/my-lib/0.3.0.md and confirm
- Monitor workflow, verify GitHub releases
- Merge release branch back to main, clean up
.uvr/release-notes/
References
Commands:
<<<<<<< current
references/cmd-version.md — read, set, or bump package versions
incoming
references/cmd-init.md — scaffold the release workflow
references/cmd-validate.md — check release.yml against schema
references/cmd-release.md — plan and dispatch a release (all flags)
references/cmd-runners.md — manage per-package build runners
references/cmd-install.md — install from GitHub releases
references/cmd-skill-init.md — copy Claude skills into project
Guides:
references/pipeline.md — the three core jobs (build, publish, bump)
references/release-plan.md — what the release plan JSON contains
references/custom-jobs.md — how to add your own jobs to the workflow
references/dev-releases.md — publishing .devN versions for testing
references/post-releases.md — correcting an already-released version
references/troubleshooting.md — common problems and fixes