diff --git a/apps/frontend/src/renderer/hooks/useIpc.ts b/apps/frontend/src/renderer/hooks/useIpc.ts index 1dab10bc5e..1ad2fea8e4 100644 --- a/apps/frontend/src/renderer/hooks/useIpc.ts +++ b/apps/frontend/src/renderer/hooks/useIpc.ts @@ -222,14 +222,18 @@ export function useIpcListeners(): void { // Sync roadmap feature when task completes if (status === 'done' || status === 'pr_created') { - useRoadmapStore.getState().markFeatureDoneBySpecId(taskId); - // Re-read state after mutation to get updated roadmap + // Use event's projectId (from callback param) — NOT the store's activeProjectId. + // During project switches, the store may hold a different project's roadmap. + const eventProjectId = projectId; const rm = useRoadmapStore.getState().roadmap; - const currentProjectId = useProjectStore.getState().activeProjectId || useProjectStore.getState().selectedProjectId; - if (rm && currentProjectId) { - window.electronAPI.saveRoadmap(currentProjectId, rm).catch((err) => { - console.error('[useIpc] Failed to persist roadmap after task completion:', err); - }); + if (rm && eventProjectId && rm.projectId === eventProjectId) { + useRoadmapStore.getState().markFeatureDoneBySpecId(taskId); + const updatedRm = useRoadmapStore.getState().roadmap; + if (updatedRm && updatedRm.projectId === eventProjectId) { + window.electronAPI.saveRoadmap(eventProjectId, updatedRm).catch((err) => { + console.error('[useIpc] Failed to persist roadmap after task completion:', err); + }); + } } } } diff --git a/apps/frontend/src/renderer/stores/roadmap-store.ts b/apps/frontend/src/renderer/stores/roadmap-store.ts index 05a9fce067..e93274744b 100644 --- a/apps/frontend/src/renderer/stores/roadmap-store.ts +++ b/apps/frontend/src/renderer/stores/roadmap-store.ts @@ -713,7 +713,9 @@ async function reconcileLinkedFeatures(projectId: string, roadmap: Roadmap): Pro if (hasChanges) { const updatedRoadmap = useRoadmapStore.getState().roadmap; - if (updatedRoadmap) { + // Guard: only save if the store still holds the same project's roadmap. + // A project switch during async reconciliation could replace the roadmap. + if (updatedRoadmap && updatedRoadmap.projectId === projectId) { console.log('[Roadmap] Reconciled linked features with task states'); window.electronAPI.saveRoadmap(projectId, updatedRoadmap).catch((err) => { console.error('[Roadmap] Failed to save reconciled roadmap:', err); @@ -729,6 +731,9 @@ export async function loadRoadmap(projectId: string): Promise { // Always set current project ID first - this ensures event handlers // only process events for the currently viewed project store.setCurrentProjectId(projectId); + store.setRoadmap(null); // Clear immediately to prevent stale cross-project saves + store.setCompetitorAnalysis(null); + store.setGenerationStatus({ phase: 'idle', progress: 0, message: '' }); // Query if roadmap generation is currently running for this project // This restores the generation status when switching back to a project