diff --git a/src/renderer/src/lib/wsl-move-renderer-store-rewrites.test.ts b/src/renderer/src/lib/wsl-move-renderer-store-rewrites.test.ts index 995c8b60640..d31ce641a75 100644 --- a/src/renderer/src/lib/wsl-move-renderer-store-rewrites.test.ts +++ b/src/renderer/src/lib/wsl-move-renderer-store-rewrites.test.ts @@ -106,12 +106,34 @@ describe('WSL move renderer store rewrites', () => { expect(client.record).not.toHaveBeenCalled() }) - it('never throws when the move list is unavailable', async () => { - client.list.mockRejectedValue(new Error('no bridge')) - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - await expect((await loadRunner()).runWslMoveRendererStoreRewrites()).resolves.toBeUndefined() - expect(warn).toHaveBeenCalled() - warn.mockRestore() + it('never throws when the move list is unavailable, and asks again later', async () => { + vi.useFakeTimers() + try { + client.list.mockRejectedValueOnce(new Error('main not ready')).mockResolvedValue([]) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + await expect((await loadRunner()).runWslMoveRendererStoreRewrites()).resolves.toBeUndefined() + expect(warn).toHaveBeenCalled() + await vi.advanceTimersByTimeAsync(2_000) + expect(client.list).toHaveBeenCalledTimes(2) + await vi.advanceTimersByTimeAsync(120_000) + expect(client.list).toHaveBeenCalledTimes(2) + warn.mockRestore() + } finally { + vi.useRealTimers() + } + }) + + it('does not retry a record another window was refused', async () => { + vi.useFakeTimers() + try { + client.list.mockResolvedValue([move({ storeIds: ['quick-open-history'] })]) + client.record.mockResolvedValue({ recorded: false, reason: 'not-main-window' }) + await (await loadRunner()).runWslMoveRendererStoreRewrites() + await vi.advanceTimersByTimeAsync(120_000) + expect(client.record).toHaveBeenCalledTimes(1) + } finally { + vi.useRealTimers() + } }) it('adds a host copy beside each moved entry, then records every store', async () => { diff --git a/src/renderer/src/lib/wsl-move-renderer-store-rewrites.ts b/src/renderer/src/lib/wsl-move-renderer-store-rewrites.ts index c2991d6ca14..90dc2be70ed 100644 --- a/src/renderer/src/lib/wsl-move-renderer-store-rewrites.ts +++ b/src/renderer/src/lib/wsl-move-renderer-store-rewrites.ts @@ -82,8 +82,9 @@ function writeAppliedMarker(marker: string): void { } } -// Main's refusals that a later call can clear; the rest mean the store is no longer pending. -const RETRYABLE_RECORD_REASONS: ReadonlySet = new Set(['gated-off', 'failed', 'not-main-window']) +// Main's refusals a later call can clear. Another window (`not-main-window`) never becomes the main +// one, and the rest mean the store is no longer pending. +const RETRYABLE_RECORD_REASONS: ReadonlySet = new Set(['gated-off', 'failed']) /** True when a store's ledger entry still has to land: the move stays unfinished until it does. */ async function rewriteMove(move: WslMovePendingRendererRewrite): Promise { @@ -144,8 +145,9 @@ async function rewritePendingMoves(): Promise { unrecorded = (await rewriteMove(move)) || unrecorded } } catch (error) { - // No bridge to ask (a web client): nothing here could ever record, so no retry. + // Transient (main not ready yet); the backoff bounds a bridge that never answers. console.warn('[wsl-move] Could not list renderer rewrites:', error) + unrecorded = true } return unrecorded }