Skip to content

[feat] Batch GitHub PR status fetching via GraphQL for faster sidebar updates #2958

Closed as not planned

Description

@vaishcodescape

Describe the feature

Problem

The sidebar currently fetches GitHub PR status one workspace at a time using individual gh pr view CLI calls. Each call spawns a child process, waits for the GitHub REST API round-trip, and parses the JSON response — all sequentially gated behind a hover interaction. This means:

  • PR status is invisible until the user hovers over a workspace item.
  • With 10+ workspaces, the N+1 pattern causes noticeable latency and unnecessary GitHub API usage.
  • Each gh pr view call is ~200-400ms, so 15 workspaces = 3-6 seconds of sequential shell spawns.

Solution

Replace the per-workspace gh pr view calls with a single batched GitHub GraphQL query that fetches PR status for all active workspaces at once, then distributes results into per-workspace React Query caches.

Architecture

Main process (Electron)

  • New batchFetchGitHubPRStatuses() function that:
    • Groups all active worktree workspaces by GitHub repository (via cached RepoContext)
    • Builds a single GraphQL query with aliased repository and pullRequests fields
    • Executes via gh api graphql (one shell spawn total, with 30s timeout)
    • Parses response with Zod discriminatedUnion schemas, normalizes GraphQL shape → existing GHPRResponse format
    • Populates the in-memory githubStatusResource cache and persists meaningful changes to SQLite
  • New batchGetGitHubStatuses tRPC procedure exposes this to the renderer

Renderer (React)

  • New useBatchGitHubStatus() hook called at the WorkspaceSidebar level
    • Polls batchGetGitHubStatuses every 15 seconds
    • Seeds empty per-workspace getGitHubStatus React Query cache slots via setData
    • Never overwrites richer data from per-workspace fetches (which include previewUrl, accurate branchExistsOnRemote)
  • Removed hasHovered gate from WorkspaceListItem and WorkspaceRow — PR status appears immediately
  • Aligned workspace-list-item stale time with batch polling interval (15s)

Key design decisions

  1. GraphQL over REST: One query fetches PRs for all repos/branches with status checks and review requests. Stays well within GitHub's 5,000 points/hour rate limit.
  2. Seed-only cache strategy: Batch results only fill empty cache slots. Per-workspace queries (triggered on hover/focus) return richer data — we never regress it.
  3. Discriminated unions: Zod schemas use __typename as the discriminator for CheckRun/StatusContext and User/Team unions — correct GraphQL pattern, better parse performance.
  4. Branch deduplication: Multiple workspaces on the same branch within a repo share a single pullRequests field in the query, with O(1) Map-based dedup.
  5. Graceful degradation: If the batch query fails (auth, network, timeout), the sidebar falls back to the existing per-workspace fetching on hover — zero breakage.

Files changed

File Change
batch-pr-status.ts New — Core batch logic: repo grouping, GraphQL query builder, response parsing, cache population
useBatchGitHubStatus.ts New — React hook for sidebar-level batch polling with seed-only cache distribution
types.ts Added GraphQL Zod schemas (GHGraphQLPRNodeSchema, etc.) and normalizeGraphQLPR()
git-status.ts Added batchGetGitHubStatuses tRPC procedure
index.ts (github) Re-exported batch function and type
pr-resolution.ts Exported formatPRData and sortPRCandidates for reuse
githubQueryPolicy.ts Exported BATCH_GITHUB_STATUS_REFETCH_INTERVAL_MS, aligned stale time
githubQueryPolicy/index.ts Re-exported constant
WorkspaceSidebar.tsx Integrated useBatchGitHubStatus hook
WorkspaceListItem.tsx Removed hasHovered gate from isActive
WorkspaceRow.tsx Removed hasHovered gate from isActive
batch-pr-status.test.ts New — Tests for schema parsing, normalization, and GHPRResponseSchema compat
githubQueryPolicy.test.ts Updated assertions for new stale time values

Performance impact

Metric Before After
Shell spawns per refresh N (one per workspace) 1 (single batch query)
Time to show PR status Hover + 200-400ms per workspace Immediate (pre-populated cache)
GitHub API calls/min N × (60s / staleTime) 4 (one batch every 15s)
User interaction required Hover over each workspace None — status visible on load

Activity

  1. github-actions commented on Mar 27, 2026

    @github-actions
    Contributor

    Triage Notes

    This issue is a feature request, not a bug report — there is no broken behavior to reproduce with a test.

    The current per-workspace PR status fetching logic works correctly via gh pr view / gh pr list calls in apps/desktop/src/lib/trpc/routers/workspaces/utils/github/. The proposed change is a performance optimization to replace N sequential shell spawns with a single batched GraphQL query.

    Current architecture (confirmed by code review):

    • Backend: fetchGitHubPRStatus() in github.ts calls getPRForBranch() in pr-resolution.ts, which tries up to 3 gh CLI strategies per workspace
    • Frontend: WorkspaceListItem and WorkspaceRow use a hasHovered gate — PR status only loads on hover via getGitHubStatusQueryPolicy()
    • tRPC: getGitHubStatus procedure in git-status.ts handles per-workspace fetching with local DB caching

    The issue provides a thorough implementation plan with file-by-file changes, cache strategy, and graceful degradation. No reproduction test is applicable here.

  2. vaishcodescape commented on Mar 27, 2026

    @vaishcodescape
    ContributorAuthor

    @saddlepaddle Can you Take a Look at this ?

  3. Kitenite commented on May 6, 2026

    @Kitenite
    Member

    Thanks for the detailed write-up — closing as a duplicate of #2845, which proposes the same GraphQL batch fetch. Consolidating discussion there.

  4. vaishcodescape commented on May 6, 2026

    @vaishcodescape
    ContributorAuthor

    Hey @Kitenite Could you guide me where I could contribute more in the codebase ? it's a very interesting piece of software and I would love to contribute more !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions