Skip to content

feat(ci): add /check-release-rollback skill#35620

Merged
dsilvam merged 2 commits into
mainfrom
feat/check-release-rollback-skill
May 11, 2026
Merged

feat(ci): add /check-release-rollback skill#35620
dsilvam merged 2 commits into
mainfrom
feat/check-release-rollback-skill

Conversation

@dsilvam
Copy link
Copy Markdown
Member

@dsilvam dsilvam commented May 8, 2026

Summary

Adds a new Claude Code slash command .claude/commands/check-release-rollback.md that automates rollback safety checks between any two dotCMS releases.

What it does

Given two version strings (e.g. 26.04.28-02_7149dce26.04.11-02_9650131):

  1. Extracts all merged PRs between the two commits via git log
  2. Fetches title + labels for each PR via gh CLI
  3. Classifies each PR as: Safe / Not Safe / Conflicting / Unlabeled
  4. Outputs a structured report with linked PRs and risk notes per unsafe PR
  5. Returns a clear YES / NO / CONDITIONAL verdict
  6. Optionally saves the report as a .txt file

Usage:

/check-release-rollback 26.04.28-02_7149dce 26.04.11-02_9650131

Motivation

During a customer's production incident (ticket #36966), a manual rollback safety check was performed and revealed multiple PRs labeled AI: Not Safe To Rollback blocking the rollback from 26.04.28-02. This skill automates that process so any engineer or support agent can run it in seconds.

Related:

Test plan

  • Run /check-release-rollback 26.04.28-02_7149dce 26.04.11-02_9650131 and verify output matches the manually produced report

🤖 Generated with Claude Code

Adds a Claude Code slash command that checks whether a dotCMS release
can be safely rolled back to a previous version.

Given two version strings (e.g. 26.04.28-02_7149dce → 26.04.11-02_9650131)
the skill:
- Extracts all merged PRs between the two commits via git log
- Fetches title + labels for each PR via gh CLI
- Classifies each as: Safe / Not Safe / Conflicting / Unlabeled
- Outputs a structured report with linked PRs and risk notes
- Returns a clear YES / NO / CONDITIONAL verdict
- Optionally saves the report as a .txt file

