From 8a0aaf9966f72fc45eedd17987ab7fbecf4f2d4a Mon Sep 17 00:00:00 2001 From: Austin Pickett Date: Thu, 1 Oct 2026 09:50:47 -0400 Subject: [PATCH] fix(desktop): keep a failed watch snapshot distinct from an empty directory The storm-breaker poll mapped a throwing snapshot to '', which is also the valid readdir snapshot of an empty directory, so an empty watched directory vanishing after a trip was never reported. Failures are now null. --- .../electron/watch-storm-breaker.test.ts | 20 +++++++++++++++++++ apps/desktop/electron/watch-storm-breaker.ts | 9 +++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/desktop/electron/watch-storm-breaker.test.ts b/apps/desktop/electron/watch-storm-breaker.test.ts index 6bbd1b4d0c..374abcb3d3 100644 --- a/apps/desktop/electron/watch-storm-breaker.test.ts +++ b/apps/desktop/electron/watch-storm-breaker.test.ts @@ -179,4 +179,24 @@ describe('guardedWatch', () => { expect(onPollChange).toHaveBeenCalledTimes(1) }) + + test('an empty directory vanishing is still a change (error is not the empty snapshot)', () => { + let gone = false + + const { fw, onPollChange } = setup('win32', { + snapshot: () => { + if (gone) { + throw new Error('ENOENT') + } + + return '' + } + }) + + fw.emit(WATCH_STORM_MAX_EVENTS + 1) + gone = true + vi.advanceTimersByTime(WATCH_STORM_POLL_MS) + + expect(onPollChange).toHaveBeenCalledTimes(1) + }) }) diff --git a/apps/desktop/electron/watch-storm-breaker.ts b/apps/desktop/electron/watch-storm-breaker.ts index 77b5d01eb9..fed18ee089 100644 --- a/apps/desktop/electron/watch-storm-breaker.ts +++ b/apps/desktop/electron/watch-storm-breaker.ts @@ -55,11 +55,16 @@ export function stormBreakerArmed(platform: string): boolean { return platform === 'win32' } -function safeSnapshot(snapshot: () => string): string { +/** + * `null` = the snapshot failed (target removed or unreadable). Kept distinct + * from the empty string, which is the valid snapshot of an empty directory, so + * an empty directory vanishing still compares as a change. + */ +function safeSnapshot(snapshot: () => string): string | null { try { return snapshot() } catch { - return '' + return null } }