Skip to content

ADR 0027: Follow-on providers and gated inventory

Status

Accepted

Context

Graph is the MVP mailbox. Architecture still requires a second adapter to prove the port, plus inventory/plan/dry-run contracts so bulk iCloud-to-Microsoft execution can stay a later flag-on milestone. MAIL-20 and MAIL-169 must not delay Synchronization Alpha on Graph. Live Gmail or iCloud OAuth is not required for this decision.

Decision

  1. Implement Gmail and iCloud against the existing ProviderAdapter port in packages/providers. Gmail uses recorded HTTP fixtures and least-privilege scopes (gmail.readonly, then gmail.modify). https://mail.google.com/ and send/compose scopes are rejected. iCloud is IMAP inventory only: write methods return CAPABILITY_NOT_SUPPORTED.
  2. Isolate account worker loops. A Gmail throw cannot halt Graph processing.
  3. Ship InventorySource, AnalyzeFolders, CreateMigrationPlan, and RunDryRun. Dry-run never calls adapter writes. StartMigration, PauseMigration, and ResumeMigration return POLICY_DENIED while ENABLE_ICLOUD_MIGRATION is off. Defaults stay dry_run_required=true and source_delete_allowed=false.
  4. ENABLE_SEND and ENABLE_DELETE stay false. AI still cannot call adapters.

Alternatives

Consequences

Recorded Gmail fixtures and mock IMAP satisfy contract tests. Migration 0019_followon_providers.sql persists plans and dry-run estimates. Live Microsoft connect (ADR 0015) is unchanged. Bulk historical migration remains out of scope.

MAIL-254 later adds the live Gmail product connection without changing this decision's MVP boundary: PKCE OAuth uses the existing least-privilege scope policy, tokens use the existing encrypted token store, and each Google identity is a separate connected account. MAIL-257 verifies that Gmail failure remains isolated from Graph processing.

MAIL-239 / MAIL-250 add the iCloud product connection. The app-specific password is an imap_secret ciphertext (ADR 0030). ADR 0031 allowed folder and cross-account move. ADR 0032 supersedes the remaining iCloud write denial: archive, mark read, flag, and delete change the mailbox. Send stays off. One user keeps every connected iCloud address, each with its own app-specific password.