Skip to content

pr-auto-review ready-check counts non-required/cancelled checks → false 'not passing', blocks auto-dispatch #680

Description

@don-petry

Bug

The pr-auto-review ready-check (pr-auto-review-reusable.yml, check-and-dispatch job) gates dispatch on every check passing:

CHECKS=$(gh pr checks "$PR_URL" --json bucket,name ...)
# map(select(.name != $self)) | map(select(.bucket != "pass" and .bucket != "skipping")) | length

This counts non-required and cancelled check contexts as "not passing", so it refuses to dispatch the review agent on PRs that are actually mergeable.

Repro (observed on #678)

dev-lead's review-changes commits carry [skip ci-relay], and dev-lead's per-PR concurrency cancels superseded runs. Each leaves stale dev-lead / ci-relay + dev-lead / dispatch check runs in state CANCELLED. These contexts are not in the branch ruleset's required set (required = SonarCloud, CodeQL, AgentShield, Detect ecosystems, Lint, ShellCheck, Agent Security Scan). Yet the ready-check reported 4 of 24 check(s) are not yet passing — skipping and never dispatched, even though the PR was mergeable: MERGEABLE with all required checks green, review-approved-ready, zero unresolved threads.

Impact: the auto-approval path silently fails for exactly the PRs that go through a review round-trip (multiple dev-lead runs → concurrency cancellations). Had to fall back to manually dispatching the review agent + a normal merge.

Fix options (pick one, prefer A)

A. Only count REQUIRED checks. Fetch the branch's required contexts (ruleset rules/branches/<branch>required_status_checks) and evaluate the passing-gate against that set, not all contexts. Most correct — mirrors what actually blocks merge.

B. Treat cancel (and skipping, already handled) as non-blocking. Add .bucket != "cancel" to the filter. Simpler, but still counts non-required failing checks. A cancelled advisory run is not a signal.

C. Use mergeStateStatus. Gate on BLOCKED vs CLEAN/UNSTABLE from the GraphQL merge state — UNSTABLE (non-required failing) is mergeable. Leans on GitHub's own required-vs-not determination.

Recommend A (explicit, testable) with B as a cheap immediate mitigation. Whatever the choice, extract the passing-gate into a pure helper + bats (the house pattern — Layer 0 of #668) so the decision matrix is pinned.

Acceptance

  • A PR with all required checks green + approved-ready + no unresolved threads dispatches the review agent even when a non-required or cancelled advisory context (e.g. dev-lead / ci-relay) is present.
  • Required-check failure/pending still blocks (unchanged).
  • Decision logic unit-tested.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dev-leadFor dev-lead agent pickup

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions