poll-pr-checks.sh queries statusCheckRollup.contexts(first: 100) with no cursor and bails OVERFLOW → exit 2 when totalCount > 100. On a repo whose full CI matrix registers more than 100 contexts, that makes the script permanently unusable on exactly the PRs it matters most for — and the sessions that hit it improvise a replacement poller in-flight, losing the grouping and self-filtering logic the script exists to provide.
Observed twice on PRQL/prql in the 2026-08-15 Dependabot batch, on both reviews that were gated on CI before approving:
The split is systematic rather than incidental: a Dependabot bump touches Cargo.lock, so every path filter opens and the whole matrix (including the nightly release builds) registers — 115–116 contexts. Path-filtered bot PRs in the same window stayed well under the page: PRQL/prql#6199 at 36, PRQL/prql#6198 at 41, PRQL/prql#6194 at 43, PRQL/prql#6193 at 87. So the primitive is available on the PRs a human will look at anyway, and unavailable on the external-dependency bumps tend approves autonomously. The two runs above were the only two gated approvals in that 24h window, and the script refused on both.
No wrong verdict reached the record — both sessions recovered, and PRQL/prql#6196's improvised poller is what caught test-msrv going red four minutes after the approval, which the session then dismissed, diagnosed and fixed. The cost is that each session re-derives a safety-critical primitive under time pressure, and the two derivations were not the same: 31891406039 fell back to gh pr view --json statusCheckRollup, while 31891399008 hand-wrote a GraphQL cursor loop into /tmp/poll6196.sh. Neither inherits the reduction the script's header argues for at length — grouping per (check name, workflow), latest-wins within a settled group, $GITHUB_RUN_ID/$GITHUB_WORKFLOW self-filtering — which is what keeps a concurrency-cancelled sibling from reading as red. On this repo that is not hypothetical: an if: always() omnibus (check-ok-to-merge) sat at FAILURE from a cancelled run for the whole of both sessions.
The guard itself is right — UNVERIFIED beats a false green when a dropped node could hide a failure. The gap is that the query never asks for the next page.
Suggested fix and verification notes
The query already has everything but the cursor. Adding pageInfo { hasNextPage endCursor }, looping with after: while accumulating nodes, then dropping the "OVERFLOW" branch (scripts/poll-pr-checks.sh:91), too_many_contexts() (:131-135), its two call sites (:144, :152) and the exit-2 clause in the header comment (:39-40) removes the failure mode rather than raising the constant. totalCount stops being load-bearing once the loop terminates on hasNextPage.
gh pr view --json statusCheckRollup is not a drop-in substitute, even though it does paginate — I verified it returns all 115 nodes on 0aff65a7 where the single-page GraphQL query reports totalCount: 115, hasNextPage: true. The script deliberately queries by commit OID rather than through the PR (scripts/poll-pr-checks.sh:7-10), and gh pr view follows the current head, which is the exact bug that pinning was added to prevent.
A repo-side workaround (raising the page size, or an overlay note telling sessions to hand-roll) would leave every other consumer with a large matrix in the same place, which is why this is filed here rather than on PRQL/prql.
Reproduce against any commit on this repo:
gh api graphql -f query='
query($o:String!,$r:String!,$s:String!){
repository(owner:$o,name:$r){ object(expression:$s){ ... on Commit {
statusCheckRollup { contexts(first:100){ totalCount pageInfo{hasNextPage} } } } } }
}' -f o=PRQL -f r=prql -f s=0aff65a743f11a02f061a09e1fd40ad13dc8f487 \
--jq '.data.repository.object.statusCheckRollup.contexts'
# {"pageInfo":{"hasNextPage":true},"totalCount":115}
poll-pr-checks.shqueriesstatusCheckRollup.contexts(first: 100)with no cursor and bailsOVERFLOW→ exit 2 whentotalCount > 100. On a repo whose full CI matrix registers more than 100 contexts, that makes the script permanently unusable on exactly the PRs it matters most for — and the sessions that hit it improvise a replacement poller in-flight, losing the grouping and self-filtering logic the script exists to provide.Observed twice on
PRQL/prqlin the 2026-08-15 Dependabot batch, on both reviews that were gated on CI before approving:contexts.totalCount472a1925exit 2— UNVERIFIED0aff65a7exit 2— UNVERIFIEDThe split is systematic rather than incidental: a Dependabot bump touches
Cargo.lock, so every path filter opens and the whole matrix (including thenightlyrelease builds) registers — 115–116 contexts. Path-filtered bot PRs in the same window stayed well under the page: PRQL/prql#6199 at 36, PRQL/prql#6198 at 41, PRQL/prql#6194 at 43, PRQL/prql#6193 at 87. So the primitive is available on the PRs a human will look at anyway, and unavailable on the external-dependency bumps tend approves autonomously. The two runs above were the only two gated approvals in that 24h window, and the script refused on both.No wrong verdict reached the record — both sessions recovered, and PRQL/prql#6196's improvised poller is what caught
test-msrvgoing red four minutes after the approval, which the session then dismissed, diagnosed and fixed. The cost is that each session re-derives a safety-critical primitive under time pressure, and the two derivations were not the same: 31891406039 fell back togh pr view --json statusCheckRollup, while 31891399008 hand-wrote a GraphQL cursor loop into/tmp/poll6196.sh. Neither inherits the reduction the script's header argues for at length — grouping per (check name, workflow), latest-wins within a settled group,$GITHUB_RUN_ID/$GITHUB_WORKFLOWself-filtering — which is what keeps a concurrency-cancelled sibling from reading as red. On this repo that is not hypothetical: anif: always()omnibus (check-ok-to-merge) sat atFAILUREfrom a cancelled run for the whole of both sessions.The guard itself is right — UNVERIFIED beats a false green when a dropped node could hide a failure. The gap is that the query never asks for the next page.
Suggested fix and verification notes
The query already has everything but the cursor. Adding
pageInfo { hasNextPage endCursor }, looping withafter:while accumulatingnodes, then dropping the"OVERFLOW"branch (scripts/poll-pr-checks.sh:91),too_many_contexts()(:131-135), its two call sites (:144,:152) and the exit-2 clause in the header comment (:39-40) removes the failure mode rather than raising the constant.totalCountstops being load-bearing once the loop terminates onhasNextPage.gh pr view --json statusCheckRollupis not a drop-in substitute, even though it does paginate — I verified it returns all 115 nodes on0aff65a7where the single-page GraphQL query reportstotalCount: 115, hasNextPage: true. The script deliberately queries by commit OID rather than through the PR (scripts/poll-pr-checks.sh:7-10), andgh pr viewfollows the current head, which is the exact bug that pinning was added to prevent.A repo-side workaround (raising the page size, or an overlay note telling sessions to hand-roll) would leave every other consumer with a large matrix in the same place, which is why this is filed here rather than on
PRQL/prql.Reproduce against any commit on this repo: