diff --git a/src/main/runtime/files/watch/file-watches.ts b/src/main/runtime/files/watch/file-watches.ts index f551cbfa219..5b06f0ffcc1 100644 --- a/src/main/runtime/files/watch/file-watches.ts +++ b/src/main/runtime/files/watch/file-watches.ts @@ -94,7 +94,8 @@ export class RuntimeFileWatches { if (leases) { await Promise.all(Array.from(leases, (lease) => lease.suspend())) } - if (!connectionId) { + // Windows watches in-process, so only the leases above exist; Git may already have revoked the path. + if (!connectionId && process.platform !== 'win32') { // Failed setup may still own a child without having published a lease-release callback. const resolvedRootPath = await resolveAuthorizedPath(rootPath, this.host.requireStore()) await closeFileExplorerWatcherInWatcherProcess(resolvedRootPath) diff --git a/src/main/runtime/orca-runtime-files-watch.test.ts b/src/main/runtime/orca-runtime-files-watch.test.ts index 6c87723e57a..d8526229c31 100644 --- a/src/main/runtime/orca-runtime-files-watch.test.ts +++ b/src/main/runtime/orca-runtime-files-watch.test.ts @@ -212,6 +212,27 @@ describe('RuntimeFileCommands file watching', () => { expect(vi.getTimerCount()).toBe(0) }) + it('closes Windows removal watchers without reauthorizing a path Git already unregistered', async () => { + Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) + const rootPath = 'C:\\projects\\repo-feature' + const watcher = createWindowsWatcher(() => { + queueMicrotask(() => watcher.emit('close')) + }) + watchMock.mockReturnValue(watcher) + resolveAuthorizedPathMock.mockResolvedValue(rootPath) + statMock.mockResolvedValue({ isDirectory: () => true }) + const { commands } = createRuntimeFileCommands(rootPath) + await commands.watchFileExplorer('id:wt-1', vi.fn()) + resolveAuthorizedPathMock.mockRejectedValue( + new Error('Access denied: path resolves outside allowed directories') + ) + + await expect(commands.closeFileExplorerWatchersForPath(rootPath)).resolves.toBeUndefined() + + expect(watcher.close).toHaveBeenCalledTimes(1) + expect(closeWatcherInWatcherProcessMock).not.toHaveBeenCalled() + }) + it('retains Windows close ownership until late physical exit without retry leaks', async () => { Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) const watcher = createWindowsWatcher(() => undefined)