fix(server): background PR checks spend less GitHub quota - #13189
Conversation
Branch PR lookups without an open PR now stay cached for 5 minutes instead of 1, so idle branches stop costing a GraphQL call on every sweep. In-app paths that can open a PR (turn end, push, create PR, user refresh) still bypass the cache. The settlement sweep only re-checks a branch uncached when the linked PR is terminal and the group would actually settle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused server-side optimization that reduces background GitHub requests with a bounded five-minute cache for non-open results and preserves explicit refresh behavior. The settlement guard and cache semantics are covered by targeted tests, with no schema, security, billing, or deployment impact. You can add or adjust custom eligibility rules. Learn more. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configurationConfiguration used: Repository: pingdotgg/t3code/.coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: pingdotgg/t3code/.coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review. 📝 WalkthroughWalkthroughBranch pull-request caching now uses different durations for open and terminal or absent results. Thread settlement groups check whether settlement can occur before refreshing branch data and reuse normalized results. Tests cover cache expiry and settlement behavior for a resumed thread. ChangesPull-request lookup and settlement
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to No actionable merge-blocking issue is established. Normal checks should confirm the cache timing and settlement tests before merging. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
…anch A group that only became eligible after the branch recheck was skipped could settle on a stale terminal link. It now waits for the next sweep. Tests pin the zero-lookup case and both sides of the 5-minute TTL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 5294f96
Merges `pingdotgg/t3code` at `aca3c87cd` into the fork — 62 commits from base `5a61f50cc`. Landed 339 files (8865+/6131-) against 335 in the upstream range; fork delta unchanged at 786 files. The gap of 4 is the three `docs/fork` files below plus `ThreadStatusIndicators.test.tsx`, which auto-merged clean while still passing the `variant` prop upstream deleted. `verify.mjs`: all 10 checks pass, tests included. ## Conflicts Six, each resolved with the verdict `preflight.mjs` printed. | Path | Verdict | Resolution | | --- | --- | --- | | `DiffPanel.tsx` | converged — diff-panel-gates | Upstream rewrote the scope dropdown as `DropdownMenuRadioGroup` / `DropdownMenuRadioItem` with the per-turn list in a submenu. Took it whole; re-applied the two `FEATURES.turnDiffs` gates at their new anchors. | | `LegacySidebar.tsx` | converged — mobile-touch-upstream-files | Upstream deleted the add-project button's icon-color className. Took upstream's button inside the fork's `FEATURES.projectManagement` wrapper. | | `ThreadStatusIndicators.tsx` | converged — thread-status-indicators | Upstream swapped `variant` for a `render` prop and extracted `PullRequestBadge`. The fork's several-links popover is now a sibling `PullRequestLinksBadge`; both render a shared `PullRequestBadgeFace`, extracted so `duplicate-adds.mjs` does not read the shared icon-and-text span as a merge artifact. | | `settings/ProjectActionsList.tsx` | unlisted → decide-then-add-entry | pingdotgg#13029 restyled the Edit button to `variant="ghost-muted"`. Took upstream's button, kept the `editable` wrapper, added a `project-actions-list` inventory entry. | | `settings/ProjectDefaultsSettings.tsx` | converged — project-defaults-settings | Took upstream whole, re-applied the five `workspaceOwnsProjectDefaults` gates. Upstream now renders the model and workspace rows from both a `category === "project"` and a `category === "general"` branch, so each gate exists twice. | | `settings/ProjectSettingsPanel.tsx` | converged — project-settings-panel | Upstream's new info Alert is unconditional and first; its new `<ProjectDefaultsSettings category="project" />` section is gated on `workspaceSettings` as a sibling rather than a fragment, because folding it in would have re-indented upstream's JSX. | `pnpm-lock.yaml` auto-merged and was re-derived with `install.mjs`, which moved `type-fest` 5.7.0 → 5.10.0 in two msw snapshot blocks and nothing else. The fork's `moatless-api` and `mermaid` edges survive. Sweep: one hit — `apps/server/src/provider/ProviderAuthFlow.ts` and its test, new from pingdotgg#12983 on the keyword `auth`. A false positive for the auth-session concern (a provider CLI's own sign-in, not the user session), taken as-is, and recorded under _Provider setup_ in the gaps because it adds `provider.auth.respond`. Tripwires steady; no new upstream workflows; no stale inventory entries. Unsupported methods: ADD and DROP both empty, KEEP unchanged at two. No `rpc.ts` union edits — `provider.auth.respond` took `ProviderSetupRpcError`, which already carries `UnsupportedMethodError`. ## Feature classification ### Usable as-is - **The web component-library pass** — roughly 24 commits (pingdotgg#12984–pingdotgg#13043) over `apps/web/src/components/ui/*`: button variants and sizes, dropdown radio groups and submenus, popover and tooltip `render` props. Nothing here touches the wire; it is the source of four of the six conflicts and all of them were restyles. - **Settings scope sentence and scope pickers in breadcrumbs** — `242816af8` and `db9a0671b`, including the new `SettingsScopeSentence.tsx`. Reads settings the fork already serves. - **Small web fixes** — `219c1d265`, `6975efd3d`, `68607c5a9`, `b954af60c`, `7c2702d68` + `da6a85b13`, `aff9318bf`, `438bf466f`. - **Every mobile-only commit**, and the non-targets: `e4422eec7` (desktop), `d7819c188` (device-hub bump, moot while `FEATURES.deviceHub` is off), `ca864a25b` / `f25a8e4b7` / `17e34773b` (model manifest). ### Unsupported in Moatless / needs implementation - **`7e65b226e` feat(auth): share provider sign-in flows and credential bindings (pingdotgg#12983).** Adds `provider.auth.respond` (`WS_METHODS.providerAuthRespond`) and `ProviderAuthRespondInput`, reshapes `provider.auth.start`'s payload from `ProviderSetupInput` to `ProviderAuthStartInput`, and adds `methods`, `interaction` and `credentialOwner` to `ProviderAuthState`. Server-only implementation in `apps/server/src/provider/ProviderAuthFlow.ts` and `ProviderCredentialStore.ts`. It makes a provider sign-in interactive — the server asks, the client answers through the new method — which is the same shape as the nine `provider.auth.*` / `provider.install.*` methods the backend already refuses. It falls under the existing _Provider setup_ gap, now listing ten methods; it needed no union edit because it took the same error type as its siblings. Closes if Moatless ever manages provider credentials on the client's behalf. ### Backend behavior to consider reproducing in Moatless All six are now bullets under _Runtime fixes upstream made to its own server_ in `docs/fork/gaps.md`, with the closing condition on each. Four are GitHub quota work. - **`96c4bfa0a` provider compatibility advisory** (pingdotgg#13130, `apps/server/src/provider/providerCompatibility.ts`) — a per-driver policy on the model manifest yields a `supported` / `unsupported` / `broken` advisory on the published `ServerProvider`, rendered above the composer. Moatless installs the provider CLIs, so it is the side that knows the version. - **`f193a6863` bypass owned caches on an explicit provider refresh** (pingdotgg#13109) — splits `server.refreshProviders` by its existing `refreshModels` flag: a user refresh force-refreshes the model manifest and version cache, background polls keep their timers. The flag is already on the contract. - **`eafb4a934` GitHub PR lookups stop probing owner-qualified heads** (pingdotgg#13200) — `gh pr list --head` answers an `owner:branch` selector with nothing while still spending a GraphQL call; upstream drops those selectors and widens the remaining probe to 100, which GitHub prices like `first:1`. - **`18de6bb32` batched PR summary reads** (pingdotgg#13198) — summaries batch behind a 10ms request window and a per-batch GraphQL query, with the sync reactor's concurrency raised 8 → 25 so a sweep's reads land in the same window. - **`5975ec78b` `wouldSettle` before an uncached PR re-query** (pingdotgg#13189) — a settlement sweep pays for the reused-branch-race lookup only when some thread in the group would actually settle. - **`f22331240` three-dot PR diffs** (pingdotgg#13170) — `readRangeContext` moved its diff stat and patch to `base...HEAD` while leaving the commit log two-dot, so a PR description written after the base advanced describes the branch's own changes. One character per command. ## Docs - `docs/fork/inventory.json` — new `project-actions-list` entry; `provider-settings-gates` gained `ProviderModelsSection.tsx` and `ProviderSettingsPanel.environment.test.tsx`. - `docs/fork/gaps.md` — _Provider setup_ names `provider.auth.respond` as its tenth method; six new bullets under _Runtime fixes upstream made to its own server_. - `docs/fork/upstream-merge-log.md` — dated entry. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Moatless task: https://moatless.soaplabstest.com/tasks/5258e642-4bee-4f38-878b-747496b2d2ea
## What's Changed * chore(mobile): drop dead nitro-markdown tgz override and @expo/metro-runtime by @juliusmarminge in pingdotgg/t3code#13148 * feat(web): show settings scope as a sentence at the top of the page by @juliusmarminge in pingdotgg/t3code#13139 * refactor(web): move settings scope pickers into breadcrumbs by @Yash-Singh1 in pingdotgg/t3code#13165 * feat(auth): share provider sign-in flows and credential bindings by @juliusmarminge in pingdotgg/t3code#12983 * refactor(mobile): git sheets use uniwind platform variants instead of className ternaries by @juliusmarminge in pingdotgg/t3code#13161 * chore(mobile): name the two project favicon caches by their job by @juliusmarminge in pingdotgg/t3code#13160 * revert(mobile): git sheets back to Platform.OS ternaries (un-guarded uniwind variants broke both platforms) by @juliusmarminge in pingdotgg/t3code#13169 * docs(mobile): document the two mobile routes that intentionally skip deep links by @juliusmarminge in pingdotgg/t3code#13164 * refactor(mobile): break module cycles with focused extractions by @juliusmarminge in pingdotgg/t3code#13151 * fix(server): generate PR diffs from branch changes by @Yash-Singh1 in pingdotgg/t3code#13170 * fix(web): preserve nested scroll behavior in chat timeline by @Yash-Singh1 in pingdotgg/t3code#13167 * test(web): cover usage model ordering without static markup by @flamboh in pingdotgg/t3code#13104 * fix(desktop): find linuxbrew node for the WSL backend by @CodyRay in pingdotgg/t3code#7827 * chore(models): use GPT-6 Luna for text generation by @extoci in pingdotgg/t3code#13115 * fix(mobile): keep ordinary offline outbox failures out of console.warn by @juliusmarminge in pingdotgg/t3code#13144 * feat(providers): check remote compatibility ranges by @juliusmarminge in pingdotgg/t3code#13130 * chore(lint): keep mobile theme escape-hatch allowlist honest by @juliusmarminge in pingdotgg/t3code#13146 * fix(web): the pull request badge reads at the meta size again by @juliusmarminge in pingdotgg/t3code#13175 * fix(mobile): uniwind platform variants stay guarded on both platforms by @juliusmarminge in pingdotgg/t3code#13172 * refactor(mobile): git sheets use uniwind platform variants instead of className ternaries by @juliusmarminge in pingdotgg/t3code#13185 * refactor(mobile): remaining className platform ternaries become class variants by @juliusmarminge in pingdotgg/t3code#13188 * fix(web): align provider emails without clipping by @Derpedyea in pingdotgg/t3code#13174 * perf(mobile): recycle the default v2 home list and scope the snooze minute tick by @juliusmarminge in pingdotgg/t3code#13149 * refactor(mobile): retire the legacy grouped thread list by @juliusmarminge in pingdotgg/t3code#13183 * fix(server): background PR checks spend less GitHub quota by @juliusmarminge in pingdotgg/t3code#13189 * fix(server): background PR sync reads summaries in batches by @juliusmarminge in pingdotgg/t3code#13198 * fix(server): GitHub PR lookups stop probing owner-qualified heads by @juliusmarminge in pingdotgg/t3code#13200 * chore(mobile): clear the legacy-list deletion fallout by @juliusmarminge in pingdotgg/t3code#13203 ## New Contributors * @CodyRay made their first contribution in pingdotgg/t3code#7827 **Full Changelog**: pingdotgg/t3code@v0.0.43-nightly.20260922.2123...v0.0.43-nightly.20260923.2135 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.43-nightly.20260923.2135
…13189) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Every T3 server re-asks GitHub about every unsettled branch once a minute (
gh pr list --head, a GraphQL call). The PR lookup cache TTL was also 60s, so nearly every sweep missed it. The settlement sweep added an uncached re-check for every thread whose linked PR was merged or closed, whether or not the thread would settle. Several servers share one token, so traces showed ~2000–2900ghcalls/hour per machine and repeated GraphQL rate limiting.Changes
Tests
GitManager.test.tsandThreadSettlementReactor.test.ts.retains protected worktrees …storage-cleanup tests inThreadSettlementReactor.test.tsfail onmaintoo. This PR does not change them.Estimated savings
These are estimates from existing server traces (~20 min per machine). Each machine's current unsettled branches were checked against the open PRs in
pingdotgg/t3code.Model
gh pr listper minute. After, branches with an open PR still cost 1 per minute and the rest cost 1 per 5 minutes.PullRequestSyncReactor, status refresh and checks.Where the savings come from
PullRequestSyncReactor(onegh pr viewper open linked PR per minute), which this PR does not touch.PullRequestSyncReactor, so this PR does nothing for it.Caveats
Next: batch
PullRequestSyncReactorinto one aliased GraphQL query per repo per sweep. That would take cups from ~1900/h ofgh pr viewto roughly 100/h, and with the v2 port the fleet would sit under ~1500/h.Follow-ups: port to the v2 services, and batch the per-PR
gh pr viewinPullRequestSyncReactor.Done with Claude Opus 5.5 in Claude Code via T3 Code.
🤖 Generated with Claude Code
Summary by CodeRabbit