Skip to content

Fix Google Calendar sync race condition and recurring completion date bug#1764

Open
martin-forge wants to merge 6 commits intocallumalpass:mainfrom
martin-forge:fix/google-calendar-sync-reliability
Open

Fix Google Calendar sync race condition and recurring completion date bug#1764
martin-forge wants to merge 6 commits intocallumalpass:mainfrom
martin-forge:fix/google-calendar-sync-reliability

Conversation

@martin-forge
Copy link
Copy Markdown
Contributor

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

Summary

Two independent Google Calendar sync reliability fixes:

  • Debounce stale metadata cache: TaskCalendarSyncService.updateTaskInCalendar discarded the explicit task payload during the 500ms debounce and re-fetched from cacheManager.getTaskInfo, which often returned stale data because Obsidian's metadata cache hadn't finished indexing. Fixed by caching the authoritative task payload in a pendingTasks Map during the debounce window.

  • Recurring completion records wrong occurrence: toggleRecurringTaskComplete and toggleRecurringTaskSkipped defaulted to getTodayLocal() when no explicit date was passed, even for scheduled-anchor recurring tasks. Completing a task late (e.g. Saturday's task on Sunday) recorded Sunday in complete_instances — but complete_instances is an occurrence index, not a completion timestamp, so this left Saturday's occurrence open and sent the wrong EXDATE to Google Calendar. Fixed by defaulting to task.scheduled for scheduled-anchor tasks, so the correct occurrence is marked as done.

Issue linkage

Test plan

  • npx jest tests/services/TaskCalendarSyncService.test.ts — debounce uses explicit payload, not stale cache
  • npx jest tests/unit/issues/issue-396-recurring-late-completion-wrong-date.test.ts — scheduled-anchor defaults to scheduled date; completion-anchor still defaults to today; undefined anchor defaults to scheduled
  • npx tsc --noEmit — clean typecheck
  • npm run build — clean build
  • Validated locally: rapid MCP task updates reflect correct dates in Google Calendar; late-completed recurring tasks mark the scheduled occurrence as done rather than recording a non-existent occurrence on the completion date

CI note

The test (20) check fails on Could not locate module tasknotes-nlp-core — this is a pre-existing repo-wide CI issue that is also affecting other PRs (for example #1677), and it was not introduced by these changes.

martin-forge and others added 6 commits April 5, 2026 01:01
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d date (callumalpass#396)

toggleRecurringTaskComplete and toggleRecurringTaskSkipped now default to
the task's scheduled occurrence date for scheduled-anchor recurring tasks
when no explicit date is passed, instead of unconditionally using today.
…ass#396)

The plugin-level toggleRecurringTaskComplete wrapper independently
derived the completion date from getTodayLocal(), diverging from the
TaskService fix. Now uses the same scheduled-anchor resolution.

Also replaced pseudo-test with a behavioural test that exercises the
real TaskService.toggleRecurringTaskComplete method with mocked
dependencies.
…sts (callumalpass#396)

The main.ts wrapper was resolving the notice date from the caller's
potentially stale task object. Now reads from cacheManager before the
service call, matching the authoritative source the service uses.

Added behavioural regression tests for toggleRecurringTaskSkipped to
match the existing completion path coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

The only failing check here is the known repo-wide tasknotes-nlp-core CI wiring issue in test (20), not a failure introduced by this branch.

I re-ran the targeted tests for the changes in this PR, plus npx tsc --noEmit and npm run build, locally and all of those passed.

If branch protection allows, I believe this PR is ready to merge as-is. If helpful, I’m also happy to put together a separate PR for the CI issue rather than fold that into this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant