Skip to content

feat(github): collapse live CI reads into a GraphQL status rollup (#1941) - #1980

Merged
JSONbored merged 2 commits into
mainfrom
claude/graphql-status-rollup
Jul 1, 2026
Merged

feat(github): collapse live CI reads into a GraphQL status rollup (#1941)#1980
JSONbored merged 2 commits into
mainfrom
claude/graphql-status-rollup

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

The gate's live CI aggregate (fetchLiveCiAggregate) reads a PR head's status via paginated GET /commits/{sha}/check-runs + GET /commits/{sha}/status + a GET /commits/{sha}/check-suites backstop — 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 statusCheckRollup query — 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:

  • One shared reducer. The entire classification (own-app skip, first-party detection, failing/passing/pending, required-context absence, fold-all validate materialization, the check-suite backstop, fail-closed on incomplete reads) is extracted into a single pure reduceLiveCiAggregate. Both fetchLiveCiAggregate (REST) and fetchLiveCiAggregateViaGraphQl feed it — only the data source differs. The extraction is behavior-preserving: the existing fetchLiveCiAggregate suite passes unchanged.
  • Reuses REST-resolved required contexts. The GraphQL path takes the same branch-protection requiredContexts the REST path already resolves (cached + request-memoized), so required-check semantics are identical — it does not rely on GraphQL's isRequired, sidestepping a real divergence trap (GraphQL reports isRequired=false when there's no protection rule, the opposite of the REST fold-all default).
  • Fails safe to REST on any uncertainty. fetchLiveCiAggregateViaGraphQl returns null — 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).
  • Off by default. 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

Validation

  • git diff --check
  • npm run typecheck
  • npm run cf-typegen (new GITHUB_STATUS_ROLLUP_GRAPHQL var) — worker-configuration.d.ts regenerated + committed
  • npm run test:coverage — new test/unit/graphql-status-rollup.test.ts covers the flag helper, every fetchLiveCiAggregateViaGraphQl branch (null/error/>100/no-commit/unknown-node/checkrun/status/suites), the PreferGraphQl flag 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) asserting graphql deep-equals rest. The fetchLiveCiAggregate refactor stays covered by its existing suite.
  • npm run test:ci
  • npm audit --audit-level=moderate

If any required check was skipped, explain why:

  • No migration: no DB change.

Safety

  • No secrets/wallets/hotkeys/trust-scores/reward values anywhere.
  • Public GitHub text unchanged (verdict is identical to REST).
  • No auth/CORS/session change.
  • OpenAPI unaffected (no route/schema change); the only API surface is the new env flag, typed in env.d.ts + wrangler.jsonc.
  • No UI change.

Notes

  • Guarded path (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.ts change is a one-line entrypoint swap (fetchLiveCiAggregatefetchLiveCiAggregatePreferGraphQl); the 3 processors tests that mocked the aggregate were repointed to that new entrypoint (same mocked result, same assertions).

)

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.
@dosubot dosubot Bot added the size:L label Jul 1, 2026
@loopover-orb

loopover-orb Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-01 03:55:35 UTC

7 files · 1 AI reviewer · no blockers · readiness 68/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change cleanly extracts the REST CI aggregation rules into a shared reducer and wires a flag-gated GraphQL statusCheckRollup path through the queue processor with REST fallback on broad uncertainty. The main behavior-preserving pieces are in place: required contexts still come from the existing REST branch-protection path, incomplete rollup pagination falls back, and the new tests cover direct REST/GraphQL equivalence for the important verdict classes. I do not see a reachable breaking defect in the provided diff, but the GraphQL shape validation is looser than the comments promise in a couple of edge cases.

Nits — 7 non-blocking
  • nit: src/github/backfill.ts:2310 should treat a non-null rollup with missing or non-object pageInfo as an unexpected shape and fall back to REST, since the current `contexts?.pageInfo?.hasNextPage` check silently accepts a response that cannot prove the first 100 contexts are complete.
  • nit: src/github/backfill.ts:2313 should consider falling back when a CheckRun node lacks a non-empty `name`; normalizing that to `""` can create a bogus seen context and does not match the function comment's 'unexpected shape' fallback contract.
  • nit: test/unit/graphql-status-rollup.test.ts:222 says the flag-off path never issues GraphQL, but the stub would let an accidental GraphQL attempt fail and then fall back to REST without failing the test; assert the GraphQL URL is never called.
  • In src/github/backfill.ts:2310, validate `contexts.pageInfo` structurally before reducing, for example require `typeof contexts.pageInfo?.hasNextPage === "boolean"` when `rollup` is non-null.
  • In src/github/backfill.ts:2313, validate required per-node fields for the known union members before pushing them into the reducer, especially CheckRun `name` and StatusContext `context/state`.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:L; no linked issue context).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 68 registered-repo PR(s), 58 merged, 274 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 68 PR(s), 274 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 68 PR(s), 274 issue(s).
  • Related work: Titles/paths share 6 meaningful terms. (issue #1941)
  • Related work: Titles/paths share 7 meaningful terms. (PR #1880)
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Explain no-issue PR.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Triage stale or unlinked PRs.
  • No action.
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 1, 2026
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.61%. Comparing base (9e2f533) to head (b8ff07c).
✅ All tests successful. No failed tests found.

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     
Files with missing lines Coverage Δ
src/github/backfill.ts 95.63% <100.00%> (+0.97%) ⬆️
src/queue/processors.ts 89.61% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

#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.
@dosubot dosubot Bot added the size:L label Jul 1, 2026
@JSONbored
JSONbored merged commit ef039a9 into main Jul 1, 2026
12 checks passed
@JSONbored
JSONbored deleted the claude/graphql-status-rollup branch July 1, 2026 04:31
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 1, 2026
@JSONbored JSONbored linked an issue Jul 1, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

feat(github): collapse status reads with GraphQL rollups

1 participant