feat(code): cache per-task PR url + state for instant task switches#2343
Open
richardsolomou wants to merge 2 commits into
Open
feat(code): cache per-task PR url + state for instant task switches#2343richardsolomou wants to merge 2 commits into
richardsolomou wants to merge 2 commits into
Conversation
Switching between tasks used to block on a `gh pr list --head <branch>` call to figure out which PR the task corresponds to. That made the PR badge / "Open PR" button pop in late on every task switch, especially after a cold start where TanStack Query's in-memory cache is empty. This stores the last-known PR URL and state on the workspace row in SQLite and lets the renderer paint from that cache immediately, while a background revalidation refreshes the value against GitHub. When the revalidated value differs from cache, `WorkspaceService` broadcasts `taskPrInfoChanged` and the renderer updates the matching `getTaskPrStatus` query in place. - New `pr_url` / `pr_state` / `pr_fetched_at` columns on `workspaces` (migration `0007_stiff_reptil`) plus `updatePrCache` on the repository - `GitService.getTaskPrStatus` returns cached state synchronously and schedules a deduplicated background `revalidateTaskPrStatus` that writes through and emits on change; `hasDiff` for worktree tasks still computes inline since it's local-only - `workspace.onTaskPrInfoChanged` tRPC subscription + `App.tsx` handler pushes fresh values into the `getTaskPrStatus` cache via `setQueriesData` - `workspace.getCachedPrUrl` lets `useTaskPrUrl` fall back to the cached PR URL so the task header opens the right PR before the live lookups return - `taskPrInfoChanged` is emitted via string literal to avoid a circular import (workspace/service eagerly loads the DI container) Generated-By: PostHog Code Task-Id: 10fff210-3861-49ad-b1fc-dbabae2fcc17
Contributor
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/code/src/main/services/git/service.ts:1858-1862
The "no-op" guard uses `!fresh.hasDiff` to skip emitting, but since `hasDiff` is never persisted to the DB there is no baseline to compare against. For a worktree task that has uncommitted changes but no PR (`prUrl: null, prState: null, hasDiff: true`), this condition will always be `false`, so `updatePrCache` and `emit("taskPrInfoChanged")` are called on every revalidation cycle even when nothing has actually changed since the last one. The repeated `setQueriesData` calls in `App.tsx` are harmless because React detects identical data, but the extra DB writes accumulate silently.
```suggestion
const cachedHasDiff = !cachedPrState && !cachedPrUrl;
if (
cachedPrUrl === fresh.prUrl &&
cachedPrState === fresh.prState &&
cachedHasDiff === fresh.hasDiff
) {
```
### Issue 2 of 2
apps/code/src/renderer/App.tsx:132-152
The `prUrl` from the `taskPrInfoChanged` payload is silently dropped here. When a background revalidation discovers a new PR URL (e.g., a PR is created after the task was first opened), the DB is updated and the event fires with the URL, but the `getCachedPrUrl` React Query cache is never invalidated or updated. Because `getCachedPrUrl` has `staleTime: 60_000`, the `cached?.prUrl` fallback in `useTaskPrUrl` will serve the old (null) value for up to a minute, defeating the fast-path for the "Open PR" button that this cache was introduced to provide.
```suggestion
onData: ({ taskId, prUrl, prState, hasDiff }) => {
// Push the fresh PR info into every matching getTaskPrStatus query
// (one per cloudPrUrl variant) so the renderer re-renders without
// waiting for the next staleTime-driven refetch.
queryClient.setQueriesData<{
prState: typeof prState;
hasDiff: boolean;
}>(
{
...trpcReact.workspace.getTaskPrStatus.pathFilter(),
predicate: (query) => {
const [, params] = query.queryKey as [
unknown,
{ input?: { taskId?: string } } | undefined,
];
return params?.input?.taskId === taskId;
},
},
() => ({ prState, hasDiff }),
);
// Keep getCachedPrUrl in sync so the "Open PR" fast-path stays warm.
queryClient.setQueryData(
trpcReact.workspace.getCachedPrUrl.queryKey({ taskId }),
{ prUrl },
);
},
```
Reviews (1): Last reviewed commit: "feat(code): cache per-task PR url + stat..." | Re-trigger Greptile |
Addresses Greptile review feedback on #2343: 1. The no-op guard in `revalidateTaskPrStatus` used `!fresh.hasDiff` to skip emitting when "nothing changed", but `hasDiff` isn't persisted, so for a worktree with uncommitted changes and no PR the guard could never engage — every revalidation cycle wrote to the DB and emitted. `hasDiff` is now excluded from the event entirely (it's still computed inline by `getTaskPrStatus` and refreshed by TanStack on the staleTime cycle), and the emit decision is based purely on whether `prUrl` or `prState` changed. 2. The `App.tsx` subscription dropped `prUrl` from the event payload, so when a PR appeared after the task was first opened, the `getCachedPrUrl` query stayed at its 60s staleTime and `useTaskPrUrl`'s "Open PR" fast-path served `null` until the next refetch. The handler now `setQueryData`s the cached URL too, and merges `prState` into the existing `getTaskPrStatus` cache entry so any inline-computed `hasDiff` survives. Generated-By: PostHog Code Task-Id: 10fff210-3861-49ad-b1fc-dbabae2fcc17
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.
Problem
Switching between tasks in the sidebar/task detail used to block on a
gh pr list --head <branch>call to figure out which PR each task corresponds to. That made the PR badge in the sidebar and the "Open PR" button in the task header pop in noticeably late on every task switch — and on cold start, every visible card paid the cost simultaneously.Changes
Per-task PR info is now cached on the
workspacesSQLite row (pr_url,pr_state,pr_fetched_at) and served stale-while-revalidate.GitService.getTaskPrStatusreturns the cachedprStatesynchronously and schedules a deduplicated background revalidation.hasDifffor worktree tasks still computes inline because it's local-only and cheap.ghlookups as before, writes through to the workspaces row, and — when the value changed — emitsWorkspaceService.taskPrInfoChanged.workspace.onTaskPrInfoChangedtRPC subscription is wired inApp.tsx; the handler updates every matchinggetTaskPrStatusquery viasetQueriesDataso the badge updates in place without a refetch.workspace.getCachedPrUrl(taskId)letsuseTaskPrUrlfall back to the cached URL so the task header's PR button opens the right PR before the liveghlookups return.taskPrInfoChangedemit uses a string literal (not theWorkspaceServiceEventconst) to avoid a circular import —workspace/serviceeagerly loads the DI container, which re-entersgit/service.Migration:
0007_stiff_reptiladds the three nullable columns toworkspaces.How did you test this?
pnpm --filter @posthog/code typecheck✅pnpm exec biome check apps/code/src✅pnpm --filter @posthog/code test✅ (1436 pass; the 23 archive integration failures pre-exist onmainbecausegit commitis blocked in this signed-commit environment — verified by re-running them on a clean tree)No manual UI verification was possible from this environment.
Publish to changelog?
no
Created with PostHog Code