Skip to main content

Module pr_status

Module pr_status 

Source
Expand description

GitHub PR check-badge resolution for the worktrees tree (#1337).

The engine half of the PR badge, mirroring the crate::worktrees registry / crate::daemon::services::worktrees adapter split: this module is pure resolution — build a query, run gh, reduce the reply, cache the result — and knows nothing about the daemon, the socket, or the tray. The adapter owns the poll loop and the change-notify.

§Why this lives in the daemon

Badges were resolved extension-side (ADR-0050), which made cost scale with the number of open VS Code windows and — the actual bug — meant nothing ever re-asked GitHub after a badge was first computed. Resolving once in the daemon and fanning the answer out over the existing subscribe stream makes cost invariant to window count and lets an unfocused window show live CI state.

§No credential enters the daemon

Resolution shells out to gh api graphql, so the GitHub token stays inside gh where the user already put it — exactly as ADR-0050 wanted, and following ADR-0003’s “shell out to gh/git for GitHub operations”. This is why moving resolution daemon-side does not widen the worktrees service’s no-credential posture (ADR-0040): the daemon never sees a token.

§Why one query for everything

repository(owner:,name:) and ref(qualifiedName:) take no first:/last: argument, so neither is a GraphQL connection and neither contributes to the query’s cost. Aliasing them is free: one query covering every (repo, branch) pair the tree knows about costs 1 point against the 5,000/hour budget, independent of how many repos, worktrees, or windows are open. Measured against rust-works/omni-dev: cost 1 up to ~50 branches, 2 at 100, 4 at 200.

Two shapes are deliberately avoided:

  • gh pr list --json statusCheckRollup, which the extension used, is an alias for a commits(last:1) connection — it adds one request per PR and costs 2.
  • checkSuites { checkRuns { totalCount } } is a third-level connection and costs 11.

§The explicit negative (#1370)

Resolution is tri-state per target, not binary. “No open PR heads this branch” is a successful answer (PrResolution::NoPr), distinct from “never resolved” (absent from the cache). Absence used to carry both meanings, which left a client unable to tell “checked, none” from “old daemon that never checked” — so every window’s degraded gh pr list fallback re-fired per window × repo × 60s forever, burning the very GraphQL budget this module exists to protect. A failed poll still reports nothing: negatives are only ever minted from a successfully parsed reply.

Structs§

MergeInfo
What the merge-queue op needs to know about a branch’s open PR.
PrBadge
The PR badge shown on a worktree row: which PR heads this branch and how its CI is doing.
PrStatusCache
The poller-written, snapshot-read resolution cache.
PrTarget
One (repo, branch) pair to resolve a badge for. Derived from the tree — a repo contributes a target per worktree that has a branch.
QueryRateLimit
The rateLimit block folded into every PR-poll reply (#1389, fix 8).

Enums§

EnqueueOutcome
The outcome of a single enqueuePullRequest mutation.
PrCheckState
The rolled-up CI verdict for a pull request.
PrResolution
The outcome for one successfully checked target (#1370).

Functions§

enqueue_pull_request
Enqueues one PR into its repo’s merge queue via the enqueuePullRequest mutation. Blocking — run on a blocking thread.
resolve_gh_binary
Resolves gh, preferring [GH_BIN_ENV], then the first existing well-known absolute path, then bare gh on PATH.
resolve_merge_targets
Resolves merge-queue eligibility facts for every target in one gh api graphql call. Blocking — run on a blocking thread.
resolve_with
Resolves every target in one gh api graphql call, using the gh at bin.
resolve_with_budget
resolve_with that also returns the poll’s folded-in graphql budget reading (#1389, fix 8).