Skip to content

FinFam repo has zero recurring-email/unsubscribe infrastructure — only the one-shot transactional pattern exists

TL;DR.

Recurring email in FinFam requires net-new unsubscribe machinery; reuse the proto-user, send-log, bulk-send, and maybe_enqueue cron patterns.

Verified by exhaustive grep (2026-07-27): fsrv has no subscription/preference table (devices.notification_prefs is push-only JSON), no unsubscribe token, no unsubscribe route, no footer unsubscribe link in notifications/email_templates/base.html, and no List-Unsubscribe header in the Postmark send path. The 'get a View report as email' feature (viewables/service/email_report.py) is deliberately one-shot transactional: its only opt-out is a sentence of copy. What IS reusable for any recurring email feature: (1) the proto-user pattern — a proto user is a real user row with registered_at IS NULL, created via create_user(email=..., email_verified=False, signup_source='<source>'), deduped case-insensitively by get_user_by_email, upgraded through the ordinary magic-link login (send_login_email coerces signup→login for existing rows) then RegistrationModal → complete_user_registration; (2) the ViewReportSend-style send-log table for rate limiting (per-user-per-hour + per-IP-per-day counts); (3) send primitives in notifications/service.py — send_email_sync (inline, stamina-retried) and queue_bulk_template_emails (fan-out, per-recipient template_data, emits BULK_EMAIL_SENT tallies); (4) ashes templates auto-registered by file stem. What must be built new for any subscription: the subscription table, a random unsubscribe token column, an unauthenticated unsubscribe route + frontend page, the footer unsubscribe link, and optional List-Unsubscribe header plumbing. Scheduling constraint: docs/scheduling.md forbids new Render cron services — periodic sends must be an idempotent maybe_enqueue* dispatcher line in scripts/render_frequent_cron.sh (*/10 tick), cloning ff/src/ff/biz/_pulse.py::maybe_enqueue_pulse_digest's three gates (due / satisfied / cooldown).

No signals yet