Five send sites now construct their envelopes through
`email_envelope.build_envelope` instead of building `EmailMessage`
ad hoc:
* `email_otc.py` — OTC mail (no List-Unsubscribe per the
proposal's tradeoff: the recipient explicitly requested the
code, so a list semantic would be wrong).
* `email_invite.py` — admin/per-RFC invite mail (mailto:-only
List-Unsubscribe — the invitee isn't a user yet, so no
per-user opt-out URL exists).
* `email._deliver` — watcher notifications (full one-click
unsubscribe: mailto + signed URL + List-Unsubscribe-Post per
RFC 8058, required by Gmail/Yahoo).
* `email._send_bundle` — the "while you were away" bundle,
one-click to the new `all` synthetic category (which lands
`email_opt_out_all = 1` because the bundle spans multiple
per-category flags).
* `digest.py` — same as the bundle: bulk-adjacent, one-click to
`all`.
`api_notifications.py` gains the POST `/api/email/unsubscribe`
endpoint (the matching receiver for `List-Unsubscribe-Post:
List-Unsubscribe=One-Click`) and accepts the `all` category in
both GET and POST handlers.
`EmailConfig` adds `unsubscribe_mailto` (env: `EMAIL_UNSUBSCRIBE_MAILTO`,
default = `EMAIL_FROM`) so deployments can route unsubscribe
courtesy mail to a humans-monitored mailbox distinct from the
no-reply sender.
The `_SENT` test buffer now also carries `envelope["message"]`
(the `EmailMessage` itself) so new tests can assert on headers
directly. Legacy `to`/`from`/`subject`/`body` keys remain for
backward compatibility.
10 new integration tests across test_otc_vertical /
test_admin_create_user_invite_vertical / test_notifications_vertical
covering: OTC has no List-Unsubscribe; invite has mailto: only;
notification has full one-click; POST one-click flips the
category; `all` category sets global opt-out via both GET and
POST.
Full suite: 277 passed.