01-projects/printables-product/reviews

lifecycle email sms

·review-and-plan
printablesscribble-workslifecycleemailsms

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)

  1. 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 touch nudges anywhere.

  2. queue.md:205 is stale — the zero-row-miss bug is fixed. Pull request (PR) #133 (commit 4315ace, merged ab5b741, 2026-09-09 20:52 Eastern Time (ET), after that day's classifier block in escalations.md:202-203) added a try/catch in runTick around its call to deliverHousehold that always writes a failed row on throw. Commit a8ddc59 (09-10) then restructured deliverHousehold into a claim-checking wrapper and moved that per-household catch out of runTick into the wrapper itself; 289a11f (09-11) replaced the catch's unconditional recordDelivery with the new outbox/recovery pattern (event(), outboxFor()), confirmed live in current deliver.js. queue.md still reads "Live-verified still unfixed."

  3. 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:email is manual-run only.

  4. SMS is inbound-only. No outbound Twilio use outside feedback-intake's inert /sms door.

  5. No List-Unsubscribe header, but a real opt-out exists: email.js:102-103 footer 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

  1. 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.
  2. Is a day-0 welcome email in scope? Not covered by N1-N5, which all assume an existing plan.
  3. 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]]