Motivated by the Lennox production incident (ticket #36966) where a
manual version of this check was performed and revealed PRs labeled
"Not Safe To Rollback" blocking a rollback from 26.04.28-02.

Usage: /check-release-rollback <current-version> <target-version>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

❌ Issue Linking Required

This PR could not be linked to an issue. All PRs must be linked to an issue for tracking purposes.

How to fix this:

Option 1: Add keyword to PR body (Recommended - auto-removes this comment)
Edit this PR description and add one of these lines:

  • This PR fixes #123 or Fixes: #123

  • This PR closes #123 or Closes: #123

  • This PR resolves #123 or Resolves: #123

  • Other supported keywords: fix, fixed, close, closed, resolve, resolved
    Option 2: Link via GitHub UI (Note: won't clear the failed check)

  1. Go to the PR → Development section (right sidebar)

  2. Click "Link issue" and select an existing issue

  3. Push a new commit or re-run the workflow to clear the failed check
    Option 3: Use branch naming
    Create a new branch with one of these patterns:

  • 123-feature-description (number at start)

  • issue-123-feature-description (issue-number at start)

  • feature-issue-123 (issue-number anywhere)

Why is this required?

Issue linking ensures proper tracking, documentation, and helps maintain project history. It connects your code changes to the problem they solve.---

This comment was automatically generated by the issue linking workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 8, 2026

Claude finished @dsilvam's task in 2m 21s —— View job


Review

A few real issues — most importantly one that likely prevents the skill from registering at all.

1. Missing YAML frontmatter on SKILL.md (likely blocking)

Every other skill in .claude/skills/ (e.g. cicd-diagnostics, dotcms-github-issues, triage, nx-generate) starts with a --- name: ... description: ... --- block. check-release-rollback/SKILL.md does not. Claude Code's skill discovery uses that frontmatter to register/list skills — without it, the /check-release-rollback command's instruction "Use the check-release-rollback skill" will likely fail to resolve. Add frontmatter matching the repo convention:

---
name: check-release-rollback
description: Check whether a dotCMS release can be safely rolled back to a previous version by inspecting all PRs merged between the two versions for rollback safety labels. Trigger when the user asks to verify rollback safety between two release versions.
---

2. "Canonical source" claim is misleading

Step 4 says: "Label constants (canonical source: .github/scripts/gather-release-data/src/categorize.ts)" — but the values diverge from that file:

Skill categorize.ts
ROLLBACK_SAFE_LABELS = ["AI: Safe To Rollback", "Human: Safe To Rollback"] Not defined anywhere in categorize.ts
INFRA_LABELS = ["Area : CI/CD"] ['infrastructure', 'security', 'dependencies', 'ci/cd', 'Area : CI/CD'] (5 entries)
INFRA_TITLE_PREFIXES = [...] Not defined; categorize.ts has its own broader list including feat, fix, etc.

If the categorize.ts file is changed, this skill won't follow. Either (a) drop the "canonical source" framing and own the constants explicitly, or (b) actually mirror them. The narrowed INFRA_LABELS (just Area : CI/CD) and the conservative INFRA_TITLE_PREFIXES look intentional for the rollback-low-risk classification — fine, just don't call them canonical.

3. Step 3 date window can miss backport PRs

TARGET_DATE=$(git log -1 --format="%aI" <target-sha>)
CURRENT_DATE=$(git log -1 --format="%aI" <current-sha>)
gh pr list ... --search "merged:>=${TARGET_DATE} merged:<=${CURRENT_DATE}"

%aI is the author date, which cherry-picks preserve from the original commit. A backport PR merged into the release branch after current-sha's author date — common for late hotfixes — would fall outside this search window. The skill's safety net is "PR not found → UNLABELED", which then downgrades the verdict from YES to CONDITIONAL even when the PR was actually labeled correctly.

Fix: use commit date %cI and pad the window (e.g. merged:>=${TARGET_DATE-1d} merged:<=${CURRENT_DATE+1d}), or look up missing PRs individually as a fallback.

4. gh pr list --limit 500 can silently truncate

For long release windows (months between LTS lines) you can blow past 500. Cross-referencing with PR_LIST will surface them as "not found in API response" → UNLABELED → false CONDITIONAL. Worth either: detecting len(api_response) == 500 and warning, or paginating.

5. CONFLICTING verdict is more permissive than NOT_SAFE

A PR with both AI: Not Safe and Human: Safe (or vice versa) is classified CONFLICTINGCONDITIONAL, while a PR with only Not SafeNO. The conservative read is "any unsafe label wins until a human resolves the conflict". As written, a single conflicted PR can mask a hard NO. At minimum, the report should call out conflicts loudly; ideally treat them as NO until reconciled. (Judgment call — flagging.)

6. Step 6 writes to cwd

rollback-safety-...txt lands in the repo root. Either prepend .tmp/ or /tmp/, or add a glob to .gitignore, otherwise it'll show up untracked the next time someone runs it on a clean checkout.

Minor

  • Step 2: revert-detection note is correct for PR-merged reverts (Revert "... (#X)" (#Y) → keep Y), but a directly-pushed revert without its own PR number would misattribute to the original PR. Edge case.
  • The Python one-liner is fine; not worth replacing with shell.

Approvals are already on the PR — none of the above blocks merge except #1, which I'd verify before shipping by running /check-release-rollback once on a real range and confirming the skill actually loads.
| Branch

- Remove hardcoded /Users/... fallback path; use git rev-parse --show-toplevel
- Fix classification order: CONFLICTING → NOT_SAFE → SAFE → UNLABELED
  (CONFLICTING was previously unreachable due to overlapping NOT_SAFE rule)
- Implement case-insensitive label matching (lowercase both sides)
- Redefine YES verdict: no NOT_SAFE + no CONFLICTING; CI/infra unlabeled
  PRs no longer block YES (avoids unreachable YES in practice)
- Replace per-PR gh pr view loop with single gh pr list date-range call
  to avoid rate limiting on large release windows
- Reference canonical ROLLBACK_UNSAFE_LABELS from categorize.ts as
  single source of truth for label constants
- Restructure as thin slash command + SKILL.md (follows repo pattern)
- Add input format validation with clear error message on bad input
- Add explicit failure path if SHA unresolvable after git fetch
- Document the "last #NNNNN = merged PR" extraction choice

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dsilvam dsilvam added this pull request to the merge queue May 11, 2026
Merged via the queue into main with commit a581590 May 11, 2026
29 checks passed
@dsilvam dsilvam deleted the feat/check-release-rollback-skill branch May 11, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants