M6.5 [P1]: Production-wire Contacts sync (per-account CardDAV config + real client) #255

Closed
opened 2026-07-31 14:42:50 +00:00 by KofTwentyTwo · 2 comments
KofTwentyTwo commented 2026-07-31 14:42:50 +00:00 (Migrated from github.com)

Contacts/Sync (grpc.rs:1049, contacts_sync.rs:8) is non-functional in production for the same reason as Calendar: no per-account CardDAV config, so prod always hits the honest-error branch and works only via injected mock. Goal: add CardDAV account configuration (endpoint + keyring creds) and construct a REAL CardDavClient (nuncio-contacts/src/carddav.rs) in the Contacts/Sync path from it. Offline E2E (wiremock CardDAV) proving the production path. Coordinate with the dup-CardDAV deletion (M6.5 P0).

Source: full-tree audit 2026-07-30 (scratchpad/audit/AUDIT-SUMMARY.md). Follow docs/STORY-WORKFLOW.md: engine+proto(if surface)+CLI+offline E2E, mock-only tests, no fabricated data, comment convention, PR to dev, PO reviews before close.

Contacts/Sync (grpc.rs:1049, contacts_sync.rs:8) is non-functional in production for the same reason as Calendar: no per-account CardDAV config, so prod always hits the honest-error branch and works only via injected mock. Goal: add CardDAV account configuration (endpoint + keyring creds) and construct a REAL CardDavClient (nuncio-contacts/src/carddav.rs) in the Contacts/Sync path from it. Offline E2E (wiremock CardDAV) proving the production path. Coordinate with the dup-CardDAV deletion (M6.5 P0). Source: full-tree audit 2026-07-30 (scratchpad/audit/AUDIT-SUMMARY.md). Follow docs/STORY-WORKFLOW.md: engine+proto(if surface)+CLI+offline E2E, mock-only tests, no fabricated data, comment convention, PR to dev, PO reviews before close.
KofTwentyTwo commented 2026-07-31 17:09:44 +00:00 (Migrated from github.com)

Account-model decision (owner): SEPARATE account entries per service. A CalDAV/CardDAV account is its own AddAccount entry with protocol=CALDAV/CARDDAV + a fully-qualified collection URL + its own keyring credential, decoupled from mail accounts. Implemented additively: new AccountProtocol variant(s) + a generic AccountConfig.collection_url field (reused by both). #254 adds CALDAV + the collection_url field/column; #255 reuses collection_url + adds CARDDAV. Both are [PROTO] -> serialize (#254 first). Depends on #254 landing the collection_url field + the CALDAV precedent.

Account-model decision (owner): SEPARATE account entries per service. A CalDAV/CardDAV account is its own AddAccount entry with protocol=CALDAV/CARDDAV + a fully-qualified collection URL + its own keyring credential, decoupled from mail accounts. Implemented additively: new AccountProtocol variant(s) + a generic AccountConfig.collection_url field (reused by both). #254 adds CALDAV + the collection_url field/column; #255 reuses collection_url + adds CARDDAV. Both are [PROTO] -> serialize (#254 first). Depends on #254 landing the collection_url field + the CALDAV precedent.
KofTwentyTwo commented 2026-08-15 02:49:12 +00:00 (Migrated from github.com)

Fixed in #431 (e444ffd), merged to dev. Contacts sync is production-wired end to end: a configured CardDAV account now actually syncs.

AccountProtocol::CardDav + Transport::CardDav(DavTransport), reusing the existing collection_url column — no schema change. Proto additions are purely additive (ACCOUNT_PROTOCOL_CARDDAV = 4, carddav oneof at field 19) with the golden regenerated from the build output. contacts_sync::sync_carddav_account builds a real CardDavClient with the credential read just-in-time from the keyring and zeroized. Contacts/Sync now returns a typed NotConfigured/FAILED_PRECONDITION instead of an untyped internal error. CLI gains --protocol carddav. The Contacts service and contact sync command already existed; no RPC was duplicated.

Reviewer note worth carrying forward: is_dav() now covers CardDAV too, so calendar sync's filter moved to the new is_caldav(). That preserves existing behaviour — without it a CardDAV account would have been swept into Calendar/Sync — but it is a real edit to an existing vertical.

Noticed and left alone: nuncio-contacts/src/db.rs still exports an unused ContactsDatabase that nuncio-store's own comment calls superseded. Dead surface, a candidate for the next de-fabrication pass.

Fixed in #431 (`e444ffd`), merged to `dev`. Contacts sync is production-wired end to end: a configured CardDAV account now actually syncs. `AccountProtocol::CardDav` + `Transport::CardDav(DavTransport)`, reusing the existing `collection_url` column — **no schema change**. Proto additions are purely additive (`ACCOUNT_PROTOCOL_CARDDAV = 4`, `carddav` oneof at field 19) with the golden regenerated from the build output. `contacts_sync::sync_carddav_account` builds a real `CardDavClient` with the credential read just-in-time from the keyring and zeroized. `Contacts/Sync` now returns a typed `NotConfigured`/`FAILED_PRECONDITION` instead of an untyped internal error. CLI gains `--protocol carddav`. The `Contacts` service and `contact sync` command already existed; no RPC was duplicated. **Reviewer note worth carrying forward:** `is_dav()` now covers CardDAV too, so calendar sync's filter moved to the new `is_caldav()`. That *preserves* existing behaviour — without it a CardDAV account would have been swept into `Calendar/Sync` — but it is a real edit to an existing vertical. Noticed and left alone: `nuncio-contacts/src/db.rs` still exports an unused `ContactsDatabase` that `nuncio-store`'s own comment calls superseded. Dead surface, a candidate for the next de-fabrication pass.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/nuncio#255