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/* or vN/hotfix/*:
git branch --show-current
If 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 = 18
v17/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. 18 from 18.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}/main
The post-merge hook will auto-delete release-manifest.json if 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.json or CHANGELOG.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
Push:
git push origin ${prefix}/dev
The post-merge hook will auto-delete release-manifest.json if 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. Only Umbraco.AI.Search/Umbraco.AI.Automate are on this grandfathered scheme.
Update the "version" field in version.json using 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 released v19.0.0).
Cutover steps
Let new_prefix = v{released_major} (e.g. v19).
Check whether vN+1/dev and vN+1/main already exist:
git ls-remote origin refs/heads/${new_prefix}/dev refs/heads/${new_prefix}/main
Create missing branches if they do not exist yet:
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 any Fixes #N/Closes #N issue 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 AI tag (per root CLAUDE.md — the D-Team Tracker project 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.1
18.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.3
18.0.0-rc.1 → 18.0.0-rc.2
18.0.0-alpha → 18.0.0-alpha.1 (append .1 if 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-ff merges — preserves the merge commit for clear history
- Post-merge hooks handle
release-manifest.json cleanup — don't manually delete it
- version.json only has a
"version" field — update only that field, preserve all other properties
- Both
vN/release/* and vN/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/main fails (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')
1---2name: post-release-cleanup3description: Merges a release or hotfix branch back into vN/main and vN/dev, bumps version.json on vN/dev so nightly builds produce versions higher than the released version, checks GitHub issues and the Azure DevOps AI Team backlog for anything needing a release-confirmation comment or closing, and optionally deletes the release branch. If the release is a new major version, creates the new vN+1/dev and vN+1/main branches and updates the GitHub default branch. Use after a release has been deployed and tagged.4---56# Post-Release Cleanup78You are the orchestrator for post-release cleanup in the Umbraco.AI repository.910## Task1112After 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.1314## Why This Matters1516Without 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.1718## Workflow1920### Phase 1: Detect Release Context21221. **Check current branch** — verify it matches `vN/release/*` or `vN/hotfix/*`:23 ```bash24 git branch --show-current25 ```26 If not on a versioned release/hotfix branch, ask the user to specify which branch to process.27282. **Extract the version prefix** from the branch name. For example:29 - `v18/release/2026.06.5` → prefix = `v18`, major = `18`30 - `v17/hotfix/2026.06.1` → prefix = `v17`, major = `17`31323. **Fetch latest tags and remote state:**33 ```bash34 git fetch origin --tags35 ```36374. **Find product version tags on this branch** that are not yet on `vN/main`:38 ```bash39 merge_base=$(git merge-base origin/${prefix}/main HEAD)40 commits=$(git rev-list $merge_base..HEAD)41 for tag in $(git tag --list '*@*'); do42 tag_commit=$(git rev-parse "$tag^{commit}" 2>/dev/null)43 if echo "$commits" | grep -q "$tag_commit"; then44 echo "$tag"45 fi46 done47 ```48495. **Parse product names and versions** from tags (e.g., `Umbraco.AI@18.1.0` → product=`Umbraco.AI`, version=`18.1.0`).50516. **Detect the released major** from the tag versions (e.g. `18` from `18.1.0`).52537. **Present findings to user** for confirmation:54 ```55 Found released products on this branch (v18/release/2026.06.5):56 - Umbraco.AI @ 18.1.057 - Umbraco.AI.OpenAI @ 18.1.05859 Proceed with merge and version bump? [Yes/Cancel]60 ```6162 If NO tags are found, warn the user:63 ```64 ⚠ No product version tags found on this branch.65 This usually means the release pipeline hasn't run yet, or tags haven't been pushed.6667 Options:68 - Wait for the release pipeline to complete and try again69 - Proceed anyway (merge only, skip version bump)70 - Cancel71 ```7273### Phase 2: Merge to vN/main74751. **Confirm with user** before merging.76772. **Store the release branch name:**78 ```bash79 release_branch=$(git branch --show-current) # e.g. v18/release/2026.06.580 ```81823. **Checkout and merge:**83 ```bash84 git checkout ${prefix}/main85 git pull origin ${prefix}/main86 git merge origin/$release_branch --no-ff -m "Merge $release_branch into ${prefix}/main"87 ```88894. **Push:**90 ```bash91 git push origin ${prefix}/main92 ```9394 The post-merge hook will auto-delete `release-manifest.json` if present and commit the cleanup.9596### Phase 3: Merge vN/main to vN/dev97981. ```bash99 git checkout ${prefix}/dev100 git pull origin ${prefix}/dev101 git merge ${prefix}/main --no-ff -m "Merge ${prefix}/main into ${prefix}/dev"102 ```1031042. **Handle merge conflicts** — if conflicts occur (likely in `version.json` or `CHANGELOG.md`):105 - For `version.json`: keep the **higher** version (overwritten in Phase 4 anyway)106 - For `CHANGELOG.md`: keep **both** sets of entries (combine)107 - For `release-manifest.json`: delete the file (must not exist on dev)108 - Ask the user for help with any other conflicts1091103. **Push:**111 ```bash112 git push origin ${prefix}/dev113 ```114115 The post-merge hook will auto-delete `release-manifest.json` if present and commit the cleanup.116117### Phase 4: Bump Versions on vN/dev118119For each released product detected in Phase 1:1201211. **Read** the current `<Product>/version.json`1221232. **Compute the patch bump:**124 - Stable version: `18.1.0` → `18.1.1`125 - Dotted pre-release: `18.0.0-beta.2` → `18.0.0-beta.3` (increment the numeric segment)126 - Pre-release without numeric segment: `18.0.0-alpha` → `18.0.0-alpha.1`127 - **Legacy non-dotted pre-release** (`18.0.0-beta2` → `18.0.0-beta3`): increment in place — do **not** convert to dotted. Only `Umbraco.AI.Search`/`Umbraco.AI.Automate` are on this grandfathered scheme.1281293. **Update** the `"version"` field in `version.json` using the Edit tool.1301314. **After all products are bumped**, commit and push:132 ```bash133 git add */version.json134 git commit -m "chore(release): Bump dev versions after release135136 Products bumped:137 - Umbraco.AI: 18.1.0 → 18.1.1138 - Umbraco.AI.OpenAI: 18.1.0 → 18.1.1139140 Co-Authored-By: Claude <noreply@anthropic.com>"141142 git push origin ${prefix}/dev143 ```144145### Phase 5: Major Version Cutover (conditional)146147**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.148149#### Detect whether a cutover is needed150151```bash152# Get the current GitHub default branch153gh api repos/umbraco/Umbraco.AI --jq '.default_branch'154# e.g. → "v18/dev"155```156157Parse the major from the default branch (e.g. `v18/dev` → `18`). Compare with the released major:158- Released major **equal to** default branch major → no cutover needed, skip this phase.159- Released major **greater than** default branch major → cutover needed (e.g. default is `v18/dev`, just released `v19.0.0`).160161#### Cutover steps162163Let `new_prefix` = `v{released_major}` (e.g. `v19`).1641651. **Check whether `vN+1/dev` and `vN+1/main` already exist:**166 ```bash167 git ls-remote origin refs/heads/${new_prefix}/dev refs/heads/${new_prefix}/main168 ```1691702. **Create missing branches** if they do not exist yet:171 - `${new_prefix}/main` — create from the tip of `${prefix}/main` (the freshly merged stable state):172 ```bash173 git push origin ${prefix}/main:refs/heads/${new_prefix}/main174 ```175 - `${new_prefix}/dev` — create from `${prefix}/dev` (after version bumps):176 ```bash177 git push origin ${prefix}/dev:refs/heads/${new_prefix}/dev178 ```179 If both already exist (dev team created them ahead of the release), skip creation.1801813. **Update the GitHub default branch:**182 ```bash183 gh api repos/umbraco/Umbraco.AI -X PATCH -f default_branch=${new_prefix}/dev \184 --jq '.default_branch'185 ```186 Confirm the returned value matches `${new_prefix}/dev`.1871884. **Inform the user** — they will need to update their local checkout:189 ```190 ✅ Major version cutover complete!191192 New branches created:193 - ${new_prefix}/main (from ${prefix}/main)194 - ${new_prefix}/dev (from ${prefix}/dev)195196 GitHub default branch updated: ${prefix}/dev → ${new_prefix}/dev197198 Developers should run:199 git fetch origin200 git checkout ${new_prefix}/dev201 ```202203### Phase 6: Check Issue Tracker204205Before cleanup, check whether any tracked issues need closing or a release-confirmation comment now that the fix is actually live.2062071. **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 any `Fixes #N`/`Closes #N` issue numbers mentioned in commit messages or PR bodies.2082092. **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.:210 ```211 Fixed in 18.1.4 (and backported to 17.1.4 for the v17 line), both now available on NuGet.212 ```213 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.2142153. **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.2162174. **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.2182195. **Check the Azure DevOps AI Team backlog** for related open work items. Scope every query to the `Umbraco AI` tag (per root `CLAUDE.md` — the `D-Team Tracker` project 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:220 ```221 mcp__azure-devops__wit_query (action: wiql), e.g.:222 SELECT [System.Id], [System.Title], [System.State] FROM WorkItems223 WHERE [System.TeamProject] = 'D-Team Tracker'224 AND [System.Tags] CONTAINS 'Umbraco AI'225 AND [System.State] NOT IN ('Closed','Removed','Done')226 AND ([System.Title] CONTAINS '<specific term>' OR ...)227 ```228 Report any real matches to the user; don't change work item state without confirmation.2292306. **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.231232### Phase 7: Cleanup (Optional)2332341. **Ask the user** if they want to delete the release/hotfix branch (local + remote):235 ```236 Delete the release branch '$release_branch'?237 - Local and remote238 - Local only239 - Skip (keep branch)240 ```2412422. If deleting:243 ```bash244 git branch -d $release_branch245 git push origin --delete $release_branch246 ```2472483. **Return to the appropriate dev branch** (the new one if a cutover occurred, otherwise `${prefix}/dev`):249 ```bash250 git checkout ${active_dev}251 ```252253### Phase 8: Summary254255Present a summary of everything that was done:256257```258✅ Post-release cleanup complete!259260Merged:261- $release_branch → ${prefix}/main262- ${prefix}/main → ${prefix}/dev263264Version bumps on ${prefix}/dev:265- Umbraco.AI: 18.1.0 → 18.1.1266- Umbraco.AI.OpenAI: 18.1.0 → 18.1.1267268[If major cutover:]269Major version cutover:270- Created v19/main from v18/main271- Created v19/dev from v18/dev272- GitHub default branch: v18/dev → v19/dev273274Issue tracker check:275- GitHub #324: already closed, posted release-confirmation comment276- Azure DevOps: no related open items found277278Branch cleanup: [deleted/kept]279280Nightly builds on ${prefix}/dev will now produce versions higher than the released versions.281```282283## Version Bump Logic284285### Stable Versions286287Simply increment the patch version:288- `18.1.0` → `18.1.1`289- `18.0.0` → `18.0.1`290291### Pre-release Versions292293New 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".294295Increment the numeric portion of the pre-release identifier:296- `18.0.0-beta.2` → `18.0.0-beta.3`297- `18.0.0-rc.1` → `18.0.0-rc.2`298- `18.0.0-alpha` → `18.0.0-alpha.1` (append `.1` if no numeric segment)299- `18.0.0-beta2` → `18.0.0-beta3` (legacy non-dotted — increment in place, never dotify)300301## Important Notes302303- **Always fetch tags first** — the release pipeline creates tags asynchronously after deploy304- **Use `--no-ff` merges** — preserves the merge commit for clear history305- **Post-merge hooks handle `release-manifest.json` cleanup** — don't manually delete it306- **version.json only has a `"version"` field** — update only that field, preserve all other properties307- **Both `vN/release/*` and `vN/hotfix/*` branches are supported** — the workflow is identical308- **If no tags are found**, the user can still proceed with merge-only (skip Phase 4)309- **Major cutover only triggers when the released major > the default branch major** — a patch or minor release on the current latest version never triggers it310311## Error Recovery312313- If the merge to `vN/main` fails (conflicts), help the user resolve conflicts before continuing314- If the push fails, check if the branch is protected and advise accordingly315- If version.json has unexpected format, show the user and ask how to proceed316- Never force-push — if push is rejected, pull and retry the merge317- If the GitHub default branch update fails, confirm admin permissions on the repo (`gh api repos/umbraco/Umbraco.AI --jq '.permissions'`)