Skip to content

Improve Google Calendar task sync reliability#1843

Closed
martin-forge wants to merge 6 commits intocallumalpass:mainfrom
martin-forge:martin-forge/google-calendar-delete-retry
Closed

Improve Google Calendar task sync reliability#1843
martin-forge wants to merge 6 commits intocallumalpass:mainfrom
martin-forge:martin-forge/google-calendar-delete-retry

Conversation

@martin-forge
Copy link
Copy Markdown
Contributor

@martin-forge martin-forge commented Apr 29, 2026

Maintainer note

Ready to merge. This is the highest-priority active Google Calendar data-integrity fix. It is distinct from #1802, but both PRs touch cleanup paths. If #1802 lands first, this PR should be rebased so the persisted deletion/index recovery also covers #1802's detached exception event IDs. If this lands first, #1802 should be rebased so detached exception cleanup routes through this retry-safe delete path. #1832 and #1844 are independent except for routine rebase conflicts.

Summary

This PR hardens TaskNotes Google Calendar task sync around the failure modes that can orphan, miss, hide, stale, or duplicate calendar events:

  • persist failed Google Calendar task-event deletions in plugin data and retry them after restart/reconnect
  • keep a persisted exported-event index so startup can queue cleanup for task files deleted while Obsidian was closed
  • persist pending task sync requests while Google Calendar is not ready, then replay the current task state after reconnect
  • preserve the existing calendar date model: scheduled, due, or both, using the configured syncTrigger
  • route UI, batch, API/MCP/TaskService, archive/unlink, and metadata-delete cleanup through the retry-safe delete path
  • treat 404/410 deletes as successful cleanup and dedupe queued work by calendar/event id
  • restore deleted-but-addressable Google event tombstones (status: cancelled) when syncing an existing event ID
  • prevent duplicate Google events when concurrent syncs race before the newly created event ID reaches Obsidian metadata
  • clean up an older indexed event when a task receives a replacement event id
  • prevent pending intermediate status updates from overwriting the final completed-event state when users quickly cycle a task to done
  • mark Google Calendar events as completed whenever the synced task is already complete, including when a completed task is scheduled later

The sync invariant remains simple: tasks belong on Google Calendar based on the existing configured date trigger. This does not add status filtering or completed-task busy/free behavior.

Root Cause

Deleted task files could lose their googleCalendarEventId before Google cleanup succeeded, especially when Google sync was not connected/ready. Task create/update/reschedule work could also be skipped while Google Calendar was unavailable instead of being replayed later. Google can retain deleted events as status: cancelled tombstones that still return from direct event lookup but are hidden from ordinary calendar views, so updating an existing event ID needs to restore it to confirmed.

There were also status and create/update race paths: direct syncTaskToCalendar calls for the same unsynced task could both create a Google event before the frontmatter event ID write became visible through metadata; a debounced intermediate status update could run after a later completion update; and a task that was already complete before it became calendar-eligible could be synced through the ordinary create/update path without the completed title marker.

Validation

  • npm test -- tests/services/TaskCalendarSyncService.test.ts tests/unit/issues/issue-google-calendar-delete-retry-queue.test.ts --runInBand
  • npm run typecheck
  • npm run lint -- --quiet
  • npm run build:test
  • obsidian plugin:reload id=tasknotes vault=test

@martin-forge martin-forge marked this pull request as ready for review April 29, 2026 11:24
@martin-forge
Copy link
Copy Markdown
Contributor Author

Cross-PR compatibility note from auditing the open Google Calendar PRs:

Recommended merge handling: either merge #1802 first and rebase this PR so the queue/index covers exception IDs too, or merge this PR first and rebase #1802 so its exception cleanup routes through the retry-safe delete path.

@martin-forge
Copy link
Copy Markdown
Contributor Author

Superseded by #1851, which consolidates the Google Calendar fixes into one ready-to-review PR.

@martin-forge martin-forge deleted the martin-forge/google-calendar-delete-retry branch April 30, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant