Context
PR #36589 added the assign-prs skill. Its first revision also classified, approved and merged individually safe Dependabot PRs. That workstream was removed in review — assign-prs is now assignment-only, and dependency updates belong to dependabot-rollup.
dependabot-rollup today combines compatible updates into a rollup branch and opens a draft PR, explicitly leaving the original PRs untouched. This issue tracks adding the removed capability there instead, as an opt-in: approve and merge Dependabot PRs that are individually safe, without building a rollup.
This is deliberately conservative — the cost of a wrong merge is far higher than the cost of leaving a PR for a human.
Safety conditions
A PR is safe only when all of these hold. Default to unsafe.
- Author is Dependabot. The two APIs spell this differently:
search/issues returns dependabot[bot], gh pr view --json author returns app/dependabot. Comparing the gh pr view value against dependabot[bot] marks every Dependabot PR unsafe and yields an empty safe list — a bug that looks like a conservative result. Match either spelling.
- Patch or minor only. Parse
bump <dep> from <version> to <version>, stripping any chore(deps): / chore(deps-dev): prefix. Target major must equal source major. Exclude anything unparseable rather than guessing.
- Non-empty diff.
changedFiles > 0. A bump followed by a corrective commit can net to zero changed files against the base, which passes every other rule here — including "no workflow files touched", trivially true when no files are touched. Close such a PR instead; Dependabot re-raises it if it still applies.
- No workflow files touched. Anything under
.github/workflows/** is out of scope regardless of semver: it alters CI execution and is a supply-chain surface.
- Every check affirmatively passed. See below — this is the subtle one.
- Cleanly mergeable.
mergeable == MERGEABLE. Exclude CONFLICTING and DIRTY. UNKNOWN means GitHub hasn't finished computing merge state: re-poll once, then exclude.
The check-verification trap
Credit to Paolo Aliprandi (@paolo-aliprandi), who caught this in review of #36589 (comment).
The original rule was a negative list: reject any entry in statusCheckRollup whose conclusion is FAILURE, TIMED_OUT, CANCELLED or ACTION_REQUIRED. That does not establish that anything passed:
- an empty
statusCheckRollup satisfies it vacuously;
- a rollup containing only neutral or skipped conclusions also satisfies it.
Once the skill adds its own approval, gh pr merge --auto will then merge as soon as nothing blocks — so a PR with no checks at all is the easiest one to auto-merge, which is exactly backwards.
Any implementation must instead:
- require a non-empty rollup;
- affirmatively verify that every relevant check concluded
SUCCESS;
- treat neutral/skipped as unproven, not passing;
- treat still-running checks as not-yet-safe and re-check later rather than merging optimistically;
- re-verify immediately before merging, since status changes between planning and execution.
Note that mergeStateStatus == BLOCKED on its own is expected and is not a disqualifier — it usually means only that the required approval hasn't been given yet, which is the approval being added.
Guardrails to carry over
- Dry-run and explicit approval before approving or merging anything.
- Never approve or merge a PR not authored by Dependabot.
- Never merge a semver-major update, or any PR touching
.github/workflows/**.
- Never bypass branch protection or use an administrator merge override.
- Never merge with a failing, cancelled, timed-out, or still-running required check.
Context
PR #36589 added the
assign-prsskill. Its first revision also classified, approved and merged individually safe Dependabot PRs. That workstream was removed in review —assign-prsis now assignment-only, and dependency updates belong todependabot-rollup.dependabot-rolluptoday combines compatible updates into a rollup branch and opens a draft PR, explicitly leaving the original PRs untouched. This issue tracks adding the removed capability there instead, as an opt-in: approve and merge Dependabot PRs that are individually safe, without building a rollup.This is deliberately conservative — the cost of a wrong merge is far higher than the cost of leaving a PR for a human.
Safety conditions
A PR is safe only when all of these hold. Default to unsafe.
search/issuesreturnsdependabot[bot],gh pr view --json authorreturnsapp/dependabot. Comparing thegh pr viewvalue againstdependabot[bot]marks every Dependabot PR unsafe and yields an empty safe list — a bug that looks like a conservative result. Match either spelling.bump <dep> from <version> to <version>, stripping anychore(deps):/chore(deps-dev):prefix. Target major must equal source major. Exclude anything unparseable rather than guessing.changedFiles > 0. A bump followed by a corrective commit can net to zero changed files against the base, which passes every other rule here — including "no workflow files touched", trivially true when no files are touched. Close such a PR instead; Dependabot re-raises it if it still applies..github/workflows/**is out of scope regardless of semver: it alters CI execution and is a supply-chain surface.mergeable == MERGEABLE. ExcludeCONFLICTINGandDIRTY.UNKNOWNmeans GitHub hasn't finished computing merge state: re-poll once, then exclude.The check-verification trap
Credit to Paolo Aliprandi (@paolo-aliprandi), who caught this in review of #36589 (comment).
The original rule was a negative list: reject any entry in
statusCheckRollupwhose conclusion isFAILURE,TIMED_OUT,CANCELLEDorACTION_REQUIRED. That does not establish that anything passed:statusCheckRollupsatisfies it vacuously;Once the skill adds its own approval,
gh pr merge --autowill then merge as soon as nothing blocks — so a PR with no checks at all is the easiest one to auto-merge, which is exactly backwards.Any implementation must instead:
SUCCESS;Note that
mergeStateStatus == BLOCKEDon its own is expected and is not a disqualifier — it usually means only that the required approval hasn't been given yet, which is the approval being added.Guardrails to carry over
.github/workflows/**.