Add daily activity digest — Phase B (v0.1.0:77)

Sends a once-a-day internal email to all active admins summarizing each team
member's email activity per investor, plus a team-wide by-investor view
(inbound + outbound, deduped). Narratives are generated on the LOCAL Spark
model, never Claude — the digest is intentionally un-anonymized, so substance
stays on Ten31 infra. This is an internal ops email, exempt from the
'agents draft, humans send' rule (which governs outward LP contact).

- backend/digest_builder.py: per-user + per-investor activity queries
  (soft-delete filtered), per-user Spark narrative with a deterministic
  fallback, two-section plain-text body, and the DB-backed policy resolver.
- backend/email_integration/digest_scheduler.py: always-on daily thread that
  re-reads the policy each cycle and sends once/day; window cursor in
  app_settings so a missed day rolls forward.
- server.py: POST /api/admin/digest/send-now and GET/PATCH
  /api/admin/digest/policy; scheduler wired into main().
- Control lives in Settings -> Admin (enable toggle + send-time dropdown),
  not StartOS actions; env vars only seed the first-boot default.
- Tests: backend/test_digest_builder.py.
This commit is contained in:
Keysat
2026-06-15 22:32:27 -05:00
parent 036226ed74
commit 323f016f64
12 changed files with 1113 additions and 19 deletions
+10
View File
@@ -20,6 +20,16 @@ X_API_KEY=
CRM_DB_PATH=./data/crm.db
CRM_DEV_DB_PATH=./data/crm_dev.db
# ── Daily activity digest (Phase B) ──
# The daily digest (each team member's activity per investor + a by-investor view,
# summarized LOCALLY on Spark — never Claude) is controlled from Settings → Admin
# (stored in the DB). These env vars only SEED the first-boot default before an
# admin sets it; once the policy row exists, the admin panel wins. The "Send Digest
# Now" button works regardless. Leave blank to default to off / 6 PM.
CRM_DIGEST_ENABLED=
# Local (box-time) hour 0-23. Default 18 (6 PM).
CRM_DIGEST_SEND_HOUR=18
# ── Daily-digest sender ──
# The digest mailer prefers Gmail domain-wide delegation (the service account that
# already powers email capture; its grant includes gmail.compose, which can send) and