Uh oh!
There was an error while loading. Please reload this page.
fix(fr-gate): re-run the gate when a PR changes its base (backend#1945) - #338
Conversation
`edited` is the only pull_request event GitHub fires on a base-branch change. Without it a retargeted PR can never re-run fr-gate: synchronize needs a push, and a retarget has nothing to push. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
saadqbal
left a comment
There was a problem hiding this comment.
Correct, and the reasoning is the part worth keeping. edited really is the only pull_request action that fires on a base change — synchronize needs a push and a retarget has nothing to push — so without it a retargeted PR carries its old base's verdict with no event able to clear it. Citing the four-hour stale check on .github#237 rather than asserting the mechanism is the right form.
Checked the sibling PRs rather than assuming a fleet change is uniform: this and docs#140 / e2e-test-agent#256 have three different patch hashes, but the difference is only hunk offsets plus docs carrying master in an unchanged context line. The change itself is identical in all three.
One thing to be aware of rather than fix: edited also fires on title and body edits, so the gate now re-runs on every description tweak. Harmless while the gate is idempotent, and much cheaper than the bug it fixes — just don't be surprised by the extra runs.
Uh oh!
There was an error while loading. Please reload this page.
What
Adds
editedtofr-gate-caller.yml'spull_requesttrigger types, plus a short commentsaying why it is there so it does not get "tidied" away later.
editedis the only event GitHub fires when a PR's base branch changes. Without it aretargeted PR can never re-run its gate:
synchronizeneeds a push, and a retarget hasnothing to push.
One file changed,
+6/-1— thetypes:line and its five-line comment. Nothing else.What this fixes, and what it does not
The two retarget directions behave differently, because the caller also carries
branches: [staging, main(, master)]. I want that stated here rather than discovered later.Retarget INTO a gated branch (
develop→staging/main/master) — fixed, and this isthe merge-blocking direction. Today
openedfires while the base is stilldevelop, sothe
branches:filter drops it and nogate / gatecheck run is ever created.gate / gateis a required check onstagingandmain/master, so the PR sits at"Expected — waiting for status to be reported" indefinitely. The only ways out are a push or
a
gate-nudgelabel toggle, both of which need write access. Withedited, the retargetitself produces the run, with no manual action at all.
Retarget OUT of a gated branch (
main→develop) — NOT fixed by this change. This isthe direction backend#1945 measured on .github#237, so it matters that this PR does not
silently under-deliver. GitHub sends
editedwith the new base (develop); thebranches:filter is evaluated against that new base, does not match, and the workflow stilldoes not run — so the stale verdict from the old base survives.
Two things make that a follow-up rather than a blocker for this PR:
required_status_checks.contextsondevelopfor all 17 repos:gate / gateis required on none of them. (
.github's develop lists a baregate, which isconformance-gate.yml— on .github#237 that context was green whilegate / gatewas thered one.) So a stale red left by this direction is reviewer confusion and wasted time —
the real cost the ticket documents — but it does not block a merge.
branches:so the gate runs on the new base andreports the pass it already has a step for ("Target branch is not gated — nothing to
enforce"). That would add a
gate / gaterun to everydevelopPR across 17 repos, whichis a change to the check surface on the busiest branch and deserves its own decision.
Does the extra frequency cost anything?
editedalso fires on title and body edits. Scope first: this workflow only runs on PRs whosebase is
staging/main/master, so the population is release-train mirror PRs, hotfixes andhotfix back-merges — machine-created, and rarely hand-edited. Ordinary
developPRs nevertrigger it at all.
The job itself is read-only. It mints a short-lived scoped App token and reads
commits/{sha}/pulls,compare/...and the ProjectV2 board. It writes no card, label orcomment; its only output is its own conclusion. So the cost is runner minutes and nothing else.
Can a re-run turn a passing gate red?
Yes, by two mechanisms, and they deserve different answers.
1. The board moved — and that red is correct, not false. The verdict is a live read of each
attributed item's
Status. Automation is monotonic and never demotes, but a human can drag acard backward or remove it from the board, and either would flip a green gate red on the next
run. That is the gate reporting current truth. GitHub evaluates the merge button against the
latest check run, so a stale green on a required gate is precisely the failure this check
exists to prevent — evaluating more often makes it more accurate, not less. It is also not new
exposure:
labeled/unlabeledalready re-trigger on every label change.The item set itself is stable for a PR nobody pushes to. It is derived from
origin/$BASE_REF..HEAD; a base that advances can only shrink that range, never add items.2. Transient infrastructure — and this is the only genuine cost. The gate fails closed by
design: an App-token mint failure, a GraphQL 5xx, or an unattributable commit after three
retries all produce a red. A body edit could therefore turn a green promotion PR red for
reasons that have nothing to do with the PR.
I judge that acceptable, and mildly positive on balance, because
editedalso adds arecovery lever for exactly that case. Today clearing a bricked gate needs
gh run rerun(admin) or a label toggle (write) — which is why .github#237 sat red: the person who diagnosed
it had
read. Editing the PR body is available to the PR author. This makes the class of"required check stuck on a verdict nothing can update" strictly smaller.
repo-inventory.yml— no change needed, confirmed rather than assumedThe ticket says
caller-drift.pycompares blob shas and therefore the fleet must merge everyother repo before
.github. That does not apply to this file.caller-drift.py's designrule 2 is "MATCH ON
uses:CONTENT, NEVER ON FILENAME" — callers are parsed as YAML and theirresolved
uses:values compared. Byte-for-byte comparison applies only to entries undercopies:, andcopies:inrepo-inventory.ymlcontains exactly one file,add-to-kanban.yml.fr-gate.ymlis listed underreusables/callers.Adding a trigger type does not change the resolved
uses:value, so the inventory needs noedit and there is no merge-ordering constraint on this set of PRs.
Verification
python3 -c "import yaml, ..."parses each edited file, and the resolvedtypes:list isasserted to be exactly
[opened, reopened, synchronize, ready_for_review, labeled, unlabeled, edited]in all 17 repos.branches:is unchanged in every one.actionlintclean on every edited file.git diff --numstatis+6/-1on the single path.github/workflows/fr-gate-caller.ymlin every repo.
.github/workflows/fr-gate-caller.ymlfrom each one's own default branch, not from asearch index.
claude-skills,release-trainandrfcscarry no caller and are untouched.Not proven: the retarget path itself. Confirming it needs a PR to actually be retargeted,
which cannot be done from the diff. The claim that
editedfires on a base change, and thatbranches:is evaluated against the new base, is from GitHub's documentedpull_requestbehaviour — it is reasoning, not a measurement, and the second half is what the "NOT fixed"
section above turns on.
Closes tracebloc/backend#1945
Note
Low Risk
Workflow trigger-only change with no app logic; extra runs on title/body edits are limited to PRs targeting staging/main and only add read-only gate evaluations.
Overview
Adds
editedto thepull_requesttrigger types infr-gate-caller.ymlso the FR gate re-runs when a PR is retargeted ontostagingormain. GitHub only emitseditedon base-branch changes; without it,gate / gatecould stay stale becausesynchronizenever fires without a new push.A short inline comment documents why
editedmust stay in the list.branches:is unchanged — retargets off gated branches still won’t run this workflow; this change targets the merge-blocking case where a PR moves onto a gated base.Reviewed by Cursor Bugbot for commit da3f56f. Bugbot is set up for automated code reviews on this repo. Configure here.