- The periodic job and the immediate 'Sync now'/foreground runs had no mutual
exclusion, so two overlapping reconciles could each see no managed calendar
and both create one (duplicate 'Birthdays', doubled events); likewise a
disable-teardown racing an in-flight sync got its calendars recreated right
after deletion. A lifecycle Mutex now makes sync() and teardown() atomic, and
sync() re-reads the enabled flag inside the lock so a teardown always wins.
- The foreground resume trigger shared a unique work name with enable/'Sync
now' under ExistingWorkPolicy.REPLACE, so a debounced foreground enqueue could
cancel-and-swallow a pending enable sync (feature on, no calendars for up to a
day). It now uses its own work name.
- doWork() mapped every exception to retry(), so a revoked calendar permission
retried with backoff forever and never surfaced. A SecurityException now parks
the feature in the stalled state instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>