On this page
Release Manager
Create and manage release PRs against master branch. Handles the full release workflow including merging release into master, version bumping in pyproject.toml, running uv lock, and creating GitHub releases.
Overview
Create and manage release PRs against master branch. Handles the full release workflow including merging release into master, version bumping in pyproject.toml, running uv lock, and creating GitHub releases.
This skill ships inside the Backend Release Manager plugin and can be installed through the Claude Code marketplace or directly in Codex from its skill path.
Parent Surface
Parent docs: Backend Release Manager
Related wrapper commands from the parent plugin:
/backend-release:check/backend-release:create/backend-release:publish When to Use This Skill
- Creating promotion PRs from dev → release to move a staging candidate onto release
- Creating release PRs from release → master to move a production candidate onto master
- Preparing hotfix releases
- Bumping versions in pyproject.toml
- Resolving merge conflicts between branches
- Publishing GitHub releases after PRs are merged
- Checking what commits are pending promotion or release
Branch Model
feature PRs -> dev (validation only)
promotion PRs -> release (move staging candidate)
origin/release head -> local-ci -> validated deploy helper -> staging deploy
release PRs -> master (move production candidate)
origin/master head -> local-ci -> validated deploy helper -> production deploy Repo Feature Detection
- local-ci support: command -v local-ci succeeds and repo root contains .local-ci.toml
- validated deploy helper: scripts/deploy/trigger_validated_backend_deploy.sh exists
Core Workflow
Before a production release, promote reviewed changes from dev to release. Merging the promotion PR does not deploy staging automatically.
Routine feature PRs still merge into dev where CI validates but never deploys. The promotion PR is the intentional step that moves the candidate onto release.
A local-ci run on the open promote/* branch is still only a preflight. In Django4Lyfe today that preflight can run the full parity lanes, but it still does not replace exact target-head validation after merge. If the preflight fails, stop and dig into the harness/code before calling the promotion ready. Exact release parity still happens on the clean merged origin/release head.
After merge: use the exact origin/release head from a clean checkout. If the validated deploy helper exists, prefer it — it runs local-ci and then triggers staging deploy. Otherwise, if the repo supports local-ci, run local-ci manually and follow the repo-local deploy steps.
Validate staging before proceeding. If issues are found, fix them on dev and create a new promotion PR instead of patching release directly.
git diff --stat compares the actual tree state (file contents), not commit history. It is the only reliable way to determine whether there is something to release. If the output is empty, there is nothing to release — stop here.
If there ARE differences, identify which PRs they belong to. Use GitHub's PR metadata (merge timestamps), not git commit ancestry:
Why the release PR's mergedAt instead of publishedAt? GitHub release publishedAt is when a human clicks "Publish" — which can be minutes or hours after the release PR actually merges. PRs merged to release in that gap would be missed on the next check. The release PR's mergedAt is the definitive cutoff because git merge origin/release captures the exact state of the release branch at that moment.
Why GitHub metadata instead of git log? All git log-based approaches (master..release, --cherry-pick, --first-parent with tags) can return stale results due to historical cherry-pick artifacts and because release tags live on master's ancestry, not release's first-parent chain. PR merge timestamps from GitHub are immune to git ancestry issues.
Merge the release branch into a branch from master. This preserves commit ancestry so that git log master..release works correctly after the PR merges.
Merging the release PR does not deploy production automatically. A local-ci run on the open release -> master PR head is only a preflight. In Django4Lyfe today that preflight can run the full parity lanes, but exact origin/master validation still happens after merge. If it fails, stop and dig into it before calling the release ready. After the PR lands, use the exact origin/master head from a clean checkout. If the validated deploy helper exists, prefer it — it runs local-ci and then triggers production deploy. Otherwise, if the repo supports local-ci, run local-ci manually and follow the repo-local deploy steps.
Why merge instead of cherry-pick? Cherry-picking creates new commits with different SHAs. Even with merge-back, git log master..release permanently shows the original commits as "pending" because git compares SHAs, not patches. Merging preserves the original commit objects so master and release share the same ancestry. After the release PR merges to master, git log master..release correctly shows only genuinely new commits.
See detailed-procedures.md for:
This step is mandatory after every release PR merge. It keeps all three branches in sync so future work starts from a consistent baseline.
First principles — why sync all three?
After a production release, the branches look like this:
The sync cascade fixes this:
Without syncing to release: the next release PR sees a stale diff (git diff --stat origin/master origin/release shows the version bump as "pending") and the release merge conflicts on pyproject.toml/uv.lock.
Without syncing to dev: the integration branch drifts from production, and subsequent feature branches are developed against code that doesn't match what's actually running in production.
- Version numbering conventions (YYYY.MM.DD[-N])
- PR title patterns (Promotion, Release, Hotfix)
- Resolving merge conflicts
- Publishing GitHub releases (merge strategy, verification, creation)
git fetch origin dev release
git diff --stat origin/release origin/dev
git checkout -b promote/YYYY.MM.DD[-N] origin/release
git merge origin/dev --no-edit
git push -u origin promote/YYYY.MM.DD[-N]
gh pr create --base release --title "Promotion: DDth Month YYYY" --body "..." git fetch origin
git worktree add ../backend-release origin/release
cd ../backend-release
CIRCLECI_TOKEN=... scripts/deploy/trigger_validated_backend_deploy.sh Resources
Declared allowed tools:
BashReadEditGrepGlob References
detailed-procedures.md
Installation
Switch between Claude Code and Codex, then copy the install command for the runtime you use.
claude plugin marketplace add DiversioTeam/agent-skills-marketplace
claude plugin install backend-release@diversiotech CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
python3 "$CODEX_HOME/skills/.system/skill-installer/scripts/install-skill-from-github.py" \
--repo DiversioTeam/agent-skills-marketplace \
--path plugins/backend-release/skills/release-manager Invocation:
/backend-release:check
/backend-release:create
/backend-release:publish name: release-manager