From 9cbbbc2b6ce83b25fc4c44d6d98cea2a3ee76d8b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 5 Sep 2026 13:33:37 -0700 Subject: [PATCH] fix: reconcile untouched drafts during discovery refresh --- .../creation-draft-session.test.ts | 25 +++++++++++++++++++ .../creation-draft-session.ts | 10 +++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.test.ts b/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.test.ts index 463ae1085be..75d1dce7473 100644 --- a/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.test.ts +++ b/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.test.ts @@ -125,3 +125,28 @@ it('discovers other-window drafts on refresh without replacing local edits', asy expect(useCreationDraftSession.getState().entries['create-1'].buffer.text).toBe('local text') expect(useCreationDraftSession.getState().entries['create-2'].buffer.text).toBe('new draft') }) + +it('reconciles external draft replacement without accumulating removed records', async () => { + for (let generation = 0; generation < 3; generation++) { + database.listDrafts.mockResolvedValue( + Array.from({ length: 64 }, (_, index) => ({ + ...draft('external', `${generation}-${index}`), + revision: 1 + })) + ) + await loadCreationDrafts(true) + expect(Object.keys(useCreationDraftSession.getState().entries)).toHaveLength(64) + } +}) + +it('preserves the open recovered draft when another writer deletes it', async () => { + database.listDrafts.mockResolvedValue([{ ...draft('keep open'), revision: 1 }]) + await loadCreationDrafts() + useCreationDraftSession.setState({ viewedDraftId: 'create-1' }) + database.listDrafts.mockResolvedValue([]) + await loadCreationDrafts(true) + expect(useCreationDraftSession.getState().entries['create-1'].buffer.text).toBe('keep open') + useCreationDraftSession.setState({ viewedDraftId: null }) + await loadCreationDrafts(true) + expect(useCreationDraftSession.getState().entries).toEqual({}) +}) diff --git a/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.ts b/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.ts index 3d766d9d711..b5cb711bd09 100644 --- a/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.ts +++ b/src/renderer/src/lib/workspace-creation-drafts/creation-draft-session.ts @@ -54,8 +54,16 @@ export function loadCreationDrafts(refresh = false): Promise { .then((drafts) => { useCreationDraftSession.setState((state) => { const entries = { ...state.entries } + const storedIds = new Set(drafts.map((draft) => draft.id)) + const isUntouched = (id: string): boolean => + entries[id]?.editVersion === 0 && state.viewedDraftId !== id + for (const id of Object.keys(entries)) { + if (!storedIds.has(id) && isUntouched(id)) { + delete entries[id] + } + } for (const draft of drafts) { - if (!entries[draft.id]) { + if (!entries[draft.id] || isUntouched(draft.id)) { entries[draft.id] = fromStored(draft) } }