From f75650a99062740dde01244f7a4cb7504f3849e2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Apr 2026 11:03:33 +0000 Subject: [PATCH] fix(macOS): invalidate git date cache generation on exitVault A refreshGitDateCache completion that ran after exit could still match its captured generation (exit did not bump gitDateCacheGeneration), repopulating gitDateCache with the previous vault's file URLs while rootURL was nil. Bump gitDateCacheGeneration alongside scanGeneration and add a unit test. Validation: web vitest (npm test -- --run) passing; macOS tests not run in CI. Co-authored-by: Danny Peck --- macOS/SynapseNotes/AppState.swift | 7 ++++++- macOS/SynapseNotesTests/AppStateExitVaultFullTests.swift | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/macOS/SynapseNotes/AppState.swift b/macOS/SynapseNotes/AppState.swift index 71bc5ba..533ec2b 100644 --- a/macOS/SynapseNotes/AppState.swift +++ b/macOS/SynapseNotes/AppState.swift @@ -327,7 +327,9 @@ class AppState: ObservableObject { /// after any sync that could change history. Empty when the vault has no git repo. private(set) var gitDateCache: [URL: GitService.FileDates] = [:] /// Monotonic counter so stale background git-log results don't overwrite a newer cache. - private var gitDateCacheGeneration: Int = 0 + /// `exitVault()` increments this (like `scanGeneration`) so a git-log that finishes after + /// close cannot repopulate `gitDateCache` and leak paths from the previous vault. + private(set) var gitDateCacheGeneration: Int = 0 /// Inverted word index: lowercase word token → set of files whose content contains it. /// Built from `noteContentCache` and updated incrementally. @@ -1686,6 +1688,9 @@ class AppState: ObservableObject { // `scanGeneration`, a scan started before exit could still complete on the main queue // and repopulate `allFiles` while `rootURL` is nil — breaking splash / command palette. scanGeneration += 1 + // Invalidate in-flight `refreshGitDateCache` work. Without this, a background git log + // can still commit after we cleared `gitDateCache`, restoring stale per-file paths. + gitDateCacheGeneration += 1 // Clear all files allFiles = [] diff --git a/macOS/SynapseNotesTests/AppStateExitVaultFullTests.swift b/macOS/SynapseNotesTests/AppStateExitVaultFullTests.swift index cb6d47e..11acb56 100644 --- a/macOS/SynapseNotesTests/AppStateExitVaultFullTests.swift +++ b/macOS/SynapseNotesTests/AppStateExitVaultFullTests.swift @@ -93,6 +93,14 @@ final class AppStateExitVaultFullTests: XCTestCase { XCTAssertEqual(sut.scanGeneration, generationBeforeExit + 1) } + /// Bumping `gitDateCacheGeneration` on exit invalidates async `refreshGitDateCache` work so + /// a slow `git log` cannot repopulate `gitDateCache` after we cleared it (wrong-vault paths). + func test_exitVault_bumpsGitDateCacheGeneration_toInvalidateInFlightRefresh() { + let generationBeforeExit = sut.gitDateCacheGeneration + sut.exitVault() + XCTAssertEqual(sut.gitDateCacheGeneration, generationBeforeExit + 1) + } + // MARK: - Git state reset func test_exitVault_resetsGitState() {