2026-09-15 review-and-plan: lifecycle email/SMS
Reviewed: [[2026-08-31-studio-charter]] §2/§8; queue.md + escalations.md (grepped for email/SMS/delivery/nudge terms); prior reviews [[2026-09-08-lifecycle-email-sms]] and [[2026-09-11-unit-economics-cost-basis]]; RayDataCo/scribble-works @ main 6c48991 (2026-09-14): workers/daily-playset/src/{deliver.js,send.js,email.js,recovery.js}, src/lib/{auth/mail.js,email-plan.js,accounts/plans.js}, docs/GENERATION-BUDGET.md, migrations-accounts/0015_email_metering.sql; git log on deliver.js/recovery.js.
Findings (ranked)
No day-0 welcome email; the doc closing the "never engaged" gap has sat unbuilt 13 days. Only two transactional templates exist repo-wide: magic-link sign-in, adult invite (
src/lib/auth/mail.js). Daily delivery defaults off (src/lib/accounts/plans.js:22) — nothing emails a household that signs up and never toggles it on. [[2026-09-02-retention-nudges-plan]] specs this (N5 "quiet for 21 days") but is unbuilt; [[2026-09-08-lifecycle-email-sms]] already flagged it open, and it's still open — no commits touchnudgesanywhere.queue.md:205 is stale — the zero-row-miss bug is fixed. Pull request (PR) #133 (commit
4315ace, mergedab5b741, 2026-09-09 20:52 Eastern Time (ET), after that day's classifier block inescalations.md:202-203) added a try/catch inrunTickaround its call todeliverHouseholdthat always writes afailedrow on throw. Commita8ddc59(09-10) then restructureddeliverHouseholdinto a claim-checking wrapper and moved that per-household catch out ofrunTickinto the wrapper itself;289a11f(09-11) replaced the catch's unconditionalrecordDeliverywith the new outbox/recovery pattern (event(),outboxFor()), confirmed live in currentdeliver.js. queue.md still reads "Live-verified still unfixed."Resend volume is now metered — closes the 09-11 review's finding #5.
migrations-accounts/0015_email_metering.sql+docs/GENERATION-BUDGET.md, PR #197/#250, merged 2026-09-13 (cb3a73d). Plan baseline: Resend Free, 3,000/mo, 100/day (src/lib/email-plan.js). No automated alert reads that cap;report:emailis manual-run only.SMS is inbound-only. No outbound Twilio use outside
feedback-intake's inert/smsdoor.No
List-Unsubscribeheader, but a real opt-out exists:email.js:102-103footer links/account/#delivery; with default-off delivery, low compliance risk at current volume.
Proposed queue.md diff
- [engineering, P1, NEW 2026-09-08] Daily-playset zero-row miss ... Live-verified still unfixed ...
+ ~~[engineering, P1] Daily-playset zero-row miss~~ DONE — fixed PR #133 (4315ace, merged ab5b741 2026-09-09), hardened further by a8ddc59 + 289a11f. Retire this line.
+ [engineering, S, NEW 2026-09-15] Wire email-plan.js's 100/day cap into `report:email` as an automated threshold check (not manual-only), before household count makes it bind.
Decisions needed
- Read/greenlight [[2026-09-02-retention-nudges-plan]] (13 days idle) — RN-D1 (opt-in timing) and RN-D3 (weekly cap: 3/household/week, quiet 20:00-07:00) still open per the plan's own text.
- Is a day-0 welcome email in scope? Not covered by N1-N5, which all assume an existing plan.
- Open Twilio for outbound SMS, or stay email-only — standing open item,
escalations.md#8.
Related
[[2026-08-31-studio-charter]] · [[2026-09-02-retention-nudges-plan]] · [[2026-09-08-lifecycle-email-sms]] · [[2026-09-11-unit-economics-cost-basis]]