Skip to content

feat: add batch-backport label to backport merged PRs together - #453

Merged
MarshallOfSound merged 4 commits into
mainfrom
feat/batch-backport
Oct 25, 2026
Merged

MarshallOfSound merged 4 commits into
mainfrom
feat/batch-backport

Conversation

@claude

@claude claude Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Requested by Samuel Attard · Slack thread

This adds a batch-backport label so related PRs can be backported together instead of one backport each. A merged PR with the label waits until no open PR carries it (or two hours after the oldest waiting PR merged, or until /trop run backport), and then trop opens one backport PR per target branch containing every waiting PR targeting it, in merge order, with one Backport of #N line per original, combined notes, every author asked for review and the highest semver label. The queue lives in the labels themselves so a restart can't lose it, trop only keeps the deadline timer and re-arms it on startup, and a member that doesn't apply on top of the earlier ones is dropped to needs-manual-bp/* while the rest still go out, which needed the sandbox job to apply members one after another (protocol bumped to v2, diffs only, backstop unchanged); stacks keep failing as a unit and docs/usage.md covers the details.

Nothing here has run against real GitHub yet, only the specs and the local sandbox runner.


Generated by Claude Code

Merged PRs labeled `batch-backport` wait until no open PR carries the
label (or two hours after the oldest of them merged), then go out as one
backport PR per target branch. The queue lives in GitHub labels; trop only
keeps a deadline timer, re-armed by a search on startup. Members that do
not apply on top of the earlier ones are left out and flagged for a manual
backport while the rest of the batch still goes out; the sandbox job
applies members one after another (protocol v2) and the backstop still
checks the result.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RMRMPAJGJ2vLN5aDeYD32
@MarshallOfSound
MarshallOfSound marked this pull request as ready for review October 25, 2026 22:06
@MarshallOfSound
MarshallOfSound requested a review from a team as a code owner October 25, 2026 22:06
@MarshallOfSound
MarshallOfSound enabled auto-merge (squash) October 25, 2026 22:06

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🔴 src/utils.ts — One batch member's failed preparation marks every PR in the batch needs-manual-bp/*, not just the member that failed. When a unit has no 'all' attempt and its squash fetch fails with a non-PatchError, the loop at src/utils.ts:1529 throws SandboxInfraError(..., false). That non-retryable error reaches the conflict branch, which comments on every top and calls flagForManualBackport(context, prs, ...) for all PRs. Fix: drop just that unit (comment on it, flag it, fail its check run), as a sandbox conflict does, and let the rest of the batch go out. Keep throwing for a retryable SandboxInfraError.

    Why this was flagged

    Trigger: a batched PR with 240 or more commits, or with only merge commits, gets null from getAllCommitsAttempt. Its getSquashAttempt then fails with any error that is not a PatchError, for example a 404 or 422 from fetchBackportCommit. In backportUnitsImpl (src/utils.ts:1520-1535) the batch path throws new SandboxInfraError(Could not fetch the squash commit(s) of #N, false). Because retryable is false, the error handler skips the infra branch at src/utils.ts:1244 and runs the conflict path. That path posts "I was unable to backport this PR ... cleanly" on every top and calls flagForManualBackport on all prs, so every PR loses its target/<branch> label and gets needs-manual-bp/<branch>. Without batching, only the PR with the failure is flagged and the others are backported. The PR description says a member that does not apply is dropped while the rest still go out. This path breaks that promise because the unit is never handed to the sandbox, so it can't be dropped the way a sandbox conflict is.

    Verification: The code is in backportUnitsImpl at src/utils.ts:949-966, which throws a non-retryable SandboxInfraError when a batch member has no 'all' attempt and its squash fetch fails, such as ECONNRESET in fetchCommitAs (src/utils.ts:220). Because retryable is false, the infra branch at :1244 is skipped. The conflict path then calls flagForManualBackport(context, prs, targetBranch) (:1333) with all PRs of the batch.

Comment thread src/operations/batch-backport.ts Resolved
Comment thread src/utils.ts Resolved
Comment thread src/operations/batch-backport.ts Resolved
Comment thread src/operations/batch-backport.ts Resolved
- A batch member whose commits don't apply and whose squash commit
  can't be fetched for good is left out like a conflict, instead of
  failing the whole batch; GitHub not answering still retries it.
- A batch an internal error stopped is retried on its own timer (up to
  three times in a row), and its PRs no longer make the PRs waiting
  since overdue, so a new merge still waits for open labeled PRs.
- A failure while handling a merge or a PR leaving the batch evaluates
  the batch again a few minutes later.
- The startup rescan carries on past an installation that fails, and
  sorts its search by recency so waiting PRs stay under the 1000-result
  cap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RMRMPAJGJ2vLN5aDeYD32
@claude

claude Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Re the review-body finding about a batch unit with no all-commits attempt whose squash fetch fails (src/utils.ts): fixed in 4a4db0e. That unit is now left out the way a sandbox conflict leaves one out: a comment on its top PR, target/* swapped for needs-manual-bp/*, and its check marked failed. The rest of the batch still goes out. Only a retryable SandboxInfraError (GitHub not answering) still stops the batch, and that batch is then retried.


Generated by Claude Code

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 optional suggestions (nits or notes on pre-existing code) were found and not posted.

Comment thread src/operations/batch-backport.ts Resolved
A batch backport runs once per target branch, but its retryable failures
were recorded per PR, so a success on one branch erased the failure on
another: the retry then found nothing, and the PR's old merge time made
later merges skip waiting for open labeled PRs. Failures, their retry
count and the deadline now go by PR and branch, a retry backports only
the branches that failed, and failures of branches no longer queued are
forgotten.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RMRMPAJGJ2vLN5aDeYD32

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

@MarshallOfSound
MarshallOfSound merged commit f448244 into main Oct 25, 2026
7 checks passed
@MarshallOfSound
MarshallOfSound deleted the feat/batch-backport branch October 25, 2026 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants