Imported from umbraco/Umbraco.AI (
.claude/skills/post-release-cleanup/SKILL.md). Install upstream withnpx skills add umbraco/Umbraco.AI --skill post-release-cleanup. Copyright stays with the author.
Post-Release Cleanup
You are the orchestrator for post-release cleanup in the Umbraco.AI repository.
Task
After a release has been deployed and tagged by the release pipeline, merge the release/hotfix branch back into the appropriate vN/main and vN/dev, bump version.json on vN/dev so nightly builds produce versions higher than the released version, check whether any GitHub issues or Azure DevOps work items need a release-confirmation comment or closing, and optionally clean up the branch. If this is the first release of a new major version, also create the new version's dev/main branches and update the GitHub default branch.
Why This Matters
Without the version bump on vN/dev, NBGV + Umbraco.GitVersioning.Extensions produces packages like 1.5.0--preview.4.gabcdef0 which sorts lower than the stable 1.5.0 in SemVer — making nightlies useless for testing.
Workflow
Phase 1: Detect Release Context
-
Check current branch — verify it matches
vN/release/*orvN/hotfix/*:git branch --show-currentIf not on a versioned release/hotfix branch, ask the user to specify which branch to process.
-
Extract the version prefix from the branch name. For example:
v18/release/2026.06.5→ prefix =v18, major =18v17/hotfix/2026.06.1→ prefix =v17, major =17
-
Fetch latest tags and remote state:
git fetch origin --tags -
Find product version tags on this branch that are not yet on
vN/main:merge_base=$(git merge-base origin/${prefix}/main HEAD) commits=$(git rev-list $merge_base..HEAD) for tag in $(git tag --list '*@*'); do tag_commit=$(git rev-parse "$tag^{commit}" 2>/dev/null) if echo "$commits" | grep -q "$tag_commit"; then echo "$tag" fi done -
Parse product names and versions from tags (e.g.,
Umbraco.AI@18.1.0→ product=Umbraco.AI, version=18.1.0). -
Detect the released major from the tag versions (e.g.
18from18.1.0). -
Present findings to user for confirmation:
Found released products on this branch (v18/release/2026.06.5): - Umbraco.AI @ 18.1.0 - Umbraco.AI.OpenAI @ 18.1.0 Proceed with merge and version bump? [Yes/Cancel]If NO tags are found, warn the user:
⚠ No product version tags found on this branch. This usually means the release pipeline hasn't run yet, or tags haven't been pushed. Options: - Wait for the release pipeline to complete and try again - Proceed anyway (merge only, skip version bump) - Cancel
Phase 2: Merge to vN/main
-
Confirm with user before merging.
-
Store the release branch name:
release_branch=$(git branch --show-current) # e.g. v18/release/2026.06.5 -
Checkout and merge:
git checkout ${prefix}/main git pull origin ${prefix}/main git merge origin/$release_branch --no-ff -m "Merge $release_branch into ${prefix}/main" -
Push:
git push origin ${prefix}/mainThe post-merge hook will auto-delete
release-manifest.jsonif present and commit the cleanup.
Phase 3: Merge vN/main to vN/dev
-
git checkout ${prefix}/dev git pull origin ${prefix}/dev git merge ${prefix}/main --no-ff -m "Merge ${prefix}/main into ${prefix}/dev" -
Handle merge conflicts — if conflicts occur (likely in
version.jsonorCHANGELOG.md):- For
version.json: keep the higher version (overwritten in Phase 4 anyway) - For
CHANGELOG.md: keep both sets of entries (combine) - For
release-manifest.json: delete the file (must not exist on dev) - Ask the user for help with any other conflicts
- For
-
Push:
git push origin ${prefix}/devThe post-merge hook will auto-delete
release-manifest.jsonif present and commit the cleanup.
Phase 4: Bump Versions on vN/dev
For each released product detected in Phase 1:
-
Read the current
<Product>/version.json -
Compute the patch bump:
- Stable version:
18.1.0→18.1.1 - Dotted pre-release:
18.0.0-beta.2→18.0.0-beta.3(increment the numeric segment) - Pre-release without numeric segment:
18.0.0-alpha→18.0.0-alpha.1 - Legacy non-dotted pre-release (
18.0.0-beta2→18.0.0-beta3): increment in place — do not convert to dotted. OnlyUmbraco.AI.Search/Umbraco.AI.Automateare on this grandfathered scheme.
- Stable version:
-
Update the
"version"field inversion.jsonusing the Edit tool. -
After all products are bumped, commit and push:
git add */version.json git commit -m "chore(release): Bump dev versions after release Products bumped: - Umbraco.AI: 18.1.0 → 18.1.1 - Umbraco.AI.OpenAI: 18.1.0 → 18.1.1 Co-Authored-By: Claude <noreply@anthropic.com>" git push origin ${prefix}/dev
Phase 5: Major Version Cutover (conditional)
Only run this phase if the released major is a NEW major — i.e., the GitHub default branch points to a lower major version than what was just released.
Detect whether a cutover is needed
# Get the current GitHub default branch
gh api repos/umbraco/Umbraco.AI --jq '.default_branch'
# e.g. → "v18/dev"
Parse the major from the default branch (e.g. v18/dev → 18). Compare with the released major:
- Released major equal to default branch major → no cutover needed, skip this phase.
- Released major greater than default branch major → cutover needed (e.g. default is
v18/dev, just releasedv19.0.0).
Cutover steps
Let new_prefix = v{released_major} (e.g. v19).
-
Check whether
vN+1/devandvN+1/mainalready exist:git ls-remote origin refs/heads/${new_prefix}/dev refs/heads/${new_prefix}/main -
Create missing branches if they do not exist yet:
${new_prefix}/main— create from the tip of${prefix}/main(the freshly merged stable state):git push origin ${prefix}/main:refs/heads/${new_prefix}/main${new_prefix}/dev— create from${prefix}/dev(after version bumps):git push origin ${prefix}/dev:refs/heads/${new_prefix}/dev
If both already exist (dev team created them ahead of the release), skip creation.
-
Update the GitHub default branch:
gh api repos/umbraco/Umbraco.AI -X PATCH -f default_branch=${new_prefix}/dev \ --jq '.default_branch'Confirm the returned value matches
${new_prefix}/dev. -
Inform the user — they will need to update their local checkout:
✅ Major version cutover complete! New branches created: - ${new_prefix}/main (from ${prefix}/main) - ${new_prefix}/dev (from ${prefix}/dev) GitHub default branch updated: ${prefix}/dev → ${new_prefix}/dev Developers should run: git fetch origin git checkout ${new_prefix}/dev
Phase 6: Check Issue Tracker
Before cleanup, check whether any tracked issues need closing or a release-confirmation comment now that the fix is actually live.
-
Find issues referenced by the merged PRs. For each PR that landed on the release/hotfix branch (from Phase 1's commit range, or from PR numbers you already know from preparing this release), check
mcp__github__issue_read(get) on anyFixes #N/Closes #Nissue numbers mentioned in commit messages or PR bodies. -
If an issue is already closed (GitHub auto-closes on merge to the default branch — which happens well before the fix actually reaches users via a release), post a follow-up comment stating the actual released version(s) now available, e.g.:
Fixed in 18.1.4 (and backported to 17.1.4 for the v17 line), both now available on NuGet.Posting a comment is public content — always confirm with the user before posting, per repo-wide safety norms. Don't post if the user doesn't respond or declines.
-
If an issue is still open despite the fix being released, ask the user whether to close it (with the same release-confirmation comment) rather than closing it silently.
-
Search for duplicate open issues describing the same symptoms (
mcp__github__search_issues) that the fix likely also resolves. Surface any candidates to the user — don't close or comment on them without explicit confirmation, since a symptom match isn't proof of the same root cause. -
Check the Azure DevOps AI Team backlog for related open work items. Scope every query to the
Umbraco AItag (per rootCLAUDE.md— theD-Team Trackerproject is shared across teams, so an unscoped search returns cross-product noise) and match on keywords from the fix's actual symptoms/root cause (component names, error strings, issue title) rather than generic terms like "agent" or "file" alone, which over-match the whole backlog:mcp__azure-devops__wit_query (action: wiql), e.g.: SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = 'D-Team Tracker' AND [System.Tags] CONTAINS 'Umbraco AI' AND [System.State] NOT IN ('Closed','Removed','Done') AND ([System.Title] CONTAINS '<specific term>' OR ...)Report any real matches to the user; don't change work item state without confirmation.
-
If nothing is found in either tracker, say so explicitly in the summary (Phase 8) rather than omitting the check — this confirms the check ran, not that it was skipped.
Phase 7: Cleanup (Optional)
-
Ask the user if they want to delete the release/hotfix branch (local + remote):
Delete the release branch '$release_branch'? - Local and remote - Local only - Skip (keep branch) -
If deleting:
git branch -d $release_branch git push origin --delete $release_branch -
Return to the appropriate dev branch (the new one if a cutover occurred, otherwise
${prefix}/dev):git checkout ${active_dev}
Phase 8: Summary
Present a summary of everything that was done:
✅ Post-release cleanup complete!
Merged:
- $release_branch → ${prefix}/main
- ${prefix}/main → ${prefix}/dev
Version bumps on ${prefix}/dev:
- Umbraco.AI: 18.1.0 → 18.1.1
- Umbraco.AI.OpenAI: 18.1.0 → 18.1.1
[If major cutover:]
Major version cutover:
- Created v19/main from v18/main
- Created v19/dev from v18/dev
- GitHub default branch: v18/dev → v19/dev
Issue tracker check:
- GitHub #324: already closed, posted release-confirmation comment
- Azure DevOps: no related open items found
Branch cleanup: [deleted/kept]
Nightly builds on ${prefix}/dev will now produce versions higher than the released versions.
Version Bump Logic
Stable Versions
Simply increment the patch version:
18.1.0→18.1.118.0.0→18.0.1
Pre-release Versions
New prerelease lines use the dotted form -{stage}.N (-alpha.1, -beta.1, -rc.1) — never non-dotted -beta1, which sorts incorrectly past 9 (beta10 < beta9). See root CLAUDE.md → "Prerelease versioning".
Increment the numeric portion of the pre-release identifier:
18.0.0-beta.2→18.0.0-beta.318.0.0-rc.1→18.0.0-rc.218.0.0-alpha→18.0.0-alpha.1(append.1if no numeric segment)18.0.0-beta2→18.0.0-beta3(legacy non-dotted — increment in place, never dotify)
Important Notes
- Always fetch tags first — the release pipeline creates tags asynchronously after deploy
- Use
--no-ffmerges — preserves the merge commit for clear history - Post-merge hooks handle
release-manifest.jsoncleanup — don't manually delete it - version.json only has a
"version"field — update only that field, preserve all other properties - Both
vN/release/*andvN/hotfix/*branches are supported — the workflow is identical - If no tags are found, the user can still proceed with merge-only (skip Phase 4)
- Major cutover only triggers when the released major > the default branch major — a patch or minor release on the current latest version never triggers it
Error Recovery
- If the merge to
vN/mainfails (conflicts), help the user resolve conflicts before continuing - If the push fails, check if the branch is protected and advise accordingly
- If version.json has unexpected format, show the user and ask how to proceed
- Never force-push — if push is rejected, pull and retry the merge
- If the GitHub default branch update fails, confirm admin permissions on the repo (
gh api repos/umbraco/Umbraco.AI --jq '.permissions')