name: uplift
description: "Create an uplift PR that cherry-picks intermittent test fixes and crash fixes from closed PRs into a target branch (beta or release). Triggers on: /uplift, create uplift, uplift PRs."
argument-hint: [github-username] [beta|release] [all|d|PR1,PR2,PR3]
disable-model-invocation: true
allowed-tools: Bash, Read, WebFetch, Grep, Glob
Uplift PR Creator
Create an uplift pull request that cherry-picks intermittent test fixes and crash fixes from a contributor's recently closed/merged PRs into a target channel branch.
Inputs
- Arguments:
$ARGUMENTS — space-separated values:
- First argument: GitHub username (the author whose closed PRs to review)
- Second argument (optional): Target channel — either
beta or release. Defaults to beta if not specified.
- Third argument (optional): PR filter — either:
all — evaluate all closed/merged PRs from this author in the past 30 days. Use gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)' where YYYY-MM-DD is 30 days ago.
- A duration like
<N>d (e.g., 10d, 60d, 7d) — evaluate all closed/merged PRs from this author in the past N days. Use gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)' where YYYY-MM-DD is N days ago from today. Parse the number by stripping the trailing d.
- A comma-separated list of PR numbers with no spaces (e.g.,
33534,33547,33580) — only evaluate these specific PRs. Fetch each one individually with gh pr view <number> --repo brave/brave-core --json number,title,mergedAt,mergeCommit,labels,body,url.
- If omitted, defaults to the recent 50 closed PRs (current behavior).
Parse the arguments by splitting $ARGUMENTS on whitespace. Examples:
/uplift netzenbot → username=netzenbot, channel=beta, filter=recent 50
/uplift netzenbot beta → username=netzenbot, channel=beta, filter=recent 50
/uplift netzenbot release → username=netzenbot, channel=release, filter=recent 50
/uplift netzenbot beta all → username=netzenbot, channel=beta, filter=all PRs (past 30 days)
/uplift netzenbot beta 10d → username=netzenbot, channel=beta, filter=all PRs (past 10 days)
/uplift netzenbot release 60d → username=netzenbot, channel=release, filter=all PRs (past 60 days)
/uplift netzenbot release 33534,33547,33580 → username=netzenbot, channel=release, filter=only those 3 PRs
Step 1: Gather Information
Run these in parallel:
Fetch closed PRs (method depends on the third argument):
- Default (no third arg): Use
gh pr list --repo brave/brave-core --author <username> --state closed --limit 50 --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'
all: Use gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)' where YYYY-MM-DD is 30 days ago from today.
<num>d duration (e.g., 10d, 60d): Use gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)' where YYYY-MM-DD is <num> days ago from today.
- Comma-separated PR list: For each PR number, use
gh pr view <number> --repo brave/brave-core --json number,title,mergedAt,mergeCommit,labels,body,url. Collect results into a list sorted by mergedAt.
The mergedAt field is a GitHub API property — if it is null, the PR was closed without being merged and should be skipped.
Determine the target branch: Fetch the content at https://github.com/brave/brave-browser/wiki/Brave-Release-Schedule and find the "Current channel information" table. Look for the row matching the target channel:
- If channel is
beta: find the Beta row to get its branch name (e.g., 1.88.x)
- If channel is
release: find the Release row to get its branch name (e.g., 1.87.x)
This branch is:
- The base branch to create the uplift PR against
- The branch to cherry-pick commits into
Step 2: Classify PRs
Review each merged PR (skip any where mergedAt is null) and classify it as either include or exclude for the uplift:
INCLUDE if the PR is any of:
- Intermittent/flaky test fix (titles often contain "Fix flaky", "Fix test:", "Fix intermittent", "Disable flaky")
- Crash fix (titles mention "crash", "null dereference", "EXCEPTION_ACCESS_VIOLATION", etc.)
- Test filter updates (disabling broken upstream tests, updating stale filter entries)
EXCLUDE if the PR is:
- A feature addition (not a fix)
- A refactor unrelated to test stability or crashes
- Already has the uplift label for the target channel (check the
labels array in the PR JSON for uplift/beta or uplift/release depending on the target channel)
- Not merged (
mergedAt is null)
Step 3: Cherry-Pick in Chronological Order
- Detect the remote: Run
git remote -v to determine whether upstream or origin points to brave/brave-core. Use whichever remote is correct (typically origin if there's no upstream).
- Fetch the target branch:
git fetch <remote> <target-branch>
- Choose a unique branch name: Use
uplift_<username>_<target-branch> as the base name. If that branch already exists (locally or on the remote), append a numeric suffix (e.g., uplift_<username>_<target-branch>_2, _3, etc.) until you find an unused name. Create the branch: git checkout -b <branch-name> <remote>/<target-branch>
- Filter out commits already in the target branch: Before cherry-picking, check which included commits are already present in the target branch. For each commit, search the target branch log for the PR number from the commit message (e.g.,
git log <remote>/<target-branch> --oneline --grep="#XXXXX"). If a match is found, that PR is already in the target branch — mark it as excluded with reason "Already in target branch" and skip it. This avoids empty cherry-picks and unnecessary merge conflicts.
- Cherry-pick the remaining commits sorted by
mergedAt timestamp (earliest first):git cherry-pick <merge_commit_sha>
- If a cherry-pick has conflicts, try to resolve them. If unresolvable, skip that PR and note it in the summary.
Step 4: Pre-submission Checks
After all cherry-picks are complete but before pushing, run these checks:
- Run format:
npm run format
- Run gn_check:
npm run gn_check
If either command indicates changes are needed (e.g., formatting fixes, GN file updates), make the necessary fixes and amend the last commit:
git add -A && git commit --amend --no-edit
- Run presubmit:
npm run presubmit -- --base=<remote>/<target-branch> --fix (e.g., npm run presubmit -- --base=origin/1.87.x --fix)
If the presubmit check fails for reasons unrelated to the cherry-picked changes (e.g., pre-existing issues in the target branch), note the failures in the summary but proceed with the uplift.
Step 5: Create the Uplift PR
Title Format
Generate the title dynamically based on the categories of PRs actually included:
- If the uplift contains only intermittent/flaky test fixes and test filter updates (no crash fixes):
Uplift intermittent test fixes to <target-branch>
- If the uplift contains only crash fixes (no test fixes):
Uplift crash fixes to <target-branch>
- If the uplift contains both test fixes and crash fixes:
Uplift intermittent test fixes and crash fixes to <target-branch>
Body Format
Only list the PRs being uplifted. Do NOT mention excluded PRs in the PR body.
Use a HEREDOC for correct formatting:
gh pr create --repo brave/brave-core --base <target-branch> --title "<title>" --body "$(cat <<'EOF'
Uplift of #XXXX, #YYYY, #ZZZZ
## Included PRs
- #XXXX - <PR title>
- #YYYY - <PR title>
...
Pre-approval checklist:
- [ ] You have tested your change on Nightly.
- [ ] This contains text which needs to be translated.
- [ ] There are more than 7 days before the release.
- [ ] I've notified folks in #l10n on Slack that translations are needed.
- [ ] The PR milestones match the branch they are landing to.
Pre-merge checklist:
- [ ] You have checked CI and the builds, lint, and tests all pass or are not related to your PR.
Post-merge checklist:
- [ ] The associated issue milestone is set to the smallest version that the changes is landed on.
EOF
)"
Labels
- If all included PRs are test filter-only changes (i.e., only modifying files in
test/filters/), add the CI/skip label to the uplift PR.
- Do NOT add
CI/skip if any included PR contains code changes beyond filter files.
Push and Create
git push -u origin <branch-name>
Step 6: Label the Base PRs
After the uplift PR is created, add the appropriate uplift label to each base PR that was included in the uplift:
- For beta:
uplift/beta
- For release:
uplift/release
gh pr edit <PR_NUMBER> --repo brave/brave-core --add-label "uplift/<channel>"
Do this for every PR that was successfully cherry-picked and included.
Step 7: Summary
After creating the PR, output a clear summary to the user:
Uplifted:
List each included PR with its number, title, and merge commit SHA.
Not Uplifted:
List each excluded PR with its number, title, and the reason it was excluded (e.g., "not merged", "already uplifted (has uplift label)", "already in target branch", "not a test fix or crash fix", "cherry-pick conflict").
PR Link:
Provide the URL of the newly created uplift PR.
1---2name: uplift3description: Create an uplift pull request that cherry-picks intermittent test fixes and crash fixes from a contributor's recently closed/merged PRs into a target channel branch.4---5
6---
7name: uplift
8description: "Create an uplift PR that cherry-picks intermittent test fixes and crash fixes from closed PRs into a target branch (beta or release). Triggers on: /uplift, create uplift, uplift PRs."
9argument-hint: [github-username] [beta|release] [all|<num>d|PR1,PR2,PR3]
10disable-model-invocation: true
11allowed-tools: Bash, Read, WebFetch, Grep, Glob
12---
13
14# Uplift PR Creator
15
16Create an uplift pull request that cherry-picks intermittent test fixes and crash fixes from a contributor's recently closed/merged PRs into a target channel branch.
17
18## Inputs
19
20- **Arguments**: `$ARGUMENTS` — space-separated values:
21 - First argument: **GitHub username** (the author whose closed PRs to review)
22 - Second argument (optional): **Target channel** — either `beta` or `release`. Defaults to `beta` if not specified.
23 - Third argument (optional): **PR filter** — either:
24 - `all` — evaluate all closed/merged PRs from this author in the past 30 days. Use `gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'` where `YYYY-MM-DD` is 30 days ago.
25 - A **duration** like `<N>d` (e.g., `10d`, `60d`, `7d`) — evaluate all closed/merged PRs from this author in the past N days. Use `gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'` where `YYYY-MM-DD` is N days ago from today. Parse the number by stripping the trailing `d`.
26 - A comma-separated list of PR numbers with no spaces (e.g., `33534,33547,33580`) — only evaluate these specific PRs. Fetch each one individually with `gh pr view <number> --repo brave/brave-core --json number,title,mergedAt,mergeCommit,labels,body,url`.
27 - If omitted, defaults to the recent 50 closed PRs (current behavior).
28
29Parse the arguments by splitting `$ARGUMENTS` on whitespace. Examples:
30- `/uplift netzenbot` → username=`netzenbot`, channel=`beta`, filter=recent 50
31- `/uplift netzenbot beta` → username=`netzenbot`, channel=`beta`, filter=recent 50
32- `/uplift netzenbot release` → username=`netzenbot`, channel=`release`, filter=recent 50
33- `/uplift netzenbot beta all` → username=`netzenbot`, channel=`beta`, filter=all PRs (past 30 days)
34- `/uplift netzenbot beta 10d` → username=`netzenbot`, channel=`beta`, filter=all PRs (past 10 days)
35- `/uplift netzenbot release 60d` → username=`netzenbot`, channel=`release`, filter=all PRs (past 60 days)
36- `/uplift netzenbot release 33534,33547,33580` → username=`netzenbot`, channel=`release`, filter=only those 3 PRs
37
38---
39
40## Step 1: Gather Information
41
42Run these in parallel:
43
441. **Fetch closed PRs** (method depends on the third argument):
45 - **Default (no third arg)**: Use `gh pr list --repo brave/brave-core --author <username> --state closed --limit 50 --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'`
46 - **`all`**: Use `gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'` where `YYYY-MM-DD` is 30 days ago from today.
47 - **`<num>d` duration** (e.g., `10d`, `60d`): Use `gh pr list --repo brave/brave-core --author <username> --state closed --limit 200 --search "closed:>YYYY-MM-DD" --json number,title,mergedAt,mergeCommit,labels,body,url --jq 'sort_by(.mergedAt)'` where `YYYY-MM-DD` is `<num>` days ago from today.
48 - **Comma-separated PR list**: For each PR number, use `gh pr view <number> --repo brave/brave-core --json number,title,mergedAt,mergeCommit,labels,body,url`. Collect results into a list sorted by `mergedAt`.
49
50 The `mergedAt` field is a GitHub API property — if it is `null`, the PR was closed without being merged and should be skipped.
51
522. **Determine the target branch**: Fetch the content at `https://github.com/brave/brave-browser/wiki/Brave-Release-Schedule` and find the "Current channel information" table. Look for the row matching the target channel:
53 - If channel is `beta`: find the **Beta** row to get its branch name (e.g., `1.88.x`)
54 - If channel is `release`: find the **Release** row to get its branch name (e.g., `1.87.x`)
55
56 This branch is:
57 - The base branch to create the uplift PR against
58 - The branch to cherry-pick commits into
59
60---
61
62## Step 2: Classify PRs
63
64Review each **merged** PR (skip any where `mergedAt` is null) and classify it as either **include** or **exclude** for the uplift:
65
66**INCLUDE** if the PR is any of:
67- Intermittent/flaky test fix (titles often contain "Fix flaky", "Fix test:", "Fix intermittent", "Disable flaky")
68- Crash fix (titles mention "crash", "null dereference", "EXCEPTION_ACCESS_VIOLATION", etc.)
69- Test filter updates (disabling broken upstream tests, updating stale filter entries)
70
71**EXCLUDE** if the PR is:
72- A feature addition (not a fix)
73- A refactor unrelated to test stability or crashes
74- Already has the uplift label for the target channel (check the `labels` array in the PR JSON for `uplift/beta` or `uplift/release` depending on the target channel)
75- Not merged (`mergedAt` is null)
76
77---
78
79## Step 3: Cherry-Pick in Chronological Order
80
811. **Detect the remote**: Run `git remote -v` to determine whether `upstream` or `origin` points to `brave/brave-core`. Use whichever remote is correct (typically `origin` if there's no `upstream`).
822. Fetch the target branch: `git fetch <remote> <target-branch>`
833. **Choose a unique branch name**: Use `uplift_<username>_<target-branch>` as the base name. If that branch already exists (locally or on the remote), append a numeric suffix (e.g., `uplift_<username>_<target-branch>_2`, `_3`, etc.) until you find an unused name. Create the branch: `git checkout -b <branch-name> <remote>/<target-branch>`
844. **Filter out commits already in the target branch**: Before cherry-picking, check which included commits are already present in the target branch. For each commit, search the target branch log for the PR number from the commit message (e.g., `git log <remote>/<target-branch> --oneline --grep="#XXXXX"`). If a match is found, that PR is already in the target branch — mark it as excluded with reason "Already in target branch" and skip it. This avoids empty cherry-picks and unnecessary merge conflicts.
855. Cherry-pick the remaining commits sorted by `mergedAt` timestamp (earliest first):
86 ```bash
87 git cherry-pick <merge_commit_sha>
88 ```
896. If a cherry-pick has conflicts, try to resolve them. If unresolvable, skip that PR and note it in the summary.
90
91---
92
93## Step 4: Pre-submission Checks
94
95After all cherry-picks are complete but before pushing, run these checks:
96
971. **Run format**: `npm run format`
982. **Run gn_check**: `npm run gn_check`
99
100If either command indicates changes are needed (e.g., formatting fixes, GN file updates), make the necessary fixes and amend the last commit:
101
102```bash
103git add -A && git commit --amend --no-edit
104```
105
1063. **Run presubmit**: `npm run presubmit -- --base=<remote>/<target-branch> --fix` (e.g., `npm run presubmit -- --base=origin/1.87.x --fix`)
107
108If the presubmit check fails for reasons unrelated to the cherry-picked changes (e.g., pre-existing issues in the target branch), note the failures in the summary but proceed with the uplift.
109
110---
111
112## Step 5: Create the Uplift PR
113
114### Title Format
115
116Generate the title dynamically based on the categories of PRs actually included:
117
118- If the uplift contains **only** intermittent/flaky test fixes and test filter updates (no crash fixes): `Uplift intermittent test fixes to <target-branch>`
119- If the uplift contains **only** crash fixes (no test fixes): `Uplift crash fixes to <target-branch>`
120- If the uplift contains **both** test fixes and crash fixes: `Uplift intermittent test fixes and crash fixes to <target-branch>`
121
122### Body Format
123
124Only list the PRs being uplifted. Do NOT mention excluded PRs in the PR body.
125
126Use a HEREDOC for correct formatting:
127
128```bash
129gh pr create --repo brave/brave-core --base <target-branch> --title "<title>" --body "$(cat <<'EOF'
130Uplift of #XXXX, #YYYY, #ZZZZ
131
132## Included PRs
133- #XXXX - <PR title>
134- #YYYY - <PR title>
135...
136
137Pre-approval checklist:
138- [ ] You have tested your change on Nightly.
139- [ ] This contains text which needs to be translated.
140 - [ ] There are more than 7 days before the release.
141 - [ ] I've notified folks in #l10n on Slack that translations are needed.
142- [ ] The PR milestones match the branch they are landing to.
143
144
145Pre-merge checklist:
146- [ ] You have checked CI and the builds, lint, and tests all pass or are not related to your PR.
147
148Post-merge checklist:
149- [ ] The associated issue milestone is set to the smallest version that the changes is landed on.
150EOF
151)"
152```
153
154### Labels
155
156- If **all** included PRs are test filter-only changes (i.e., only modifying files in `test/filters/`), add the `CI/skip` label to the uplift PR.
157- Do NOT add `CI/skip` if any included PR contains code changes beyond filter files.
158
159### Push and Create
160
161```bash
162git push -u origin <branch-name>
163```
164
165---
166
167## Step 6: Label the Base PRs
168
169After the uplift PR is created, add the appropriate uplift label to **each base PR** that was included in the uplift:
170- For beta: `uplift/beta`
171- For release: `uplift/release`
172
173```bash
174gh pr edit <PR_NUMBER> --repo brave/brave-core --add-label "uplift/<channel>"
175```
176
177Do this for every PR that was successfully cherry-picked and included.
178
179---
180
181## Step 7: Summary
182
183After creating the PR, output a clear summary to the user:
184
185### Uplifted:
186List each included PR with its number, title, and merge commit SHA.
187
188### Not Uplifted:
189List each excluded PR with its number, title, and the reason it was excluded (e.g., "not merged", "already uplifted (has uplift label)", "already in target branch", "not a test fix or crash fix", "cherry-pick conflict").
190
191### PR Link:
192Provide the URL of the newly created uplift PR.