From 3c97920ae2bc2fcecbec1bd12e16c84af14e19b8 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Tue, 14 Jul 2026 13:21:40 +0200 Subject: [PATCH] fix(sessions): cancel pending draft-prompt flush on delete deleteSession removed the record from memory and IndexedDB but left the debounced draft-prompt flush timer running; it would fire afterward and persistTouched the deleted session back into IndexedDB, resurrecting a draft deleted inside the 400ms window on the next reload. Clear the per-session timer in deleteSession. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/sessions/sessionState.svelte.ts | 5 +++++ .../sessions/sessionStateIndexedDb.test.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/frontend/src/lib/components/sessions/sessionState.svelte.ts b/frontend/src/lib/components/sessions/sessionState.svelte.ts index 8a88b10afa..7fbf2c8ede 100644 --- a/frontend/src/lib/components/sessions/sessionState.svelte.ts +++ b/frontend/src/lib/components/sessions/sessionState.svelte.ts @@ -942,6 +942,11 @@ export function setSessionArchived(id: string, archived: boolean) { export function deleteSession(id: string) { const s = sessionState.sessions.find((x) => x.id === id) if (!s) return + // Cancel any pending draft-prompt flush: left running, its persistTouched + // would write the record back to IndexedDB after we delete it, resurrecting + // a draft deleted inside the debounce window. + clearTimeout(draftPromptFlushHandles.get(id)) + draftPromptFlushHandles.delete(id) sessionState.sessions = sessionState.sessions.filter((x) => x.id !== id) if (sessionState.currentSessionId === id) { sessionState.currentSessionId = sessionState.sessions[0]?.id diff --git a/frontend/src/lib/components/sessions/sessionStateIndexedDb.test.ts b/frontend/src/lib/components/sessions/sessionStateIndexedDb.test.ts index 1e96bb8010..9e4bae08e1 100644 --- a/frontend/src/lib/components/sessions/sessionStateIndexedDb.test.ts +++ b/frontend/src/lib/components/sessions/sessionStateIndexedDb.test.ts @@ -219,6 +219,24 @@ describe('sessionState IndexedDB persistence', () => { expect(sessionState.sessions).toEqual([]) }) + it('deleting a draft inside the debounce window does not resurrect it', async () => { + const user = freshUser() + userStore.set(user) + await flush() + + const s = session({ id: 't4b', transient: true, pending_workspace_id: 'wsA' }) + sessionState.sessions = [s] + // Schedule a flush, then delete before the 400ms timer fires. The cancelled + // timer must not write the record back to IndexedDB. + setSessionDraftPrompt('t4b', 'typed then deleted') + deleteSession('t4b') + await new Promise((r) => setTimeout(r, 500)) + + await rehydrate(user) + await flush() + expect(sessionState.sessions).toEqual([]) + }) + it('removes a session record', async () => { const user = freshUser() userStore.set(user)