feat(server,app): redesign git/GitHub refresh architecture with server authority

Centralizes refresh orchestration in WorkspaceGitService and GitHubService:
server coalesces, deduplicates, and throttles git/GitHub reads; clients peek
snapshots (cold reads fall through to a one-shot fetch) and never drive
refreshes. Introduces a two-TTL model (consumer vs internal), force-emit
bypass so fingerprint-matched refreshes still propagate, notifyGitMutation
wiring at ~13 action handlers, adaptive GitHub polling based on check state,
and checkout_status_update / prStatus push-to-cache wire messages. Fixes the
stale diff-stat bug and the inverted base-branch diff direction, and flips
next-action affordances promptly after commit / PR merge.
This commit is contained in:
Mohamed Boudra
2026-04-19 22:35:26 +07:00
parent 128d6668ae
commit 5a2708653c
50 changed files with 6320 additions and 1073 deletions
@@ -1,10 +1,8 @@
import { useQuery } from "@tanstack/react-query";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect } from "react";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import type { CheckoutPrStatusResponse } from "@server/shared/messages";
const CHECKOUT_PR_STATUS_STALE_TIME = 20_000;
const WORKSPACE_PR_HINT_REFETCH_INTERVAL = 60_000;
export function checkoutPrStatusQueryKey(serverId: string, cwd: string) {
return ["checkoutPrStatus", serverId, cwd] as const;
}
@@ -16,6 +14,7 @@ interface UseCheckoutPrStatusQueryOptions {
}
export type CheckoutPrStatusPayload = CheckoutPrStatusResponse["payload"];
export interface PrHint {
url: string;
number: number;
@@ -71,9 +70,24 @@ export function useCheckoutPrStatusQuery({
cwd,
enabled = true,
}: UseCheckoutPrStatusQueryOptions) {
const queryClient = useQueryClient();
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
useEffect(() => {
if (!client || !isConnected || !cwd) {
return;
}
return client.on("checkout_status_update", (message) => {
const prStatus = message.payload.prStatus;
if (!prStatus || prStatus.cwd !== cwd) {
return;
}
queryClient.setQueryData(checkoutPrStatusQueryKey(serverId, cwd), prStatus);
});
}, [client, isConnected, cwd, queryClient, serverId]);
const query = useQuery({
queryKey: checkoutPrStatusQueryKey(serverId, cwd),
queryFn: async () => {
@@ -83,8 +97,10 @@ export function useCheckoutPrStatusQuery({
return await client.checkoutPrStatus(cwd);
},
enabled: !!client && isConnected && !!cwd && enabled,
staleTime: CHECKOUT_PR_STATUS_STALE_TIME,
refetchInterval: 15_000,
staleTime: Infinity,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
});
return {
@@ -95,7 +111,6 @@ export function useCheckoutPrStatusQuery({
isFetching: query.isFetching,
isError: query.isError,
error: query.error,
refresh: query.refetch,
};
}
@@ -104,9 +119,24 @@ export function useWorkspacePrHint({
cwd,
enabled = true,
}: UseCheckoutPrStatusQueryOptions): PrHint | null {
const queryClient = useQueryClient();
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
useEffect(() => {
if (!client || !isConnected || !cwd) {
return;
}
return client.on("checkout_status_update", (message) => {
const prStatus = message.payload.prStatus;
if (!prStatus || prStatus.cwd !== cwd) {
return;
}
queryClient.setQueryData(checkoutPrStatusQueryKey(serverId, cwd), prStatus);
});
}, [client, isConnected, cwd, queryClient, serverId]);
const query = useQuery<CheckoutPrStatusPayload, Error, PrHint | null>({
queryKey: checkoutPrStatusQueryKey(serverId, cwd),
queryFn: async () => {
@@ -116,8 +146,10 @@ export function useWorkspacePrHint({
return await client.checkoutPrStatus(cwd);
},
enabled: !!client && isConnected && !!cwd && enabled,
staleTime: CHECKOUT_PR_STATUS_STALE_TIME,
refetchInterval: WORKSPACE_PR_HINT_REFETCH_INTERVAL,
staleTime: Infinity,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
select: selectWorkspacePrHint,
});