Skip to content

Sprint 03 runtime: Graph connect, workers, initial sync

Duckbill can authorize a Microsoft mailbox, run restartable initial sync against the provider port (mock or Graph), and process background work with leases, retry, DLQ, and pause switches.

Graph connect

Initial sync

runInitialSyncPage enumerates folders, pages messages, upserts MailMessage / MessageInstance (unique on account + provider message id), writes an outbox event, and enqueues independent FETCH_BODY and CLASSIFY work items. AI enqueue failure cannot prevent the upsert. Classification UNPROCESSED is valid. initial_sync_status stays awaiting_reconciliation until runReconciliation (MAIL-28) marks healthy. See reconciliation.

MAIL-103 exposes a session-gated bounded page at POST /api/sync/initial (default account jason@jasonkoch.io) and count-only GET /api/sync/cardinality. Responses never include mail bodies. See MVP E2E evidence.

Ongoing mailbox changes use incremental sync (MAIL-24): webhook hints, getChanges delta, cursor advance after durable process, and polling fallback. See Incremental sync.

Workers

Leased sync_work_items plus transactional outbox (ADR 0010). Transient errors back off. Authorization and exhausted attempts go to dead_letter_items. DLQ replay is explicit and audited. Poison isolation is per message id so siblings continue.

Pause and circuit breaker

automation_pauses scopes: GLOBAL, MAILBOX, ACTION_TYPE, AI. Global and mailbox pauses block COMMAND work (provider mutations). Sync pages still run. The ai circuit breaker opens on an error budget and skips only CLASSIFY. Recovery mode (MAIL-56 / ADR 0026) inserts a global pause and refuses new mutations until operators clear it. See operational health and backup and restore.

CLASSIFY work is recommendation-only (MAIL-37 / ADR 0022). The worker must not call mail provider adapters. Inference timeouts, invalid JSON, and prompt injection leave the instance synced. See AI intelligence.