661ad35ee5
- docs/guides/email.md: new "Outbound mail — the daily digest" section (Gmail-DWD primary → SMTP fallback; gmail.compose send capability; the internal-digest exemption from the agents-draft rule). - AGENTS.md: add digest env names (CRM_DIGEST_SENDER, SMTP_*); consolidate the v75/v76 deploy bullets into one current bullet; drop finished v74 narrative.
2.7 KiB
2.7 KiB
paths
| paths | |
|---|---|
|
Email capture & drafts (Gmail)
Read this before editing Gmail capture or draft creation.
What it does
backend/email_integration/captures Gmail via domain-wide delegation (credentials.py,matcher.py,parser.py,db.py,sync.py,scheduler.py,routes.py) and creates Tier-B in-thread drafts (compose.py). It has its ownmigrations/.- Captured email becomes CRM activity through a propose → approve flow — nothing lands on a contact record until a human approves the proposal.
Hard rule
- Agents draft; humans send. Never let an agent send email, post, or contact an LP autonomously. Tier-B
compose.pyonly creates a Gmail draft for human review.
Outbound mail — the daily digest (internal; exempt from "agents draft")
The CRM sends an internal daily activity digest to the fund's own admins. This is the ONE automated send path, and it does not violate the hard rule above: that rule governs outward LP/prospect contact. An internal ops email to the team's own inboxes is a different category. Never extend this path to send to LPs/prospects.
- Transport selector:
backend/digest_mailer.py(top-level, not in this package) —send_digest(conn, to_addrs, subject, body)picks Gmail-DWD (preferred) → SMTP (fallback). DWD-impersonation sender =CRM_DIGEST_SENDERenv, else the first active admin. - Gmail-DWD path:
gmail_send.py(this package) — reusescredentials.py'sDWDCredentialProviderwith thegmail.composescope to callusers.messages.send(REST, mirrorscompose.py; body is{raw}not the draft's{message:{raw}}). The deployment's DWD grant includesgmail.compose(which authorizes send) but not the narrowgmail.send— so requestgmail.compose. Verified live 2026-06-15 (token mint + a realmessages.send). - SMTP fallback:
backend/smtp_send.py(top-level) — stdlib smtplib readingSMTP_*env, populated on the box by the Configure Digest SMTP Start9 action (writes/data/secrets/smtp/*; entrypoint exportsSMTP_*). A dedicated per-package account, independent of any StartOS system-wide SMTP. - The admin
POST /api/admin/digest/test-emailrestricts recipients to the active-admin set (not an open relay), and logs send failures rather than echoing them (an auth error can carry a token/credential). Digest content generation (Phase B) runs on Spark, never Claude — the digest is deliberately un-anonymized.
Known gap
- Tier-B drafts currently reply to the LP only; reply-all is the next change (see AGENTS.md → Current state).
See also docs/gmail-enablement-runbook.md.