Back to Onyx

Merge Dependabot PRs

.cursor/skills/merge-dependabot-prs/SKILL.md

4.5.0-cloud.06.3 KB
Original Source

Merge Dependabot PRs

Treat each blocked PR as its own small diagnosis, not one "just merge it" action.

Confirm before anything consequential. Approving, closing, pushing to a PR branch, merging main into someone's branch, and rerunning CI all touch shared state. Triage everything into buckets first, then confirm the plan per bucket with AskUserQuestion — not per PR, and not after the fact. Re-confirm if a genuinely new kind of problem appears mid-flight (e.g. a real regression where a mechanical fix was expected).

How Onyx gates merges

  • main merges exclusively through the merge queue ("Main Protection" ruleset). Enqueue with gh pr merge <pr> --auto — no strategy flag; the queue picks the method. --squash/--merge/--rebase will be rejected.
  • Required checks: quality-checks, playwright-required, database-tests, backend-check, mypy-check, Jest Tests, required.
  • required (in pr-integration-tests.yml) and playwright-required (in pr-playwright-tests.yml) are aggregate needs: [...] + if: always() jobs over the slow integration/playwright matrices. If either is absent from gh pr checks, its matrix is still running — the PR isn't blocked. (merge-group.yml provides instant-pass stubs of the same names inside the queue itself.)
  • mergeStateStatus/autoMergeRequest are unreliable for "is it actually queued" — query the queue directly: references/graphql-queries.md.

1. Discover the batch

bash
gh pr list --search "is:open author:app/dependabot assignee:<user>" \
  --json number,title,headRefName,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup

PRs are labeled by ecosystem (dependabot:python, dependabot:javascript, dependabot:docker, dependabot:actions, plus dependabot:sandbox) and assigned per .github/dependabot.yml. Flag duplicates: two PRs bumping the same package, or a manual bump PR overlapping a bot one.

2. Triage into buckets, confirm the plan

These PRs aren't in a rush. Wait for every check to complete — required and advisory alike (e.g. storybook-build on web/** changes, which never gates the queue but pages Slack if broken post-merge) — and treat any red check as a failure to classify, never as noise to skip.

  • Green & ready — every check completed and passing, advisory included.
  • Failing — mechanical — only a generated/lock file wasn't regenerated after the bump (see step 5 for the Onyx cases).
  • Failing — needs diagnosis — anything else; requires reading the failing job's log first, never just the check name.
  • Conflicting — real merge conflict against main.
  • Superseded — a newer PR in the batch covers it.

Independent of bucket, assess compatibility: note each PR's semver jump and whether the package is production or dev-only. Major bumps (and 0.x minors, which semver allows to break) are never covered by a blanket "enqueue the green ones" — green CI proves the build, not the behavior. Surface each one individually in the confirmation with a one-line breaking-changes summary from the release notes Dependabot embeds in the PR body, and let the user opt in per PR.

Summarize buckets and proposed actions, confirm with AskUserQuestion, then act.

3. Green: approve and enqueue

bash
gh pr review <pr> --approve
gh pr merge <pr> --auto

If a PR falls out of the queue (e.g. head_ref_force_pushed after a Dependabot rebase), re-running gh pr merge <pr> --auto is routine, not a failure.

4. Superseded: comment which PR supersedes it, then gh pr close

5. Mechanical: fix in an isolated worktree

Once the bucket is approved, individual stale-generated-file fixes don't need per-PR confirmation. Known Onyx cases:

  • uv bumps (dependabot:python): the exported backend/requirements/*.txt files go stale when only pyproject.toml/uv.lock were bumped. Regenerate via the same pre-commit hooks CI uses:
    bash
    pre-commit run --files pyproject.toml uv.lock backend/requirements/*.txt
    
  • bun bumps (dependabot:javascript): stale bun.lock — run bun install in the bumped directory (repo root or web/).

Getting the branch: prefer the harness's isolated-worktree feature if it has one (Claude Code: EnterWorktree). Otherwise, if the current checkout is clean, work in place — gh pr checkout <pr>, fix, push, and return to the previous branch. If neither applies (e.g. dirty checkout), ask the user where to resolve (AskUserQuestion) rather than picking a spot — and steer away from tmpfs paths like /tmp, which can hit "Disk quota exceeded" mid-post-checkout hook (uv sync/bun install) even when they look roomy.

6. Needs diagnosis: read the failing job's log, then classify

  • Pre-existing — the same job also fails on main's current HEAD (references/graphql-queries.md). Not this PR's problem; leave it and say so.
  • External flake (rate limit, transient network, shared infra) — confirm from the log, rerun once (gh run rerun <runId> --failed). If the same failure recurs, stop and ask — a persisting "flake" may not be one.
  • Real regression from the bump — diagnose the root cause, then ask whether to fix or leave it. Never patch unrelated source just to force CI green.

7. Conflicts

  • Untouched Dependabot branch: comment @dependabot rebase (or recreate) — Dependabot owns the branch and will redo it properly.
  • Branch we already pushed fixes to: @dependabot rebase discards non-Dependabot commits. Instead rebase onto origin/main yourself, rerun the step 5 regeneration if lockfiles conflicted, push with --force-with-lease, and verify git diff origin/main...HEAD --stat still shows only the intended bump. If a pre-push hook trips on a stale local cache (e.g. dev type-gen referencing a file deleted upstream), clear the cache and retry — don't skip the hook.

8. Track to completion

Prefer one polling loop (e.g. Monitor) over repeated manual checks: watch both queue state and PR state (MERGED/CLOSED), and surface new failures as they appear rather than staying silent until success.

9. Report

Merged (N): #A, #B, #C
Closed as superseded (N): #D (superseded by #E)
Left for you (N): #F — <specific unresolved reason>