Skip to content

Add two-way contacts sync (Apple Contacts, HubSpot, Folk) - #2

Draft
davidrose wants to merge 1 commit into
mainfrom
claude/contact-enrichments-status-eUe9R
Draft

Add two-way contacts sync (Apple Contacts, HubSpot, Folk)#2
davidrose wants to merge 1 commit into
mainfrom
claude/contact-enrichments-status-eUe9R

Conversation

@davidrose

Copy link
Copy Markdown

What this is

A new contacts-sync/ project that keeps the same contact consistent across Apple Contacts (iCloud), HubSpot, and Folk — two-way, with de-duplication and conflict resolution.

This was built fresh: no prior sync code was present in this repo or reachable from the session, so this establishes the foundation. If existing local code turns up, we can reconcile against it.

How it works

  1. Fetch all contacts from every configured provider.
  2. Cluster records into people by shared email (high precision), plus prior links remembered in a SQLite state store so people stay merged even if an email later changes.
  3. Decide canonical version — records changed since the last sync are merged field-by-field, applied oldest→newest so the most recent edit wins. List fields (emails/phones) are unioned, never overwritten.
  4. Write back — create where missing, update where stale, on every provider.
  5. Persist content hashes + cluster links for the next run.

Convergent by design: a second run with no external edits is a no-op. --dry-run prints the plan without writing.

Layout

  • contacts_sync/models.py — normalized contact model, normalization, field-level merge
  • contacts_sync/matching.py — union-find clustering over emails
  • contacts_sync/engine.py — two-way orchestration + last-write-wins conflict resolution
  • contacts_sync/state.py — SQLite state store
  • contacts_sync/providers/ — HubSpot (CRM v3), Folk, Apple/iCloud CardDAV adapters
  • contacts_sync/cli.pystatus / sync / --dry-run
  • tests/ — pure-logic + full round-trip tests via an in-memory provider

Verification

  • 13 tests passing (pytest): normalization, email clustering, prior-link merge, two-way merge, last-write-wins on conflicting fields, no-duplicate merge-by-email, convergence, dry-run-writes-nothing.
  • CLI status smoke-tested.

Status / not-yet-done (intentional — flag before adding)

  • Provider adapters need live credential validation. Logic is done; the HubSpot/Folk/Apple HTTP calls haven't been run against real accounts. Folk's payload shape in particular should be confirmed against the current API (mapping is isolated to two methods).
  • No deletion/archival propagation yet (create/update only — safe by default; deletes need a tombstone policy).
  • Companies synced as a name only, not as first-class objects.
  • No rate-limit backoff/retry; no automatic CardDAV principal discovery.

Open questions

  • Confirm Folk API payload shape against your workspace.
  • Want deletions propagated, or keep create/update-only?
  • Where should this project ultimately live — this release-notes repo is an odd home; happy to move it to a dedicated repo.

https://claude.ai/code/session_011tTG82Uxsg2nfE8yxsXfR7


Generated by Claude Code

Normalized contact model, email-based identity matching, field-level
last-write-wins merge with multi-value union, convergent two-way engine,
SQLite state store, and a status/sync CLI. Provider adapters for HubSpot
(CRM v3), Folk, and Apple Contacts via iCloud CardDAV.

13 tests passing (models, matching, full sync round-trip via in-memory
provider).

https://claude.ai/code/session_011tTG82Uxsg2nfE8yxsXfR7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants