fix(github): stop PR-status requests from starving startup connection pool

Watching N worktrees fired N PR-status requests at once (startWatching
called refresh() directly, bypassing the batch limiter). Each request can
take 20s+ under GitHub secondary-rate-limiting, and N of them saturate the
browser's ~6 HTTP/1.1 connections per origin, starving the critical path
(bootstrap session.status, diffs, sending messages) until they finish — the
UI appeared frozen for ~20s on startup.

- Gate all PR-status network calls through a global concurrency semaphore
  (max 2), so free sockets always remain for critical traffic.
- Bound resolveGitHubPrStatus with a 12s timeout so a slow request fails
  fast instead of holding a socket; the client keeps its last-known status.
- Reuse already-fetched repo metadata for the default branch instead of a
  redundant repos.get, reducing serial GitHub calls (less rate-limiting).
This commit is contained in:
Bohdan Triapitsyn
2026-06-28 23:41:14 +03:00
parent f040ea85e6
commit 9f720c66af
3 changed files with 84 additions and 7 deletions
@@ -160,6 +160,17 @@ const getRepoDefaultBranch = async (octokit, repo) => {
return cached.defaultBranch;
}
// Reuse the full repo metadata if it was already fetched (expandRepoNetwork
// calls getRepoMetadata for every candidate before the default-branch loop).
// This avoids a redundant repos.get per repo — fewer serial GitHub calls means
// less exposure to secondary-rate-limiting that makes PR status slow.
const metaCached = repoMetadataCache.get(repoKey);
if (metaCached && Date.now() - metaCached.fetchedAt < REPO_DEFAULT_BRANCH_TTL_MS) {
const defaultBranch = normalizeText(metaCached.data?.default_branch) || null;
defaultBranchCache.set(repoKey, { defaultBranch, fetchedAt: Date.now() });
return defaultBranch;
}
try {
const response = await octokit.rest.repos.get({
owner: repo.owner,