Re-arm Dependabot PRs that GitHub silently disarmed - #10
Conversation
Auto-merge is armed on PR open, then GitHub disables it when a PR-triggered workflow completes with a skipped job. Nothing re-arms it and every run still reports success, so green PRs accumulate. Measured on appeler/pranaam, where nine had. Adds a scheduled sweep that re-arms or lands stranded PRs and logs a line for each one. Eligibility is still decided once, by fetch-metadata in the pull_request context, and recorded as a label the sweep reads back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zizmor's excessive-permissions audit rejects a workflow-level write grant once there is more than one job: it applies to every job whether or not that job needs it. Both jobs here do need both scopes, but they now say so themselves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 29dad2a6d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if label not in names: | ||
| verdict, act = "ineligible (no policy label)", "none" |
There was a problem hiding this comment.
Backfill eligibility before rejecting unlabelled PRs
On rollout, every already-open Dependabot PR—including the stranded PRs this change is intended to recover—predates the new label-writing step. Because merging this workflow does not generate a pull_request event for those PRs, they remain unlabelled and every scheduled run permanently classifies them as ineligible; add a one-time backfill or another safe classification path for pre-existing PRs.
Useful? React with 👍 / 👎.
| merge) | ||
| echo "merging #$n" | ||
| gh pr merge "$n" --squash --delete-branch || echo "::warning::could not merge #$n" |
There was a problem hiding this comment.
Pin the validated head when merging
If Dependabot updates or rebases a PR after prs.json is collected but before this command runs, the terminal-green checks belong to the old head while gh pr merge merges the new head, potentially before its checks are created. The inspected gh pr merge --help explicitly provides --match-head-commit SHA to require that the head still matches, so collect headRefOid and pass it here to close this race.
Useful? React with 👍 / 👎.
ci.yml triggers on pull_request, and this repo has had no PR since the summary formatting landed, so ruff has not looked at main. Four ISC004: implicitly concatenated f-strings inside a list, where a missing comma would silently join two lines into one rather than fail. One SIM102. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nine green Dependabot PRs had accumulated on
appeler/pranaamwhile every auto-merge run reported success. The cause, from run 30836798988 (PR #29):Auto-merge was armed. GitHub disarmed it once a PR-triggered workflow finished with a skipped job, and
on: pull_requestnever fires again — so nothing re-arms, ever. calibre and the others have not hit this only because they happen to have no skipped job in a PR-triggered workflow. The automation is green throughout.What changed
A
schedule:trigger (every 3h) that re-arms stranded PRs. It converges regardless of why GitHub disarmed, which aworkflow_runtrigger racing the disarm event would not.Eligibility is still decided exactly once, by
dependabot/fetch-metadatain thepull_requestcontext where it is reliable — the verdict is recorded as anautomerge-eligiblelabel and read back by the sweep. Deriving it a second time from a branch name would not recover major-versus-minor, and two copies of the rule would drift.The sweep reads
statusCheckRollup, notmergeStateStatusalone:CLEANis GitHub's opinion about mergeability, and this job needs the stronger fact that every check is terminal and none failed before it merges anything directly.It logs a line per PR — armed / merged / ineligible / checks-failing / already-armed — and warns when an eligible PR it cannot act on has been open past
STALE_AFTER_HOURS. A sweep that quietly does nothing would reproduce the bug it fixes.Verified
Decision logic run against the five real open pranaam PRs and six fixtures:
The five real ones are Python-ecosystem majors and the sweep refuses all of them, which is the negative check.
🤖 Generated with Claude Code