From ecabefc67701cf7f28c6b44c053ac52c8fae69d7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:47:24 +0000 Subject: [PATCH] fix: prevent duplicate events when rescheduling events with attached sessions When a calendar event with an attached note/session is rescheduled, the sync logic now properly detects the rescheduled event and updates the existing event's time instead of creating a duplicate. Previously, events with non-empty sessions would skip the rescheduled event detection, causing: 1. The old event to remain with the old time 2. A new event to be created with the new time 3. Both appearing in the sidebar as duplicates The fix moves the rescheduled event detection before the hasNonEmptySession check, so events with sessions are also updated when rescheduled. Co-Authored-By: john@hyprnote.com --- .../process/events/sync.test.ts | 66 +++++++++++++++++++ .../apple-calendar/process/events/sync.ts | 8 +-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/services/apple-calendar/process/events/sync.test.ts b/apps/desktop/src/services/apple-calendar/process/events/sync.test.ts index f324bdd1e1..8c1f647b60 100644 --- a/apps/desktop/src/services/apple-calendar/process/events/sync.test.ts +++ b/apps/desktop/src/services/apple-calendar/process/events/sync.test.ts @@ -227,6 +227,72 @@ describe("syncEvents", () => { }); }); + describe("rescheduled events", () => { + test("updates event with non-empty session when rescheduled", () => { + const ctx = createMockCtx({ + eventToSession: new Map([["event-1", "session-1"]]), + nonEmptySessions: new Set(["session-1"]), + }); + + const result = syncEvents(ctx, { + incoming: [ + createIncomingEvent({ + tracking_id_event: "new-track-1", + title: "Existing Event", + started_at: "2024-01-20T10:00:00Z", + ended_at: "2024-01-20T11:00:00Z", + }), + ], + existing: [ + createExistingEvent({ + id: "event-1", + tracking_id_event: "old-track-1", + title: "Existing Event", + started_at: "2024-01-15T10:00:00Z", + ended_at: "2024-01-15T11:00:00Z", + }), + ], + }); + + expect(result.toUpdate).toHaveLength(1); + expect(result.toUpdate[0].id).toBe("event-1"); + expect(result.toUpdate[0].started_at).toBe("2024-01-20T10:00:00Z"); + expect(result.toAdd).toHaveLength(0); + expect(result.toDelete).toHaveLength(0); + }); + + test("does not create duplicate when event with session is rescheduled", () => { + const ctx = createMockCtx({ + eventToSession: new Map([["event-1", "session-1"]]), + nonEmptySessions: new Set(["session-1"]), + }); + + const result = syncEvents(ctx, { + incoming: [ + createIncomingEvent({ + tracking_id_event: "new-track-1", + title: "Existing Event", + started_at: "2024-01-20T14:00:00Z", + ended_at: "2024-01-20T15:00:00Z", + }), + ], + existing: [ + createExistingEvent({ + id: "event-1", + tracking_id_event: "old-track-1", + title: "Existing Event", + started_at: "2024-01-15T10:00:00Z", + ended_at: "2024-01-15T11:00:00Z", + }), + ], + }); + + expect(result.toAdd).toHaveLength(0); + expect(result.toUpdate).toHaveLength(1); + expect(result.toDelete).toHaveLength(0); + }); + }); + describe("disabled calendar cleanup", () => { test("preserves events with non-empty sessions when calendar disabled", () => { const ctx = createMockCtx({ diff --git a/apps/desktop/src/services/apple-calendar/process/events/sync.ts b/apps/desktop/src/services/apple-calendar/process/events/sync.ts index 38db2f26b6..5de497adba 100644 --- a/apps/desktop/src/services/apple-calendar/process/events/sync.ts +++ b/apps/desktop/src/services/apple-calendar/process/events/sync.ts @@ -56,10 +56,6 @@ export function syncEvents( continue; } - if (hasNonEmptySession) { - continue; - } - const rescheduledEvent = findRescheduledEvent(ctx, storeEvent, incoming); const rescheduledEventKey = rescheduledEvent ? getEventKey( @@ -86,6 +82,10 @@ export function syncEvents( continue; } + if (hasNonEmptySession) { + continue; + } + out.toDelete.push(storeEvent.id); }