Skip to content

Latest commit

 

History

History
178 lines (147 loc) · 9.61 KB

File metadata and controls

178 lines (147 loc) · 9.61 KB

Development Rules

Test-driven workflow

For every feature:

  1. Define the CLI behavior and JSON contract.
  2. Add a failing unit or integration test.
  3. Implement the smallest change that makes the test pass.
  4. Run the full test suite.
  5. Build a release binary, install it locally, and verify the real CLI when the feature touches macOS frameworks.
  6. Update the usage documentation and roadmap status.

Tests must not create real Contacts records repeatedly. Use deterministic pure tests for mapping and matching, and keep the documented local fixtures for occasional end-to-end verification.

The real CLI integration smoke test is deliberately separate from swift test and from CI. Run the safe local path with:

bash scripts/run_local_contacts_integration.sh

For process-level JSON contract and negative-path checks, run:

bash scripts/run_cli_contract_tests.sh

This suite is local-only and does not write or delete Contacts records.

Calendar 0.3 has separate process-contract, read-only, and dry-run gates:

bash scripts/run_calendar_contract_tests.sh
bash scripts/run_calendar_read_smoke.sh
bash scripts/run_calendar_dry_run_smoke.sh
bash scripts/run_local_calendar_integration.sh

They do not save Calendar changes. A real Calendar CRUD gate requires separate explicit authorization and may operate only on one disposable event through create, read-back, edit, read-back, delete, and absence verification. The real gate also requires --with-writes --confirm "CALENDAR CRUD TEST"; that command-line phrase does not replace explicit user authorization for the current task.

Only when explicitly validating real writes, run the disposable-contact path:

bash scripts/run_local_contacts_integration.sh --with-writes

The write path creates and cleans up only the temporary integration contact. It must not delete the permanent person, organization, or create smoke-test fixtures.

Contacts contract

  • kind is person or organization and comes from the native Contacts record type.
  • external_id is optional in read models but required for creation.
  • The CLI must never create a contact without external_id; this is a permanent Contacts contract, not a deferred feature.
  • External IDs are encoded as mpia://ext-id/<id> in the URL field.
  • The reserved URL label is strictly mpia-cli. Readers must not treat Homepage or other labels as an external ID.
  • The reserved URL value is mpia://ext-id/<id>.
  • imageAvailable is the Contacts.framework availability result; it is not a definitive statement about whether Contacts.app displays an iCloud avatar.
  • Avatar apply responses include avatar.status. readback_confirmed means the saved record returned non-empty image data. verification_unknown means the save was accepted but Contacts.framework could not safely read the image back; follow avatar.nextAction, and never auto-delete or auto-recreate the record. Avatar writes must not be retried automatically.
  • contacts avatar verify performs a lightweight availability preflight and skips imageData reads when the preflight is false, reducing iCloud fault risk.
  • contacts avatar replace is the explicit recovery path for records that cannot be edited in place. It requires RECREATE CONTACT, creates a new Contacts record, and must never be invoked automatically.
  • If a write fails with CoreData error 134092, the CLI must treat the record as potentially corrupted, preserve diagnostic details, and tell the Agent to preserve the JSON fields, delete the record with explicit confirmation, recreate it, and retry. The CLI must never auto-delete or auto-recreate a contact.
  • Apple contact identifiers are local implementation details, not cross-system IDs.
  • Query fields are normalized according to their data type; combined queries use AND semantics and accept at most three distinct fields.
  • Ambiguous matches must be reported; the CLI must not silently choose a record for a write.

Safety and privacy

  • Check Contacts authorization before reading or writing.
  • Writes require explicit --dry-run or --apply.
  • Never access the private Contacts database or upload contact data.
  • Version 0.1 permits only the iCloud container; if it is unavailable, writes must fail rather than fall back to local or another account.
  • Diagnostics retain external_id only as a correlation key. Email addresses, international phone numbers, absolute paths, and underlying exception text are redacted before being written to ~/Library/Logs/mpia-cli/diagnostics.log.
  • Diagnostics must not include names, organizations, postal addresses, avatar bytes, or full JSON contact payloads.

