Add sglang-cherrypick skill for batching bot-cherry-pick dispatches#26068
Conversation
This skill takes a release branch and a list of merged PRs and dispatches .github/workflows/bot-cherry-pick.yml for each, then monitors the runs to completion and reports per-PR outcome. Before any dispatch it runs a local pre-flight: - Validates each PR is MERGED and has a recorded merge commit - Indexes existing cherry-pick PRs on the target branch and classifies each input PR as already-merged / open-duplicate / closed-only / new, skipping the dispatch when an existing cherry-pick already landed or is still in flight - Simulates the cherry-pick locally with `git merge-tree --write-tree` to surface conflicts and the exact file list that would change, without touching the working tree This catches the two failure modes that previously cost a full CI run each: re-cherry-picking a PR that already landed (creates duplicate PRs) and dispatching against a target that has drifted enough to conflict. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new Claude skill, sglang-cherrypick, which automates the process of cherry-picking merged PRs onto release branches for the sgl-project/sglang repository. The skill implements a comprehensive workflow including argument validation, local conflict detection using git merge-tree, and automated monitoring of GitHub Action runs. Review feedback highlighted a potential logic error in the jq filters used to identify existing cherry-pick PRs; specifically, using contains for PR number matching could lead to false positives if one PR number is a substring of another. The reviewer suggested using endswith to ensure more reliable identification of the bot-created PRs.
| declare -A PR_TO_EXISTING_CP=() # source_pr -> JSON array of existing cherry-pick PRs | ||
| for PR in "${PRS[@]}"; do | ||
| PR_TO_EXISTING_CP[$PR]=$(jq -c \ | ||
| "[.[] | select(.title | contains(\"(#${PR})\"))]" <<<"$EXISTING_CP_JSON") |
There was a problem hiding this comment.
The contains("(#${PR})") filter in jq performs a substring match, which can lead to false positives if one PR number is a prefix of another (e.g., PR #123 matching a cherry-pick PR for #1234). Since the bot appends the PR number as a suffix, using endswith is more reliable and avoids incorrect classification of PRs as already cherry-picked.
| "[.[] | select(.title | contains(\"(#${PR})\"))]" <<<"$EXISTING_CP_JSON") | |
| "[.[] | select(.title | endswith(\"(#${PR})\"))]" <<<"$EXISTING_CP_JSON") |
| --state all \ | ||
| --limit 30 \ | ||
| --json number,title,url,createdAt \ | ||
| --jq "[.[] | select(.title | contains(\"(#${PR})\"))][0]") |
There was a problem hiding this comment.
Using contains here can incorrectly match PRs where the target PR number is a substring of another PR number in the title. Switching to endswith ensures the skill correctly identifies the specific cherry-pick PR created for the source PR, avoiding confusion when multiple cherry-picks have similar PR numbers.
| --jq "[.[] | select(.title | contains(\"(#${PR})\"))][0]") | |
| --jq "[.[] | select(.title | endswith(\"(#${PR})\"))][0]") |
…gl-project#26068) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…gl-project#26068) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…gl-project#26068) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Adds a new Claude Code skill at
.claude/skills/sglang-cherrypick/SKILL.md. Given a release branch and a list of merged PRs, the skill dispatches.github/workflows/bot-cherry-pick.ymlfor each, monitors the resulting workflow runs to completion, and reports per-PR success/failure with links to the created cherry-pick PRs.Pre-flight (runs before any workflow dispatch)
MERGEDwith a recorded merge commit. Fails the whole batch up front rather than triggering partial dispatches.gh pr list --base <branch> --label cherry-pick --state all) and classifies each input PR as already-merged / open-duplicate / closed-only / new. Skips the dispatch when a cherry-pick already landed or is in flight, so we don't create redundant PRs.git merge-tree --write-tree --merge-base=<sha>^ <target> <sha>. This is side-effect-free (no working-tree change, no ref update) and matches the workflow's-m 1semantics for merge commits. Surfaces conflicts and the exact file list that would change, so the user sees in seconds what would otherwise take a full CI run to discover.Dispatch + monitoring
gh workflow runstdout (gh ≥ 2.45), with a snapshot/diff polling fallback for older gh.gh run watchto completion per run (workflow's per-branch concurrency group serializes them anyway).(#<source_pr>). On failure, surfaces the first::error::line from--log-failed.Why this exists
Manually dispatching cherry-picks for 10+ PRs at release time is tedious and easy to get wrong — typically you discover conflicts or duplicate dispatches only after waiting on CI. The pre-flight runs entirely locally in a few seconds and tells you exactly what would land, what would conflict, and what's already been picked.
Verified end-to-end against
release/v0.5.12(single PR dispatch produced PR #26063), and the pre-flight was verified against an 11-PR batch — it correctly identified PR #25733 as already merged (#26063), flagged #24932 (5 disagg files) and #25562 (model_config.py) as conflicting, and listed the file changes for the remaining 8 PRs.Test plan
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ✅ Run #26276703895
Latest PR Test (Extra): ❌ Run #26276703472