# iMail (`IMailSync`)

## Purpose

Read Mail.app messages via AppleScript / ScriptingBridge and stream them to
the server.

## Direction

Read-only in practice (Mac to server). The `Sender` is implemented but unwired
— see [known-limitations](../known-limitations.md#imail-sender-is-unwired).

## OS surface

Mail.app via ScriptingBridge. The `Package.swift:64-66` linker setting pulls in
`ScriptingBridge.framework`. Reader runs synchronously on a background thread
(`@packages/imail/Sources/IMailSync/SyncManager.swift:90-93`). Requires
Automation > Mail permission.

## Files

- `Reader.swift` — `Reader.shared`; `fetchMessages(since:)` traverses Mail.app
  accounts, mailboxes, and messages. Applies a ±1-minute watermark tolerance
  (documented at `SyncManager.swift:40-43`).
- `MIMEParser.swift` — minimal MIME header decoder used by the Reader.
- `APIClient.swift`:
  - `syncMail(_:)` -> `POST /client/imail/sync`
    (`@packages/imail/Sources/IMailSync/APIClient.swift:86-88`)
  - `getStats()` -> `GET /client/imail/stats` (line 102-103)
- `Sender.swift` — `Sender.send(_:)` drives Mail.app via ScriptingBridge.
- `SendQueueAdapter.swift` — `IMailSendTransport` + `MailSender` + dispatch
  closure. Used by the `SyncManager`'s `SendQueueClient` to drain
  `/client/imail/send-queue/pending`.
- `APIClient.swift` — `getPendingSends()` / `reportSendResult(...)` added
  alongside the existing read endpoints.
- `SyncManager.swift` — reads off-main, batches 50; owns a lazy
  `SendQueueClient<IMailSendTransport>` started via `didStartSync()` and
  stopped via `willStopSync()`.

## Timing

- Read interval: **300s** (`@packages/imail/Sources/IMailSync/SyncManager.swift:61`).
- Outbound `SendQueueClient`: **60s**.
- Batch size: 50.

## Server surface

- Entity tables: existing mail-ingest tables live under `features/imail/`;
  outbound table is `icloud.mail_send_queue`
  (`src/server/src/entities/mailSendItem/schema.ts`), built on the shared
  `sendQueueTableSql` factory.
- Client routes (`src/server/src/surfaces/client/imail.ts`):
  - `GET /client/imail/stats`
  - `POST /client/imail/sync`
  - `GET /client/imail/send-queue/pending`
  - `POST /client/imail/send-queue/:id/result`
- Web routes (`src/server/src/surfaces/my/mail.ts`):
  - `GET /my/mail/conversations`
  - `GET /my/mail/conversations/:id/messages`
  - `GET /my/mail/last-sync`
  - `POST /my/mail/send` — enqueues a `send_mail` action
- Admin route (`src/server/src/surfaces/admin/mail-send-queue.ts`):
  - `POST /admin/mail-send-queue/enqueue`

## Web surface

- Tab: `/mail` (`web/src/App.tsx:59`).
- API helpers: `web/src/api/mail.ts`.

## Known limitations

- **AppleScript / ScriptingBridge fragility.** The reader runs in a detached
  `Task` but a hung Mail.app blocks until macOS times the Apple event out.
  The send path inherits the same constraint.
- **No folder browser** in `/my/mail`; only conversations and messages by ID.
- **No CC/BCC UI** in the web Compose form yet (the server payload schema and
  Sender both accept them; only the web form is missing them).

## Tests (`@packages/imail/Tests/IMailSyncTests/`)

- `MIMEParserTests.swift` — header decoding (hermetic).
- `SenderTests.swift` — `MailSender` dispatch with a `FakeApplier`: success
  path, unknown-action failure, error propagation.

Not covered by tests: Mail.app reading, ScriptingBridge bindings, live send.