Calendar contract (0.3)

  • Use only Apple's public EventKit framework; never read Calendar's private database.
  • Reads require fullAccess; not-determined, denied, restricted, and write-only states produce distinct stable errors.
  • Default and explicit source selection must resolve to one unique iCloud CalDAV source and must never silently fall back to another account.
  • Queries require ordered start/end values, a range of at most 366 days, and a limit from 1 through 200.
  • Source, calendar, calevent_, and cursor IDs are machine-local opaque values.
  • A calevent_ ID binds a calendar item and occurrence start so recurring-event mutations do not accidentally target the first occurrence.
  • Create, edit, and delete require --dry-run or --apply; delete apply also requires --confirm "DELETE EVENT".
  • Recurring edit/delete requires "span":"this" or "span":"future" in params.
  • Attendees are read-only in 0.3. The adapter does not send invitations and rejects non-empty attendee input.
  • Timed Calendar dates are ISO 8601; all-day events use date-only YYYY-MM-DD with an exclusive end date. Time zones use valid IANA identifiers.
  • Each alarm uses exactly one relative or absolute trigger. alarms: [] clears alarms, and create removes inherited default alarms before applying JSON.
  • Calendar POST /calendar/create with "idempotent":true in params uses an opaque, privacy-minimized 60-second receipt for immediate process retries. It must not persist event text.
  • Conflict scans are capped at 200 events; adjacent boundaries are not conflicts.
  • Dry-run private JSON remains in auto-deleted mode-700 temporary directories; tests do not print titles, attendees, locations, URLs, or notes.
  • Real apply tests never modify existing user events and use only a disposable fixture.
  • The recurring real gate must verify this and future mutation scope and remove every occurrence even when an intermediate assertion fails.

Safari contract (0.8)

  • Safari 0.8.1 reads bookmarks and Reading List from a bounded, non-symlink Bookmarks.plist snapshot. It must preserve a strictly read-only file handle and fail closed on malformed, oversized, duplicate-proxy, or excessively deep structures.
  • Ordinary bookmarks exclude Safari proxy nodes and the Reading List subtree. All item, folder, and cursor IDs are opaque; cursors bind the exact snapshot SHA-256 and become stale after any plist change.
  • Reading List add uses Safari's official AppleScript command. Require strict JSON, --dry-run|--apply, normalized URL idempotency, a five-second deadline, and immediate bounded read-back. Never automatically retry pending or unknown outcomes.
  • Never return or diagnose raw plist IDs, plist bytes, titles, URLs, preview text, user paths, or AppleScript source.
  • Version 0.8.1 exposes direct Bookmarks.plist mutation only as guarded local-only bookmark/folder CRUD. It requires Safari fully exited, an exact source hash, metadata-exact private recovery, atomic replacement, bounded read-back, and explicit syncStatus=local_only. It must never claim iCloud synchronization or stop/modify Safari synchronization processes.
  • Existing Safari data is never a mutation fixture. Every live add or direct mutation gate requires explicit current-task authorization and verified zero-residue cleanup.

Codex authorization and Computer Use

  • Codex should automatically perform authorization and settings flows that do not require a password, Apple ID entry, or security confirmation.
  • This includes opening the relevant macOS Settings pane, launching the already-authorized local app, and accepting the explicitly requested ordinary permission prompt through Computer Use when the system allows it.
  • Hand the flow to the user in an external Terminal or UI only when macOS asks for an administrator password, Apple ID credentials, a security confirmation, or another secret that Codex must not enter.
  • Do not repeatedly ask the user to click through a flow that Codex can safely complete. Report the exact remaining hand-off point and the reason.
  • Computer Use actions must remain within the requested app and permission scope. Never type, store, or expose passwords, tokens, or other credentials.

Compatibility

The current deployment target is macOS 26.0+. Use the repository's Swift/Xcode toolchain and keep framework availability checks close to the adapter boundary.

For compatibility verification, rebuild the Release configuration before testing the binary. A stale .build/release/mpia may not contain the latest source changes.

Metadata (0.1)

metadata belongs to the JSON contract only. Contacts 0.1 does not promise to write it into Apple Contacts; it must not be silently encoded into Notes, URLs, or another field. Any future persistence requires a versioned encoding and migration rule.

Ordinary reads must not request image bytes. Avatar verification first performs a lightweight availability preflight; avatar replacement is the explicit recovery path for records that cannot safely be edited in place.