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:
@@ -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.
|
||||
|
||||
@@ -133,8 +133,9 @@ The Microsoft sign-in design treats Nocal as a public desktop client: sign-in
|
||||
will open the system browser, use the authorization-code flow with PKCE `S256`,
|
||||
and return through a loopback redirect. Its application registration must be
|
||||
multitenant for the intended organizational and personal Microsoft account
|
||||
audience. Initial synchronization requests only delegated `Calendars.Read`, not
|
||||
write access. Users can normally consent to that delegated permission, but
|
||||
audience. Initial synchronization requests delegated `User.Read` and
|
||||
`Calendars.Read`, not write access. Users can normally consent to those
|
||||
delegated permissions, but
|
||||
their organization's tenant policy may still require administrator consent or
|
||||
deny the application. Nocal never embeds a client secret in its open-source
|
||||
desktop binary.
|
||||
@@ -183,21 +184,21 @@ calendar a deterministic local ID. Event reads request immutable Graph IDs and
|
||||
use strict bounded parsing with credential-safe failures. Requests and opaque
|
||||
next/delta links are restricted to HTTPS `graph.microsoft.com` URLs.
|
||||
|
||||
Event windows are half-open UTC intervals. Each event page, its deletions, and
|
||||
its continuation cursor cross the cache boundary in one transaction. A partial
|
||||
round can resume from its durable cursor, and deletion tombstones remain
|
||||
explicit instead of silently dropping remote state.
|
||||
Event windows are half-open UTC intervals. Primary-calendar delta pages cross
|
||||
the cache boundary with their deletions and continuation cursor in one
|
||||
transaction, so a partial round resumes safely and tombstones remain explicit.
|
||||
|
||||
The stable Graph v1.0 `calendarView` delta API is documented only for the
|
||||
primary calendar. This slice discovers all calendars but delta-syncs only the
|
||||
primary; it uses neither beta nor undocumented per-calendar routes. Secondary
|
||||
calendar support requires a deliberate choice between v1 full-window
|
||||
reconciliation with defined deletion semantics and beta-specific delta. Nocal
|
||||
does not yet claim Outlook-equivalent coverage.
|
||||
Stable-v1 secondary-calendar reconciliation reads every page of a complete
|
||||
per-calendar view before publishing the window. Durable per-window membership
|
||||
distinguishes an event that moved outside the range from an event Graph actually
|
||||
deleted: an omitted event loses its cached instance only when no other complete
|
||||
window references it, while its raw event record remains live. This avoids beta
|
||||
and undocumented delta routes, but a secondary window requires a complete
|
||||
refetch on every refresh.
|
||||
|
||||
There is still no account UI/CLI orchestration or live Microsoft credential
|
||||
integration, and remote writes remain out of scope. Account setup/orchestration
|
||||
and the secondary-calendar reconciliation decision are the next product phase.
|
||||
is the next product phase.
|
||||
|
||||
## Local file interchange
|
||||
|
||||
|
||||
@@ -84,11 +84,18 @@ Completed Microsoft Graph read phase:
|
||||
- Half-open UTC event windows and per-page event/tombstone/cursor transactions
|
||||
- Resumable primary-calendar v1 delta synchronization
|
||||
|
||||
Completed secondary-calendar stable-v1 reconciliation:
|
||||
|
||||
- Documented per-calendar `calendarView` reads with complete bounded paging
|
||||
- Schema-v2 durable per-window event membership
|
||||
- Atomic complete-window replacement after every page validates
|
||||
- Range omission distinguished from a remote deletion tombstone
|
||||
- Overlapping windows retain events until their last membership is removed
|
||||
- No beta or undocumented per-calendar delta dependency
|
||||
|
||||
Next provider-boundary slices:
|
||||
|
||||
- Account setup and CLI/TUI orchestration with live credential integration
|
||||
- Choose secondary-calendar behavior: v1 full-window reconciliation with
|
||||
deletion semantics, or an explicit beta-specific delta dependency
|
||||
- Generic `SyncProvider` integration and observable sync status
|
||||
|
||||
## 0.3 — CalDAV
|
||||
@@ -104,12 +111,11 @@ Next provider-boundary slices:
|
||||
ETag-aware remote writes after the conflict boundary is proven
|
||||
- Account/calendar management UI and per-calendar ANSI identity
|
||||
|
||||
The Graph read phase discovers all calendars but uses stable v1 delta only for
|
||||
the primary calendar; it deliberately avoids beta and undocumented
|
||||
per-calendar routes. No account UI/CLI orchestration or live Microsoft
|
||||
credential integration exists yet, and remote writes remain out of scope. Do
|
||||
not claim Outlook-equivalent coverage until the secondary-calendar strategy is
|
||||
chosen and verified.
|
||||
The Graph read boundary uses stable v1 delta for the primary calendar and
|
||||
complete stable-v1 calendar views for secondary calendars; it deliberately
|
||||
avoids beta and undocumented per-calendar delta routes. No account UI/CLI
|
||||
orchestration or live Microsoft credential integration exists yet, and remote
|
||||
writes remain out of scope.
|
||||
|
||||
## 1.0 — distribution quality
|
||||
|
||||
|
||||
Reference in New Issue
Block a user