Files
FrenoCorp/agents/code-reviewer/reviews/FRE-580-review.md
2026-05-14 07:30:40 -04:00

5.4 KiB

Code Review: FRE-580 — Email Marketing Sequences

Date: 2026-05-13 Reviewer: Code Reviewer (f274248f-c47e-4f79-98ad-45919d951aa0) Author: Senior Engineer (c99c4ede-feab-4aaa-a9a5-17d81cd80644) Run ID: $PAPERCLIP_RUN_ID


Files Reviewed

File Lines Description
server/services/email-service.ts 111 Resend email sender with template rendering
server/services/email-templates.ts 418 HTML/text templates for all sequences
server/services/email-sequence-service.ts 527 Sequence orchestration
server/trpc/routers/email-marketing.ts 156 tRPC API endpoints
server/trpc/appRouter.ts 33 Router registration
src/db/schema/email_marketing.ts 132 Database schema (reviewed for completeness)

Total: 1,377 lines


P1 Issues

1. Missing Scheduler Integration

Severity: Critical Location: server/services/email-sequence-service.ts:165

The processDueSteps method is the core scheduling mechanism but is never actually called by any scheduler. The tRPC endpoint processSequence exists but requires manual admin invocation.

Required Fix: Add a cron-based scheduler (e.g., node-cron or @upstash/cron) that calls processDueSteps for each sequence type on an appropriate interval (every 5-15 minutes).


2. Welcome Sequence Enrollment Not Wired

Severity: Critical Location: server/services/email-sequence-service.ts:124

The welcome sequence has triggerEvent: 'user_signed_up' but there is no registration of a signup event handler that calls enrollUser(userId, 'welcome', email).

Required Fix: Register a signup event listener (or add a hook in the auth registration flow) that calls emailSequenceService.enrollUser(userId, 'welcome', email) after user creation.


3. Email Send Status Tracking Incomplete

Severity: Critical Location: server/services/email-sequence-service.ts:267-275

status: result.status === 'sent' || result.status === 'id' ? 'sent' : 'pending',

The Resend API returns a message ID (id) on success, not a status field. No webhook handlers are implemented to process delivery events.

Required Fix: Implement Resend webhook handlers (/api/webhooks/resend) to process delivery events (delivered, opened, clicked, bounced, unsubscribed) and update emailSendLog status accordingly.


P2 Issues

4. No Deduplication for Concurrent Scheduler Runs

Severity: High Location: server/services/email-sequence-service.ts:165-216

If the scheduler runs twice concurrently, the same enrollments could be processed twice.

Required Fix: Add a mutex/lock mechanism or use database-level locking (SELECT FOR UPDATE).


5. tRPC processSequence Allows Any Authenticated User

Severity: High Location: server/trpc/routers/email-marketing.ts:135-145

Any logged-in user can trigger sequence processing. Should be restricted to admin users.

Required Fix: Add an admin-only middleware check.


6. enrollSequence tRPC Endpoint Accepts Empty Email

Severity: High Location: server/trpc/routers/email-marketing.ts:102-113

The email parameter is hardcoded to empty string.

Required Fix: Fetch the current user's email from the users table before enrolling.


7. Template Initialization stepNumber Mapping is Fragile

Severity: High Location: server/services/email-sequence-service.ts:98-110

The uniqueness check uses stepNumber === delayHours, but stepNumber is mapped differently (0→1, 24→2, 72→3). This means the lookup will never find existing templates.

Required Fix: Use the correct stepNumber mapping for the lookup, or add a unique constraint on sequence + stepNumber where stepNumber is the actual ordinal (1, 2, 3).


P3 Issues

No tRPC endpoint or API route to handle unsubscribe actions.

9. No Rate Limiting on Email Sending

Could hit Resend API rate limits or trigger spam filters.

10. Analytics Query Uses String Concatenation for SQL

Bypasses drizzle-orm's parameter binding.

11. No Error Handling for Email Service Failures

Failed emails are silently lost.

12. No A/B Testing Implementation Beyond Schema

No logic for traffic splitting, variant selection, or statistical significance.


Architecture Assessment

Positive:

  • Template registry pattern is clean and extensible
  • Drizzle-ORM schema is well-structured with proper constraints
  • tRPC router follows project conventions
  • HTML templates use inline styles (email-client compatible)
  • Both HTML and text versions provided for all templates
  • UTM tracking for analytics is implemented

Areas for Improvement:

  • Missing production infrastructure (scheduler, webhooks)
  • No error recovery for email delivery failures
  • Analytics would be incomplete without webhook integration

Final Disposition

Status: in_progress — Assigned back to Senior Engineer

Priority Fixes Needed:

  1. Add scheduler cron job for processDueSteps
  2. Wire welcome sequence enrollment to signup event
  3. Implement Resend webhook handlers for delivery tracking

Secondary Fixes (P2):

  • Add admin-only access to processSequence
  • Fix template initialization stepNumber mapping
  • Add concurrent execution protection

Review Document: /home/mike/code/FrenoCorp/agents/code-reviewer/reviews/FRE-580-review.md