Files
2026-05-27 23:29:53 +01:00

6.9 KiB

Tasks: enterprise-macos-contacts

Change ID: enterprise-macos-contacts Package: @suaveplan/macos-contacts Target path: universal/platform/macos-contacts Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/logger, @suaveplan/retry


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create universal/platform/macos-contacts/ with src/, tests/, and feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0:
    • "type": "module", "sideEffects": false
    • exports map: "." barrel + "./core/contacts-client/contacts-client", "./core/process-runner/process-runner", "./utils/parse-contact", "./types/common"
    • dependencies: @suaveplan/error workspace:^, @suaveplan/types workspace:^, @suaveplan/logger workspace:^, @suaveplan/retry workspace:^, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build
    • devDependencies: vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, fast-check catalog:testing, @suaveplan/testing workspace:^
  • Write tsconfig.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig(). Add platform guard: skip osascript tests when process.platform !== 'darwin'.
  • Add lint, typecheck, test, test:coverage, build, docs:check scripts.

2. Types and error codes

  • Define all public types in src/types/common.ts: MacosContact, MacosAddress, MacosGroup, CreateContactData, ListContactsOptions, SearchContactsOptions, ContactsClientOptions, ProcessRunner, OsascriptRunnerOptions.
  • Write src/types/common.md (≥200 words, simple + advanced examples).
  • Define error codes in src/error-codes.ts: MacosContactsErrorCode union, metadata, registerMacosContactsErrorCodes().
  • Write Zod schemas in src/types/schemas.ts + src/types/schemas.md.

3. Process runner (src/core/process-runner/)

  • Implement createOsascriptRunner(options?) — wraps Bun.spawn (or Node execFile fallback — never exec, to prevent shell injection) to run AppleScript via osascript with -e as a separate argument (not interpolated into a shell string).
  • Implement default timeout of 10 000 ms; throw MacosContactsError(CONTACTS_SCRIPT_FAILED) on non-zero exit.
  • Expose the ProcessRunner interface so tests can inject a fake runner without module-level mocking.
  • Write process-runner.test.ts: fake runner happy path, timeout, non-zero exit code.
  • Write process-runner.md (≥200 words).

4. Contact parsing (src/utils/parse-contact.ts)

  • Parse the tab-delimited or structured text output returned by AppleScript contact scripts into MacosContact objects.
  • Implement parseContact(raw: string): MacosContact and parseContacts(raw: string): MacosContact[].
  • Validate output with MacosContactSchema; throw MacosContactsError(CONTACTS_PARSE_FAILED) on schema error.
  • Write parse-contact.test.ts (≤500 lines): full contact, empty fields, multi-email, no phone.
  • Write parse-contact.md (≥200 words).

5. AppleScript templates (src/scripts/)

  • Write list-contacts.applescript: returns all contacts with id, name, emails, phones.
  • Write get-contact.applescript: looks up by id, returns full contact record.
  • Write search-contacts.applescript: filters by query string across specified fields.
  • Write create-contact.applescript: creates new contact from JSON-encoded data passed as argument.
  • Write update-contact.applescript: updates existing contact fields.
  • Write delete-contact.applescript: deletes contact by id.
  • Write list-groups.applescript: returns all groups.

6. Contacts client (src/core/contacts-client/)

  • Implement createContactsClient(options?) — wires process runner (defaults to createOsascriptRunner()), logger, retry.
  • Implement listContacts(options?, signal?) — calls list script, parses output; OTel span macos_contacts.list.
  • Implement getContact(id, signal?) — OTel span macos_contacts.get.
  • Implement searchContacts(query, options?, signal?) — OTel span macos_contacts.search.
  • Implement createContact(data, signal?) — validates with CreateContactDataSchema; OTel span macos_contacts.create.
  • Implement updateContact(id, data, signal?) — OTel span macos_contacts.update.
  • Implement deleteContact(id, signal?) — OTel span macos_contacts.delete.
  • Implement listGroups(signal?), addToGroup(...), removeFromGroup(...).
  • All async methods check signal.aborted before invoking runner.
  • Wrap runner failures with retry via @suaveplan/retry.
  • Write contacts-client.test.ts (≤500 lines) using injected fake ProcessRunner. No osascript calls in unit tests.
  • Write contacts-client.md (≥200 words).

7. Property-based tests (tests/property.test.ts)

  • parseContacts round-trip: for valid MacosContact objects, serialise to AppleScript output format and re-parse; result equals original.
  • Schema idempotency: MacosContactSchema.parse(MacosContactSchema.parse(c)) equals parse(c).
  • Search filtering: contacts matching query string are always a subset of all contacts.

8. Docs

  • All per-file .md files: common.md, schemas.md, process-runner.md, parse-contact.md, contacts-client.md.
  • universal/platform/macos-contacts/README.md: purpose, macOS requirement note, installation, quick-start, API section, dependency table.

9. Verify and publish

  • bunx turbo lint --filter=@suaveplan/macos-contacts — zero errors.
  • bunx turbo typecheck --filter=@suaveplan/macos-contacts — zero errors.
  • bunx turbo test --filter=@suaveplan/macos-contacts — all tests pass (unit tests with fake runner).
  • bunx turbo test:coverage --filter=@suaveplan/macos-contacts — 100% on all touched modules.
  • bun run docs:check --filter=@suaveplan/macos-contacts — zero errors.
  • bun scripts/publish.ts --package @suaveplan/macos-contacts.
  • Update CLAUDE.md package listing.
  • Archive: move openspec/changes/enterprise-macos-contacts/ to openspec/archive/2026-04-24-enterprise-macos-contacts/.