# OpenSpec Change: enterprise-contacts - **Change ID**: enterprise-contacts - **Package**: `@suaveplan/contacts` - **Category**: universal - **Target path**: `universal/email/contacts` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/contacts.md` --- ## Summary Deliver `@suaveplan/contacts` on the genesis branch from scratch at enterprise quality. The package provides contact and group management: a typed `Contact` model with hierarchical group nesting, CRUD operations with optimistic concurrency (ETag-style version fields), deduplication via `@suaveplan/dedup`, tag-based filtering, CSV/VCard import-export, full-text search across name/email/phone fields, a `ContactStore` interface with an in-memory reference adapter, and event bus integration for change propagation. Genesis adds structured errors, Zod schemas, OTel instrumentation, and property-based tests. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `ContactError`; `CONTACT.*` error codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike`, `StorageLike` | | `@suaveplan/events` (`workspace:^`) | Change event bus | | `@suaveplan/dedup` (`workspace:^`) | Email/phone deduplication engine | | `@suaveplan/pagination` (`workspace:^`) | Cursor/offset pagination for contact lists | | `zod` (`catalog:forms`) | Schema validation | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`). --- ## Public API surface ### Core types ```ts interface Contact { id: string version: number // optimistic concurrency firstName?: string lastName?: string displayName: string email: EmailEntry[] phone: PhoneEntry[] address: AddressEntry[] tags: string[] groupIds: string[] metadata: Record createdAt: Date updatedAt: Date } interface EmailEntry { value: string label?: string // 'work' | 'personal' | custom primary?: boolean } interface PhoneEntry { value: string label?: string primary?: boolean } interface AddressEntry { street?: string city?: string state?: string postalCode?: string country?: string label?: string } interface ContactGroup { id: string name: string parentId?: string // hierarchical nesting contactCount: number createdAt: Date } ``` ### `ContactStore` ```ts interface ContactStore { create(data: ContactCreateInput): Promise get(id: string): Promise update(id: string, patch: ContactPatch, expectedVersion: number): Promise delete(id: string): Promise search(query: string, options?: SearchOptions): Promise> list(filter?: ContactFilter, options?: ListOptions): Promise> importCsv(csv: string, options?: ImportOptions): Promise importVcard(vcard: string): Promise exportCsv(filter?: ContactFilter): Promise deduplicate(options?: DeduplicateOptions): Promise } function createMemoryContactStore(options?: MemoryStoreOptions): ContactStore ``` ### Error codes ```ts type ContactErrorCode = | 'CONTACT_NOT_FOUND' | 'CONTACT_VERSION_CONFLICT' | 'CONTACT_DUPLICATE_EMAIL' | 'CONTACT_INVALID_EMAIL' | 'CONTACT_IMPORT_FAILED' | 'CONTACT_GROUP_NOT_FOUND' ``` --- ## Genesis additions 1. **`ContactError`** extends `SuaveplanError` with six stable codes. 2. **Zod schemas** for `Contact`, `ContactGroup`, `EmailEntry`, `PhoneEntry`, `AddressEntry`, `ContactFilter`, `SearchOptions`. 3. **VCard 3.0/4.0 import** — parses `BEGIN:VCARD` blocks into `Contact` records; exports contacts as VCard 4.0. 4. **Deduplication** — delegates to `@suaveplan/dedup`; surfaces a `deduplicate()` method returning merge candidates. 5. **OTel instrumentation** — spans on `create`, `update`, `delete`, `search`; counters `contact.create_total`, `contact.search_total`; histogram `contact.search_duration_ms`. 6. **Property-based tests** — CSV import/export round-trip, optimistic concurrency conflict detection. --- ## File layout ```text universal/email/contacts/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/ index.ts types/ common.ts common.md core/ contact-store/ contact-store.ts contact-store.md contact-store.types.ts contact-store.test.ts index.ts memory-store/ memory-store.ts memory-store.md memory-store.test.ts index.ts io/ csv-import/ csv-import.ts csv-import.md csv-import.test.ts index.ts vcard-import/ vcard-import.ts vcard-import.md vcard-import.test.ts index.ts csv-export/ csv-export.ts csv-export.md csv-export.test.ts index.ts search/ contact-search/ contact-search.ts contact-search.md contact-search.test.ts index.ts __benchmarks__/ contacts.bench.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Downstream consumers**: `@suaveplan/campaigns`, `@suaveplan/audience-rules`. **Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/dedup`, `@suaveplan/pagination`.