Add opt-in Daily Digest (daily email of last 24h of library recaps)

Multi-mode, off by default. Each new recap is synthesized into a 1-2
paragraph overview via the relay (operator-absorbed) and cached onto the
session JSON; a daily 08:00 scan emails opted-in users their fresh
recaps, deduped by a per-user watermark that never skips a failed or
over-cap recap. One-click tokenized unsubscribe; settings-modal toggle;
admin test trigger. Bumps to 0.2.158.
This commit is contained in:
Keysat
2026-06-15 19:50:48 -05:00
parent 962423ca10
commit b4fa5d7be8
14 changed files with 1144 additions and 17 deletions
+2 -2
View File
@@ -127,13 +127,13 @@ unsure whether a change is contract-affecting, assume it is and check.
## Current state
**Live on the operator's StartOS box** — app **0.2.157** + relay **0.2.124**. Tests: `cd server && npm test` → **119 pass**.
**Live on the operator's StartOS box** — app **0.2.157** + relay **0.2.124** (0.2.158 built + committed, install in progress). Tests: `cd server && npm test` → **142 pass**.
**Done & live:** self-serve Pro/Max purchase (Bitcoin inline-Lightning + Zaprite card, prepaid, relay owns tier/expiry), core-decoupling, per-tenant subscriptions, and expiry-reminder emails (`POST /api/admin/reminders/run {test_email}`). Plans in `docs/*-plan.md`.
**Shipped this session (committed + pushed + verified live):** **0.2.156** — iOS sign-in "network error" flake; both sign-in paths now retry 3× with growing backoff (`91af0b7`). **0.2.157** — mobile/UX cluster: YT-minimize black-frame, podcast-audio + scroll loss on background re-render, redundant loading box, and a best-effort iOS scroll tweak (`693bb98`). Mechanics now captured as conventions above.
**In progressDaily Digest** (`docs/daily-digest-plan.md`, **proposed, awaiting go-ahead**): opt-in (off by default) daily email of the last 24h of library recaps, each a 12 paragraph overview synthesized from the recap's stored topic summaries; clones the `subscription-reminders.js` scan pattern. Next: build **phase 1** (schema `users.digest_enabled`/`last_digest_at` + opt-in toggle + settings UI) — but **first resolve open Q4: does `/relay/analyze` fit the synthesis call, or is a new relay capability needed?** (cross-repo: would touch `../recap-relay`). The other 3 open Qs (synthesis-cost owner, send hour, single-mode) have defaults in the plan.
**Daily DigestFEATURE-COMPLETE; 0.2.158 built + committed, install in progress** (`docs/daily-digest-plan.md`). Opt-in (off by default) once-a-day email of a user's last ~24h of library recaps, each a 12 paragraph overview synthesized from the recap's stored topic summaries; clones the `subscription-reminders.js` scan pattern. **All 5 phases built:** schema (`users.digest_enabled`/`last_digest_at`/`digest_unsub_token` + `migrateUserDigestPrefs`); `GET`/`POST /api/account/digest` (opt-in stamps the watermark to now) + settings-modal toggle; `server/daily-digest.js` (synthesis via `/relay/analyze`, **operator-absorbed** via operator install identity, `digestOverview` cache; `selectDigestEpisodes` watermark/cap/overflow; `runDigestScan` acts at `SEND_HOUR=8`, `MIN_RESEND_MS=20h`, advances watermark only on send, never throws; `startDigestScheduler`; public `GET /api/digest/unsubscribe?token=`); `renderDigestEmail`; `listScopeSessions`; `POST /api/admin/digest/run {test_email}`. Wired in `index.js` (multi) + `tenant-auth.js` public path. **19 digest tests, full suite 138 pass.** Verified on a real multi-mode boot (migrations apply, scheduler starts, unsubscribe 400/404/200 flips the flag end-to-end). **Q4** — `/relay/analyze` fits as-is, no relay change. **Q1** — operator-absorbed, zero operator action. **Pending: on-box smoke test** (relay synthesis + SMTP only run on the box) — `POST /api/admin/digest/run {test_email}` to eyeball the render, then opt in + add a recap + force a scan. **No version bump yet.** (Aside: relay `AGENTS.md:78` mis-describes `/relay/analyze` as `{transcript}→topic sections JSON` — stale; flagged for `../recap-relay` in the inbox.)
**Pending operator actions:**
1. **Verify the mobile can't-scroll-to-top fix on the iPad** — UNVERIFIED in 0.2.157 (iOS-layout-specific, not reproducible off-device); send a screen recording if it persists. Inbox item kept open + annotated.