From 1c3d6434a855b56dde4714e4134d324da999a470 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Mon, 2 Mar 2026 11:14:46 -0800 Subject: [PATCH] fix: reduce excessive POST /api/auto-mode/context-exists requests Two changes: 1. Server: Skip morgan logging for context-exists endpoint (like health check) 2. UI: Use a stable fingerprint (feature IDs + statuses) instead of the unstable features array reference as the useEffect dependency. This prevents re-checking context for all features on every React Query refetch when the actual feature set hasn't changed. --- apps/server/src/index.ts | 5 +++- .../board-view/hooks/use-board-effects.ts | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index ab61b641..2b48f662 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -261,7 +261,10 @@ morgan.token('status-colored', (_req, res) => { app.use( morgan(':method :url :status-colored', { // Skip when request logging is disabled or for health check endpoints - skip: (req) => !requestLoggingEnabled || req.url === '/api/health', + skip: (req) => + !requestLoggingEnabled || + req.url === '/api/health' || + req.url === '/api/auto-mode/context-exists', }) ); // CORS configuration diff --git a/apps/ui/src/components/views/board-view/hooks/use-board-effects.ts b/apps/ui/src/components/views/board-view/hooks/use-board-effects.ts index 30077cb6..d697f2cb 100644 --- a/apps/ui/src/components/views/board-view/hooks/use-board-effects.ts +++ b/apps/ui/src/components/views/board-view/hooks/use-board-effects.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { getElectronAPI } from '@/lib/electron'; import { createLogger } from '@automaker/utils/logger'; import type { Feature } from '@/store/app-store'; @@ -68,10 +68,27 @@ export function useBoardEffects({ // Note: Running tasks sync is now handled by useAutoMode hook in BoardView // which correctly handles worktree/branch scoping. + // Build a stable fingerprint of feature IDs + statuses so context checks + // only re-run when the set of features or their statuses actually change, + // not on every React Query refetch that produces a new array reference. + const featuresFingerprint = useMemo(() => { + return features + .map((f) => `${f.id}:${f.status}`) + .sort() + .join(','); + }, [features]); + + // Keep a ref to the latest features array for use inside the effect + const featuresRef = useRef(features); + useEffect(() => { + featuresRef.current = features; + }, [features]); + // Check which features have context files useEffect(() => { const checkAllContexts = async () => { - const featuresWithPotentialContext = features.filter( + const currentFeatures = featuresRef.current; + const featuresWithPotentialContext = currentFeatures.filter( (f) => f.status === 'backlog' || f.status === 'merge_conflict' || @@ -99,10 +116,11 @@ export function useBoardEffects({ setFeaturesWithContext(newSet); }; - if (features.length > 0 && !isLoading) { + if (featuresFingerprint && !isLoading) { checkAllContexts(); } - }, [features, isLoading, checkContextExists, setFeaturesWithContext]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [featuresFingerprint, isLoading, checkContextExists, setFeaturesWithContext]); // Re-check context when a feature stops, completes, or errors // This ensures hasContext is updated even if the features array doesn't change