Skip to content

ADR 0025: Safe provider write-back through MailCommand / ProviderMutation

Status

Accepted

Context

Rules and UI must not call mail provider adapters. Sprint 06 Stream B (MAIL-44 / MAIL-45 / MAIL-43) requires user-requested read, flag, and archive write-back, but only after a durable command exists. Provider HTTP success is not canonical state. Send and delete stay off. AI cannot authorize side effects. Graph write methods already exist; the missing piece was the command outbox and sync convergence.

Decision

  1. Every write creates a MailCommand and ProviderMutation first (0017_provider_mutations.sql). The row has an idempotency key, actor, audit event, pre-execution snapshot, and status PENDING.
  2. evaluatePolicy (ADR 0019) runs with actor USER before any adapter call. AI actors are denied. Send and delete persist as FAILED / POLICY_DENIED and never call an adapter. ENABLE_SEND is not authorization.
  3. Graph-capable safe writes (MarkRead, MarkUnread, SetFlag, Archive, Move) run only when ENABLE_MICROSOFT_WRITES=true and the adapter advertises the capability. Unsupported work returns CAPABILITY_NOT_SUPPORTED. Partial bulk reports explicit counts.
  4. Adapter success moves the mutation to ACCEPTED, not SUCCEEDED. Optimistic API responses may return command_id + pending. Healthy status requires normal sync to observe the desired state (MAIL-65). Echoes update canonical instance state and do not enqueue duplicate user workflows. Mismatch becomes RECONCILE_NEEDED and is visible in health.

Alternatives

Consequences

Application owns submitCommand and convergeWriteBacks. providers/graph owns capability mapping and Graph PATCH/move. Incremental sync remains the observation path. Gmail and iCloud stay out of scope.