feat(github): collapse live CI reads into a GraphQL status rollup (#1941) - #1980
Conversation
) The gate's live CI aggregate reads a head's status via paginated /check-runs + /status + a /check-suites backstop — 3-8 REST calls on the core rate-limit bucket per PR per sweep. Add a flag-gated GraphQL path that collapses them into one statusCheckRollup query on the separate GraphQL points bucket. Equivalence by construction: the entire classification is extracted into one pure reduceLiveCiAggregate that BOTH the REST and GraphQL paths feed, so the verdict is byte-identical (the existing fetchLiveCiAggregate suite passes unchanged). The GraphQL path reuses the REST-resolved required contexts (no isRequired divergence) and returns null — falling back to the proven REST aggregate — on any error, unexpected shape, or >100 rollup contexts. Gated OFF by default (GITHUB_STATUS_ROLLUP_GRAPHQL): byte-identical deploy.
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-01 03:55:35 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1980 +/- ##
==========================================
+ Coverage 95.57% 95.61% +0.03%
==========================================
Files 218 218
Lines 24257 24298 +41
Branches 8795 8816 +21
==========================================
+ Hits 23184 23232 +48
Misses 436 436
+ Partials 637 630 -7
🚀 New features to boost your workflow:
|
#1941) fetchLiveCiAggregateViaGraphQl promised to fall back on any unexpected shape but normalized malformed/partial data to empty inputs. A 200 with a top-level GraphQL errors array (a field resolver failing → statusCheckRollup null) would read as 'no checks' and could let the gate merge a PR whose CI is actually failing. Now it returns null (→ REST) when: the response carries top-level errors, the object is not a resolved Commit (checkSuites nodes absent/non-array), or a non-null statusCheckRollup carries a malformed contexts connection. The one legitimate empty case (statusCheckRollup null on a check-less commit) still maps to 'unverified', matching REST. Adds tests for each fall-back path.
Summary
The gate's live CI aggregate (
fetchLiveCiAggregate) reads a PR head's status via paginatedGET /commits/{sha}/check-runs+GET /commits/{sha}/status+ aGET /commits/{sha}/check-suitesbackstop — 3–8 REST calls against the core rate-limit bucket, per PR, per sweep. This is the biggest avoidable status-read cost (#1941).This adds a flag-gated GraphQL path that collapses those reads into one
statusCheckRollupquery — which lands on GitHub's separate GraphQL points bucket, so the hot status path stops competing with webhooks for the core budget.Correctness — provably equivalent, not a reimplementation
The gate merge/close decision reads this (a guarded path), so the GraphQL path must be byte-identical to REST. It is, by construction:
validatematerialization, the check-suite backstop, fail-closed on incomplete reads) is extracted into a single purereduceLiveCiAggregate. BothfetchLiveCiAggregate(REST) andfetchLiveCiAggregateViaGraphQlfeed it — only the data source differs. The extraction is behavior-preserving: the existingfetchLiveCiAggregatesuite passes unchanged.requiredContextsthe REST path already resolves (cached + request-memoized), so required-check semantics are identical — it does not rely on GraphQL'sisRequired, sidestepping a real divergence trap (GraphQL reportsisRequired=falsewhen there's no protection rule, the opposite of the REST fold-all default).fetchLiveCiAggregateViaGraphQlreturnsnull— and the caller falls back to the proven REST aggregate — on a missing token/owner, a GraphQL error, an unexpected shape, or >100 rollup contexts (a single page can't enumerate them; REST paginates).GITHUB_STATUS_ROLLUP_GRAPHQL=false→ the gate uses the REST aggregate, byte-identical deploy.Advances #1941 (the headline status-read collapse). Once enabled per-deployment, REST core-bucket pressure on the status path drops to ~0.
Scope
CONTRIBUTING.md; nosite//CNAME.Validation
git diff --checknpm run typechecknpm run cf-typegen(newGITHUB_STATUS_ROLLUP_GRAPHQLvar) —worker-configuration.d.tsregenerated + committednpm run test:coverage— newtest/unit/graphql-status-rollup.test.tscovers the flag helper, everyfetchLiveCiAggregateViaGraphQlbranch (null/error/>100/no-commit/unknown-node/checkrun/status/suites), thePreferGraphQlflag routing + REST fallback, and a 7-scenario REST↔GraphQL equivalence matrix (all-green / failed-check / failed-status / pending-required / pending-non-required / missing-required / suite-in-progress) assertinggraphqldeep-equalsrest. ThefetchLiveCiAggregaterefactor stays covered by its existing suite.npm run test:cinpm audit --audit-level=moderateIf any required check was skipped, explain why:
Safety
env.d.ts+wrangler.jsonc.Notes
src/github/backfill.ts/ the gate's CI aggregate): expected to be held for owner review — the equivalence matrix + behavior-preserving refactor are the safety argument.src/queue/processors.tschange is a one-line entrypoint swap (fetchLiveCiAggregate→fetchLiveCiAggregatePreferGraphQl); the 3 processors tests that mocked the aggregate were repointed to that new entrypoint (same mocked result, same assertions).