fix: supersede completed backport approval check runs instead of updating them - #423
Merged
dsanders11 merged 2 commits intoAug 25, 2026
Merged
Conversation
…ting them The Checks API treats a completed check run as terminal: a PATCH asking to move it back to 'queued' succeeds and applies the output, but silently keeps the old status and conclusion. The dedupe path added in #422 therefore could not un-green a run that had already concluded success - the labeled-event reset only rewrote the output text, leaving a green check whose output read 'Needs Backport Approval' (observed on electron/electron#53035). - queueBackportApprovalCheck now only updates an existing run in place while it is still pending; a completed run is superseded by a fresh queued run, which branch protection consults as the latest run per name. - The opened-event evaluation no longer concludes 'not required' for backport PRs authored by trop itself: trop adds labels in a separate API call shortly after creating the PR, so the live labels are still empty in that window. The check stays queued until the labeled events that follow trop's own label writes settle the verdict.
jkleinsc
approved these changes
Aug 20, 2026
jkleinsc
marked this pull request as ready for review
August 20, 2026 21:30
dsanders11
self-requested a review
August 20, 2026 22:33
…kports The opened-event guard special-cased backports authored by trop's bot user, but the window it protects against is not author-specific: a declared backport's labels are always written by trop itself - backportImpl labels trop-created backports in a separate API call after opening them, and updateManualBackport labels manually-opened backports with at least the base-ref label - so labels can land after the opened delivery for any author, and webhook deliveries can be delayed or reordered besides. Gate the guard on the author-agnostic property that actually matters: whether the PR body declares 'Backport of #N'. Every declared backport is guaranteed at least one trop-written label and therefore a labeled event that settles the verdict, so opened can safely leave the check pending. PRs without a declaration (e.g. fast-track PRs targeting release branches) get no guaranteed labeled event and would hang queued forever, so opened still concludes 'not required' for them. Also skip the re-queue when the check has already concluded: a labeled delivery processed before a late opened delivery has already settled the verdict from the same live labels, and superseding that concluded run would leave a queued check no follow-up event ever completes.
dsanders11
previously requested changes
Aug 20, 2026
dsanders11
left a comment
Member
There was a problem hiding this comment.
Have some concerns, doing a more thorough review.
MarshallOfSound
approved these changes
Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by John Kleinschmidt · Slack thread
Follow-up to #422. Makes the "Backport Approval Enforcement" check create a fresh queued run when the existing run has already completed, instead of attempting an in-place reset that the Checks API silently ignores.
Before
A completed "Backport Approval Not Required" run could not be un-greened. When
backport/requested 🗳landed after the run had concluded, the labeled-event reset from #422 calledchecks.update({ status: 'queued' })on the completed run — but the Checks API treats a completed run as terminal, so the PATCH only rewrote the output text while keepingstatus: completed/conclusion: success. The result was a green check whose output read "Needs Backport Approval", exactly what happened on electron/electron#53035 (check run 96268219963), leaving the PR mergeable without backport approval.This window is routine for trop-created backports: trop opens the PR first and adds its labels (
backport, target branch,backport/requested 🗳, semver) in a separate API call a few seconds later, so theopened-event evaluation from live labels (the #422 fix) genuinely sees no labels and concludes success before the labels arrive.After
A late approval-request label supersedes the completed run:
queueBackportApprovalCheckstill resets a run in place while it isqueued/in_progress, but when the existing run iscompletedit creates a fresh queued run — branch protection consults only the latest run per name, so the stale green run no longer gates anything. Additionally, theopened-event evaluation no longer concludes "not required" for any declared backport — a PR whose body carries aBackport of #Ndeclaration, regardless of author; the check stays queued until the labeled events that follow trop's label writes settle the verdict, closing the create-then-label window at the source (and failing safe — pending — if the labels never arrive).How
src/utils/checks-util.ts(queueBackportApprovalCheck): keep the fix: evaluate backport approval from live labels to prevent check race #422 dedupe/update path only for runs withstatus !== 'completed'; otherwise fall through tochecks.createwithstatus: 'queued'.src/index.ts: in the not-approved/not-requested branch,action === 'opened'no longer concludes for declared backports (getPRNumbersFromPRBody(pr).length > 0). The general rule: a declared backport's labels are always written by trop itself —backportImpllabels trop-created backports in a separate API call after opening them, andupdateManualBackportlabels manually-opened backports with at least the base-ref label — so every declared backport is guaranteed a subsequentlabeledevent, andopenedcan safely leave the check pending for any author and any webhook timing. PRs without aBackport of #Ndeclaration (fast-track / roller PRs targeting release branches) receive this check too but get no guaranteedlabeledevent and never re-evaluate otherwise, soopenedmust still conclude "Backport Approval Not Required" for them — always-queue-on-openedwould hang those checks forever. As delivery-reordering protection, a lateopeneddoes not re-queue a run that an earlier-processedlabeleddelivery already concluded from the same live labels — superseding it would leave a queued run no follow-up event ever completes.spec/checks-util.spec.tscovers queued/in_progress →checks.updateand completed →checks.create(no update);spec/index.spec.tscovers the any-author pending-on-openedrule, the undeclared-PR conclude-on-openedrule, the late-openedno-re-queue rule, and a labeled-event regression test mirroring #53035 (stale completed-success run +backport/requested 🗳→ pending state re-asserted).Note on the audit of other writes to this check:
updateBackportApprovalCheckand the otherupdate*Checkhelpers always PATCH with aconclusion, i.e. completed → completed transitions, which the API does apply (the validity check has always flipped success/failure this way); only transitions out of the completed state are silently ignored, and the enforcement check's only such transition is the queued reset fixed here. Firsthand evidence from #53035: the queued-reset PATCH succeeded but leftstatus/conclusion/completed_atfrozen while applying the new output.Refs: electron/electron#53035, electron/electron#52973, #422