feat(sync): reconcile secondary Graph calendars

Read documented Graph v1 calendarView pages for secondary calendars and publish each finite window only after the complete bounded listing validates.

Add schema-v2 per-window event membership so moved-out occurrences are evicted without being mislabeled as remote tombstones, while overlapping windows retain shared instances.

Verified: 18/18 fresh normal, 18/18 -Werror, and 18/18 ASan/UBSan tests.
This commit is contained in:
2026-07-18 13:05:21 +01:00
parent 0582444c61
commit 98e07e287e
11 changed files with 1520 additions and 60 deletions

View File

@@ -177,8 +177,9 @@ Microsoft authentication is designed for a public desktop client. It uses the
system browser and authorization-code flow, a fresh PKCE verifier and `S256`
challenge, and a loopback redirect. The Entra application registration is
multitenant and selects the intended organizational and personal Microsoft
account audience. The initial read-only Graph scope is the delegated
`Calendars.Read` permission. It normally permits user consent, while tenant
account audience. The read-only Graph scopes are delegated `User.Read` for
identity and `Calendars.Read` for calendar data. They normally permit user
consent, while tenant
consent policy remains authoritative and may require administrator approval or
block consent. A desktop binary distributed as open source is not a confidential
client and must not embed or depend on a client secret.
@@ -268,20 +269,25 @@ credential-bearing response material. Requests use only HTTPS
values are opaque continuation capabilities: Nocal validates their scheme and
host but does not reconstruct or reinterpret their query parameters.
Event synchronization uses half-open UTC windows. Each page's normalized
events, deletion tombstones, and opaque next/delta cursor commit in one cache
transaction. A partial round is therefore durable and resumable, while a cursor
can never advance beyond its corresponding event changes.
Event synchronization uses half-open UTC windows. For the primary calendar,
each delta page's normalized events, deletion tombstones, and opaque cursor
commit in one cache transaction. A partial round is durable and resumable, and
the cursor cannot advance beyond its corresponding changes.
Stable Microsoft Graph v1.0 documents `calendarView` delta only for the primary
calendar. The adapter discovers all calendars but delta-syncs events only from
the primary calendar. It does not call beta or an undocumented per-calendar
delta route. Secondary calendars remain an explicit architecture decision:
either reconcile complete v1 calendar-view windows with well-defined deletion
semantics, or accept a beta-specific delta dependency. That choice must be made
and tested before Nocal claims Outlook-equivalent calendar coverage.
Secondary-calendar reconciliation through stable Microsoft Graph v1.0 uses the
documented per-calendar `calendarView` route, accumulates every page within
fixed total bounds, and publishes only a completely validated listing. Schema
version 2 tracks durable event membership by calendar and window. Replacing one
window upserts its returned events and removes instances for omitted events only
when no other complete window still references them. The event envelope remains
live and its raw provider payload is retained, because absence can mean that an
event moved outside the requested range rather than that it was deleted
remotely.
This design avoids beta and undocumented per-calendar delta routes. Its
tradeoff is bandwidth: every secondary window is fetched completely on each
refresh, while the primary calendar remains incremental.
There is no account UI or CLI orchestration and no live Microsoft credential
integration yet; tests use injected transports and credentials. Remote writes
remain out of scope. Account setup and orchestration plus the secondary-calendar
reconciliation decision are next.
remain out of scope. Account setup and orchestration are next.