Skip to content

Handoff: CreatorTrack Personal Contacts <-> Google Contacts two-way sync

For: an Opus remote worker. Repo: ~/creatortrack (~/forge-suite is a symlink to it). Base: main. This is LARGE: run /feature-plan first, get Justin's spec approval, THEN build.

Work isolation (MANDATORY)

  • git -C ~/creatortrack worktree add ~/creatortrack-wt/contacts-gsync -b feat/contacts-google-sync main then cp -al ~/creatortrack/node_modules ~/creatortrack-wt/contacts-gsync/node_modules. Worktree dev uses next dev --webpack. Commit OFTEN; merge to main ONCE (--no-ff).

What/where

  • App = id "crm" (renamed Personal Contacts this session), components/apps/crm/. People live in the crm.people system collection (lib/system-apps.ts); createPerson self-provisions the app. Fields to map: name, birthday, anniversary, addresses, emails, phones, "how you know them", notes.
  • Two-way sync (Justin confirmed): import Google Contacts → crm.people; write app edits back to Google via the People API (people.createContact/people.updateContact). Conflict handling via updateTime/etag (last-writer-wins is acceptable v1 if you log skips).

Reuse, do NOT hand-roll (doctrine)

  • Google OAuth: CT's own GCP project creatortrack-499820 (oauth project). Publish to Production (Testing = 7d token expiry); ENABLE the People API; add scope https://www.googleapis.com/auth/contacts.
  • Account-connection plumbing already exists for Calendar two-way sync (calendar, gcal sync handoff): per-workspace connected Google accounts, select_account=1 add-account flow, token storage/refresh. MIRROR that pattern for contacts; do not build a second OAuth path. See agent integrations HUB.
  • Auth single-host: Google login only on app.creatortrack.ai (auth single-host).

Schema + sync

  • A mapping table linking crm.people rows <-> Google resourceName + etag for idempotent upsert and write-back (never double-create). Migration in db/migrations/*.sql.
  • Background sync on a systemd timer, cadence like forge-creatortrack-sales-gmail-sync.timer (every 30 min) or campaigns-sync. Idempotent (upsert keys). Fail loud + notify on auth/quota errors.

Verify before merge

  • npx tsc --noEmit 0 errors; eslint clean. In the dev app: connect a Google account, run an import, confirm contacts land in Personal Contacts; edit a person, confirm it writes back to Google; re-run sync, confirm no duplicates.
  • Register: MEMORY.md index line + memory/general/reference_creatortrack_contacts_google_sync.md.
  • Merge to main --no-ff, notify Justin's origin chat.