# Duplicate SMS Investigation

**Date:** 2025-01-29
**Priority:** HIGH - URGENT
**Status:** ROOT CAUSE IDENTIFIED

## Summary

Leads are receiving duplicate SMS messages because they can be enrolled in **multiple sequences simultaneously**. The deduplication check only prevents re-enrollment in the *same* sequence, not in *different* sequences.

---

## Evidence: Paddy Fleming Ivers Case

**Lead ID:** `4ec855fe-bd3a-4a39-8650-38a21428a834`
**Phone:** `+61400464446`

### Messages Received (3 total):

| Time | Message | Source |
|------|---------|--------|
| `03:56:23.060` | "Hey Paddy, Rocky from RateRight. We supply quality tradies..." | Sequence A (`d0f0f1c5`) |
| `03:56:23.777` | "Hey Paddy, how'd that hire go?..." | Sequence B (`a5ed489e`) |
| `05:08:44.638` | "Hey Paddy, just checking in..." | Manual send |

**Key Finding:** Messages 1 and 2 were sent **717 milliseconds apart** from **two different sequences**.

### Enrollment Evidence:

- **Enrollment 1:** `9c129a47-e30e-47f8-a71a-4ec9700cfc1f` → Sequence `d0f0f1c5-7dc7-4fbd-a550-e0d3254d5d67`
- **Enrollment 2:** `82292bfe-aeb2-44df-86dd-3b606ffe6ddb` → Sequence `a5ed489e-df15-42cc-af81-0f3e5a08a0ae`

Both enrollments were created at nearly the same time, and both fired their Step 1 messages simultaneously.

---

## Root Cause Analysis

### Location: `src/routes/sequences.js` (lines 148-162)

```javascript
// Check for existing active enrollments (prevent duplicates within 30 days)
const { data: existingEnrollments } = await db
  .from('sequence_enrollments')
  .select('lead_id')
  .eq('sequence_id', sequenceId)  // ← BUG: Only checks SAME sequence
  .in('lead_id', leadsToEnroll)
  .or(`status.eq.active,enrolled_at.gte.${thirtyDaysAgo.toISOString()}`);
```

### The Bug:
The deduplication query only checks if a lead is enrolled in **the same sequence**. It does **NOT** check if the lead is already enrolled in **any other active sequence**.

### How It Happens:
1. User bulk-enrolls leads in Sequence A
2. User bulk-enrolls leads in Sequence B  
3. Both enrollments succeed (different sequence IDs)
4. Sequence processor runs, finds both enrollments are "due"
5. Both sequences send their Step 1 messages simultaneously
6. Lead receives 2+ identical/similar SMS within seconds

### Contributing Factors:
1. **No global enrollment mutex** - A lead can be in unlimited active sequences
2. **No send throttle** - The sequence processor doesn't check recent outbound SMS before sending
3. **No cooldown period** - After sending, there's no delay before another sequence can send to same lead

---

## Impact Assessment

### Leads Affected:
- **Confirmed:** At least 1 (Paddy Fleming Ivers)
- **Suspected:** More leads enrolled in multiple sequences on 2025-01-29

### Statistics (Last 7 Days):
- Total SMS Sent: 25
- SMS on Jan 29: 24 (spike indicates batch enrollment event)
- Responses: 6

### Business Impact:
- **Lead trust erosion** - Receiving duplicate messages looks spammy
- **Potential blocks** - Leads may block the number
- **Carrier spam flagging** - Risk of Twilio number being flagged
- **Reputation damage** - Unprofessional impression

---

## When This Started

The bug has always existed in the sequence enrollment logic. The trigger was likely:
- A recent bulk enrollment action that enrolled leads in multiple sequences
- This appears to have happened on **2025-01-29** around **03:56 UTC** based on message timestamps

---

## Additional SMS Sending Paths (Not Affected)

Verified these paths do NOT cause duplicates:
- ✅ Manual SMS (`/api/sms/send`) - Single message, no automation
- ✅ Scheduled SMS (`scheduledSms.js`) - Processes scheduled messages only
- ✅ Auto-reply in webhooks - Has 5-minute deduplication check
- ✅ Playbook sends - Excludes leads contacted in last hour

---

## Immediate Actions Needed

1. **STOP** any bulk sequence enrollments until fix is deployed
2. **AUDIT** sequence_enrollments table for leads in multiple active sequences
3. **PAUSE** duplicate enrollments to prevent further damage
4. **IMPLEMENT** fix (see `sms-duplicate-fix-plan.md`)

---

## Files Involved

| File | Role |
|------|------|
| `src/routes/sequences.js` | Enrollment endpoint (BUG HERE) |
| `src/jobs/sequenceProcessor.js` | Processes sequence steps |
| `src/routes/sms.js` | Direct SMS sending |
| `src/routes/webhooks.js` | Has auto-reply dedup (good pattern) |
