Repository navigation
feat(github): poll PRs via one GraphQL call per repo - #42
Merged
Merged
Conversation
…estWithChecks Additive step toward dropping the raw checks array from the polled PR shape: aggregatePrWithChecks derives the two new fields, and every fixture/test literal gains them (values coherent with each literal's existing counts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One PollOpenPrs query returns open PRs + check rollups + latest reviews + counts per repo. The adapter maps rollup contexts to CheckRun-shaped lites and reuses aggregatePrWithChecks, so status semantics (including cancelled-as-non-blocking) match the REST path exactly. rateLimit sits at the query root for the client to read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All consumers move off the raw checks array: lengths become totalCheckCount, rerun lookups read failedCheckSuiteIds[0]. The PR-detail pop-out keeps raw CheckRun[] as window-local state threaded to ChecksTab via a new optional PrDetailPanel prop; getPRWithChecks now returns { pr, checks } and PR_REFRESHED_EVENT forwards the runs so the Checks tab stays fresh after mutations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the REST fan-out (open-PRs list + 2N hydration + N check-runs ≈ 22 calls/repo/cycle) with a single PollOpenPrs query. The prior-state snapshot now backs a repo-level fallback: a failed poll keeps that repo's last-known PRs instead of dropping them for a cycle, which previously caused spurious new-PR notifications on recovery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…raphql calls GitHub's REST and GraphQL quotas are separate 5000/h pools; the client now tracks them independently (getGraphqlRateLimit) and isRateLimitLow trips on either, so adaptive polling slows when the GraphQL pool runs low. graphql() gains GET-style start/ok/failed log lines (with query name, duration, point cost), reads the inline root rateLimit field, and now raises GitHubAuthError / GitHubRateLimitError like the REST verbs so token-cache invalidation keeps working on a GraphQL-only poll path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e ids replace it The raw CheckRun array no longer rides along on every polled PR; consumers read totalCheckCount / failedCheckSuiteIds instead. Raw runs survive only as the PR-detail window's local state (REST cold path) and component props. Fixtures export openPrChecks / approvedPrChecks for stories that mock the check fetch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fetchGitHubRateLimit returns both pools; the settings panel shows two labeled bars, and the polling hook surfaces whichever pool is tighter to the sidebar's rate-limit display (the REST pool no longer updates during polling, so it alone would go stale). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirrors the existing per-submodule mock pattern so the barrel re-export of pollOpenPrsAggregate never pulls the real module into story bundles. Also ports vite.config.ts's package-manager-agnostic web-tree-sitter wasm resolution to the storybook config — the literal node_modules path only worked when the package happened to be nested under the app dir. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…stall, e2e port reuse Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cached PullRequestWithChecks entries written before the polling migration carry a raw checks array and lack totalCheckCount / failedCheckSuiteIds; the init sequence seeds the store from this cache, and failedCheckSuiteIds[0] crashed PrCardContainer at boot (ErrorBoundary unmounted the tree before the first poll could overwrite the stale entries). Normalize at the single load boundary by re-deriving the summary through aggregatePrWithChecks from the legacy checks array. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…in e2e The e2e harness answers all api.github.com traffic with a bare [] body, so client.graphql resolved with undefined data and the adapter TypeError'd into a console.error every poll cycle, tripping the render-smoke boot contract on CI. The adapter now degrades to an empty result (warn) when repository.pullRequests is absent, and the e2e route answers /graphql with a well-formed empty repository so cycles no-op like the REST path used to. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Collapses the GitHub polling fan-out (~22 REST calls per repo per cycle: open-PR list + 2N detail/review hydration + N check-runs) into a single
PollOpenPrsGraphQL query per repo, while preserving every UI-visible field and feature. Implementsdocs/superpowers/specs/2026-05-07-github-graphql-polling-design.md.Key changes
services/github/polling.ts— owns the query and the response adapter. Rollup contexts are mapped toCheckRun-shaped objects and fed through the existingaggregatePrWithChecks, so status semantics (including cancelled-as-non-blocking from 28f0294) match the REST path by construction.PullRequestWithChecksslims down — drops the rawchecks: CheckRun[]array; gainstotalCheckCountandfailedCheckSuiteIds(what the rerun actions actually need). Raw runs survive only as the PR-detail pop-out's local state (REST cold path), threaded toChecksTabvia a new optionalPrDetailPanelprop;getPRWithChecksreturns{ pr, checks }andPR_REFRESHED_EVENTforwards the runs so the Checks tab stays fresh after mutations.isRateLimitLowtrips on either (adaptive polling),graphql()gainsstart/ok/failedlog lines with query name + point cost and properGitHubAuthError/GitHubRateLimitErrorhandling. Settings shows both pools; the sidebar surfaces the tighter one.PrCardContainerat boot (failedCheckSuiteIds[0]);loadCachedPRsnow re-derives the summary from the legacy checks array.build-storybookunder hoisted installs.Deliberate behavior changes
commentCounton polled PRs is now real (comments + reviewThreadstotals) — the REST list endpoint had no comment fields, so it was always 0 until a single-PR refresh.mergeable: UNKNOWNmaps toundefined(nomergeable_statefallback in GraphQL); resolves on a later poll.labels(20),reviewRequests(20),latestReviews(50),contexts(100)per PR, 100 PRs per repo — truncation past these logs a warning.Verification
tsc, biome, andbuild-storybookgreen; typecheck gated every commit via lefthookgraphql start/okpair per repo per cycle (cost=5, GraphQL pool decrementing), zero RESTpulls?state=open/check-runscalls in steady state, repo-failure fallback exercised🤖 Generated with Claude Code