6.9 KiB
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 statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create
universal/platform/macos-contacts/withsrc/,tests/, and feature subdirectories per the layout inproposal.md. - Write
package.jsonat version0.1.0:"type": "module","sideEffects": falseexportsmap:"."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:builddevDependencies:vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,fast-check catalog:testing,@suaveplan/testing workspace:^
- Write
tsconfig.jsonextending@suaveplan/typescript-config/library.json. - Write
vitest.config.tsusingcreateTestConfig(). Add platform guard: skip osascript tests whenprocess.platform !== 'darwin'. - Add
lint,typecheck,test,test:coverage,build,docs:checkscripts.
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:MacosContactsErrorCodeunion, metadata,registerMacosContactsErrorCodes(). - Write Zod schemas in
src/types/schemas.ts+src/types/schemas.md.
3. Process runner (src/core/process-runner/)
- Implement
createOsascriptRunner(options?)— wrapsBun.spawn(or NodeexecFilefallback — neverexec, to prevent shell injection) to run AppleScript viaosascriptwith-eas a separate argument (not interpolated into a shell string). - Implement default
timeoutof 10 000 ms; throwMacosContactsError(CONTACTS_SCRIPT_FAILED)on non-zero exit. - Expose the
ProcessRunnerinterface 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
MacosContactobjects. - Implement
parseContact(raw: string): MacosContactandparseContacts(raw: string): MacosContact[]. - Validate output with
MacosContactSchema; throwMacosContactsError(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 tocreateOsascriptRunner()), logger, retry. - Implement
listContacts(options?, signal?)— calls list script, parses output; OTel spanmacos_contacts.list. - Implement
getContact(id, signal?)— OTel spanmacos_contacts.get. - Implement
searchContacts(query, options?, signal?)— OTel spanmacos_contacts.search. - Implement
createContact(data, signal?)— validates withCreateContactDataSchema; OTel spanmacos_contacts.create. - Implement
updateContact(id, data, signal?)— OTel spanmacos_contacts.update. - Implement
deleteContact(id, signal?)— OTel spanmacos_contacts.delete. - Implement
listGroups(signal?),addToGroup(...),removeFromGroup(...). - All async methods check
signal.abortedbefore invoking runner. - Wrap runner failures with retry via
@suaveplan/retry. - Write
contacts-client.test.ts(≤500 lines) using injected fakeProcessRunner. No osascript calls in unit tests. - Write
contacts-client.md(≥200 words).
7. Property-based tests (tests/property.test.ts)
parseContactsround-trip: for validMacosContactobjects, serialise to AppleScript output format and re-parse; result equals original.- Schema idempotency:
MacosContactSchema.parse(MacosContactSchema.parse(c))equalsparse(c). - Search filtering: contacts matching query string are always a subset of all contacts.
8. Docs
- All per-file
.mdfiles: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.mdpackage listing. - Archive: move
openspec/changes/enterprise-macos-contacts/toopenspec/archive/2026-04-24-enterprise-macos-contacts/.