Create and manage release PRs against master branch. Use this when preparing releases, bumping versions, resolving merge conflicts, and publishing GitHub releases. Handles the full release workflow including merging release into master, version bumping in pyproject.toml, running uv lock, and creating GitHub releases.
Manages the full release workflow including version bumps, PR creation, and GitHub releases.
/plugin marketplace add diversioteam/agent-skills-marketplace/plugin install backend-release@diversiotechThis skill is limited to using the following tools:
Manages the full release workflow for Django4Lyfe backend releases to production.
git fetch origin master release
# PRIMARY CHECK — are there actual code differences between the branches?
git diff --stat origin/master origin/release
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:
# Get the merge date of the last release PR (the definitive cutoff).
# The release PR's merge to master is the exact moment `git merge origin/release`
# captured the release branch state. Anything merged to release BEFORE that
# moment was included; anything AFTER is genuinely new.
LAST_RELEASE_DATE=$(gh pr list --base master --state merged --limit 100 \
--json number,title,mergedAt \
--jq '[.[] | select(.title | test("^(Release|Hotfix)"))] | sort_by(.mergedAt) | last | .mergedAt // empty' \
2>/dev/null || echo "")
# List PRs merged to release since that date
if [ -n "${LAST_RELEASE_DATE}" ]; then
gh pr list --base release --state merged --limit 100 --json number,title,mergedAt \
--jq "[.[] | select(.mergedAt > \"${LAST_RELEASE_DATE}\")] | sort_by(.mergedAt) | .[] | \"#\\(.number): \\(.title)\""
else
# No previous release — list recent merged PRs as candidates
gh pr list --base release --state merged --limit 20 --json number,title \
--jq '.[] | "#\(.number): \(.title)"'
fi
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.
# 1. Create branch from master
git checkout -b releases/YYYY.MM.DD[-N] origin/master
# 2. Merge release into it
git merge origin/release --no-edit
# 3. Bump version in pyproject.toml
# Format: YYYY.MM.DD for first release, YYYY.MM.DD-N for subsequent releases
# 4. Update lock file
uv lock
# 5. Commit version bump
git add pyproject.toml uv.lock
git commit -m "Version bump to YYYY.MM.DD[-N]"
# 6. Push and create PR
git push -u origin releases/YYYY.MM.DD[-N]
gh pr create --base master --title "Release: DDth Month YYYY" --body "..."
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.
| Scenario | Version Format | Example |
|---|---|---|
| First release of day | YYYY.MM.DD | 2026.01.21 |
| Second release | YYYY.MM.DD-2 | 2026.01.21-2 |
| Third release | YYYY.MM.DD-3 | 2026.01.21-3 |
| Hotfix release | YYYY.MM.DD or YYYY.MM.DD-N | 2026.01.21 |
Title patterns:
Release: 21st January 2026Release 2: 21st January 2026Hotfix Release: 21st January 2026Body format:
- https://github.com/DiversioTeam/Django4Lyfe/pull/XXXX
- https://github.com/DiversioTeam/Django4Lyfe/pull/YYYY
If the merge has conflicts:
# 1. Edit conflicted files to keep correct changes
# 2. For uv.lock conflicts, regenerate:
git checkout --theirs uv.lock
uv lock
# 3. Stage resolved files
git add <resolved-files>
# 4. Complete merge
git commit -m "Merge origin/release into releases/YYYY.MM.DD"
After PR is merged to master, create a GitHub release.
IMPORTANT: Merge Strategy — Release PRs to master MUST be merged using
"Create a merge commit" (not squash). Squash merging breaks commit ancestry
and causes master..release to grow unboundedly. If GitHub is configured to
allow multiple merge strategies, always select "Create a merge commit" for
release PRs.
gh pr view <PR_NUMBER> --json state,mergeCommit,mergedAt
# Should show: "state": "MERGED"
gh release list --limit 5
# Get the PR body which contains the list of included PRs
gh pr view <PR_NUMBER> --json body,title
gh release create YYYY.MM.DD[-N] \
--title "Release Title" \
--notes "$(cat <<'EOF'
- https://github.com/DiversioTeam/Django4Lyfe/pull/XXXX
- https://github.com/DiversioTeam/Django4Lyfe/pull/YYYY
EOF
)" \
--target master
| Release Type | Tag | Title |
|---|---|---|
| First of day | 2026.01.21 | January 21st 2026 |
| Second release | 2026.01.21-2 | Release 2: January 21st 2026 |
| Third release | 2026.01.21-3 | Release 3: January 21st 2026 |
| Hotfix | 2026.01.21 | Hotfix Release: January 21st 2026 |
gh release list --limit 3
# Or view specific release:
gh release view YYYY.MM.DD[-N]
# 1. Check PR is merged
gh pr view 2608 --json state,mergeCommit,mergedAt
# 2. Create release (using heredoc for multi-line notes)
gh release create 2026.01.21 \
--title "January 21st 2026" \
--notes "$(cat <<'EOF'
- https://github.com/DiversioTeam/Django4Lyfe/pull/2607
EOF
)" \
--target master
# 3. Verify
gh release list --limit 3
This step is mandatory after every release PR merge. It keeps the branches
in sync so that git diff --stat origin/master origin/release is clean and
future releases start from a consistent baseline.
git fetch origin
git checkout release
git merge origin/master --no-edit
git push origin release
Why this matters: After a release PR merges into master, master has a merge
commit and a version-bump commit that release doesn't. Without merge-back,
git diff --stat origin/master origin/release shows the version bump as a
pending difference, and the next git merge origin/release will conflict on
pyproject.toml / uv.lock. The merge-back keeps both branches aligned.
Before creating a release PR, verify:
Ruff formatting passes:
./.security/ruff_pr_diff.sh
If it fails, fix with:
.bin/ruff format <file>
Active Python type gate passes (strict):
ty (mandatory if configured)pyrightmypyRLS policies for new models:
# Check status
.bin/django optimo_bootstrap_support_shell_rls
# Apply if needed (safe for production)
.bin/django optimo_bootstrap_support_shell_rls --apply
When reporting release status:
Created: https://github.com/DiversioTeam/Django4Lyfe/pull/XXXX
**Summary:**
- Version: `YYYY.MM.DD[-N]`
- Title: "Release: DDth Month YYYY"
- Target: `master`
- Conflicts: None / Resolved
**Included PRs:**
- #XXXX - Description
- #YYYY - Description
When listing releases:
| Release | Tag | PRs Included |
|---------|-----|--------------|
| Release Name | `tag` | #PR1, #PR2 |
git log master..release works correctly. Cherry-picking creates duplicate commits with different SHAs, causing stale "pending" commits that were already shipped.gh pr view2026.01.21-2 = tag 2026.01.21-2git merge origin/master --no-edit on release after every release PR merge. Without this, the version bump stays only on master, causing git diff --stat to show false differences and the next release merge to conflict.Here's a complete example of releasing PR #2607:
# 1. Check what needs releasing (diff is the source of truth)
git fetch origin master release
git diff --stat origin/master origin/release
# Output shows files changed — confirms there IS something to release
# 2. Identify which PRs are included (uses release PR merge date as cutoff)
LAST_RELEASE_DATE=$(gh pr list --base master --state merged --limit 100 \
--json number,title,mergedAt \
--jq '[.[] | select(.title | test("^(Release|Hotfix)"))] | sort_by(.mergedAt) | last | .mergedAt // empty' \
2>/dev/null || echo "")
gh pr list --base release --state merged --limit 100 --json number,title,mergedAt \
--jq "[.[] | select(.mergedAt > \"${LAST_RELEASE_DATE}\")] | sort_by(.mergedAt) | .[] | \"#\\(.number): \\(.title)\""
# Output: #2607: #GH-4420: Include alert notifications in Slack App Home
# 3. Check today's date
date # Wed Jan 21 2026
# 4. Create branch from master and merge release
git checkout -b releases/2026.01.21 origin/master
git merge origin/release --no-edit
# 5. Bump version
sed -i '' 's/version = ".*"/version = "2026.01.21"/' pyproject.toml
# 6. Update lock file
uv lock
# 7. Commit version bump
git add pyproject.toml uv.lock
git commit -m "Version bump to 2026.01.21"
# 8. Push and create PR
git push -u origin releases/2026.01.21
gh pr create --base master \
--title "Release: 21st January 2026" \
--body "- https://github.com/DiversioTeam/Django4Lyfe/pull/2607"
# 9. After PR is merged, publish GitHub release
gh pr view 2608 --json state # Verify merged
gh release create 2026.01.21 \
--title "January 21st 2026" \
--notes "- https://github.com/DiversioTeam/Django4Lyfe/pull/2607" \
--target master
# 10. Merge master back into release (MANDATORY)
git fetch origin
git checkout release
git merge origin/master --no-edit
git push origin release
# 11. Verify release and branch sync
gh release list --limit 3
git diff --stat origin/master origin/release # Should be empty
# Check what's pending release (source of truth — compares actual file contents)
git fetch origin master release && git diff --stat origin/master origin/release
# Identify new PRs (uses last release PR's merge date as cutoff)
LAST_RELEASE_DATE=$(gh pr list --base master --state merged --limit 100 \
--json number,title,mergedAt \
--jq '[.[] | select(.title | test("^(Release|Hotfix)"))] | sort_by(.mergedAt) | last | .mergedAt // empty' \
2>/dev/null || echo "") && \
gh pr list --base release --state merged --limit 100 --json number,title,mergedAt \
--jq "[.[] | select(.mergedAt > \"${LAST_RELEASE_DATE}\")] | sort_by(.mergedAt) | .[] | \"#\\(.number): \\(.title)\""
# Check current version
grep '^version' pyproject.toml
# List recent releases
gh release list --limit 10
# Check PR status
gh pr view <NUMBER> --json state,mergeable,mergeCommit
# View release details
gh release view <TAG> --json body,tagName,name
# For uv.lock conflicts:
git checkout --theirs uv.lock
uv lock
git add uv.lock
# For code conflicts: resolve manually, then:
git add <resolved-files>
git commit # Completes the merge
# Edit pyproject.toml to correct version
uv lock
git add pyproject.toml uv.lock
git commit --amend -m "Version bump to correct-version"
git push --force-with-lease # Only if not yet reviewed
gh pr close <NUMBER>
# Create new PR with correct base
gh pr create --base master ...
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.