# WhatsApp Nudge Testing — Batch 2 (R8 / J5 / R4)

**Created:** 2026-06-09
**Branch:** `feat/wa-nudge-batch2-r8-j5-r4-j2-2026-06-08`
**Services:**
- [`app/Services/RecruiterStatusWeeklyNudgeService.php`](../app/Services/RecruiterStatusWeeklyNudgeService.php) — R8
- [`app/Services/JobseekerWeeklyJobAlertsNudgeService.php`](../app/Services/JobseekerWeeklyJobAlertsNudgeService.php) — J5
- [`app/Services/RecruiterNoJobPostedNudgeService.php`](../app/Services/RecruiterNoJobPostedNudgeService.php) — R4

**Crons:** `cron/recruiter-status-weekly.php`, `cron/jobseeker-weekly-job-alerts.php`, `cron/recruiter-no-job-posted.php`

For exhaustive per-nudge details (trigger logic, params, message bodies), see [`whatsapp-nudges-comprehensive-inventory.md`](whatsapp-nudges-comprehensive-inventory.md).

---

## Pre-deploy dry-run (validated 2026-06-09 against UAT RDS)

```
RecruiterStatusWeeklyNudgeService::runWeeklySweep(true)
  → candidates=0, sent_whatsapp=0, failed_whatsapp=0, skipped_already=0
  (UAT has no shortlists in the last 7 days)

JobseekerWeeklyJobAlertsNudgeService::runWeeklySweep(true)
  → candidates=412, sent_whatsapp=412, failed_whatsapp=0, skipped_already=0
  (412 active UAT jobseekers have at least one automatch)

RecruiterNoJobPostedNudgeService::runDailySweep(true)
  → candidates=136, sent_whatsapp=136, failed_whatsapp=0, skipped_already=0
  (136 UAT recruiters signed up >7d ago who never posted a job)
```

All three returned valid JSON with the expected counter fields. No SQL parse errors, no missing columns, no PHP fatals.

---

## Post-SCP smoke test (run on UAT EC2)

```bash
ssh -i /Users/apple/Projects/kaam-ec2/kaabil-cc-key-pair.pem ubuntu@65.1.107.228

cd /var/www/uat.kaabil.cc/recruiter
sudo -u www-data php cron/recruiter-status-weekly.php  --test
sudo -u www-data php cron/jobseeker-weekly-job-alerts.php --test
sudo -u www-data php cron/recruiter-no-job-posted.php --test
```

Expected output for each: `[RESULT] candidates=N, sent_whatsapp=0, ...` with appropriate N (matching the dry-run above, modulo any UAT data changes in the meantime).

---

## End-to-end fire test (after Azmarq template verification)

Even though templates are Meta-approved per the 2026-06-08 Azmarq Templates.xlsx, until we fire a real cron without `--test` we don't know whether Azmarq actually delivers. Once the cron is scheduled on UAT crontab:

1. Wait for the next scheduled run (Monday 9/10 AM IST for J5/R8, daily 9 AM for R4)
2. Check the `wa_nudge_log` table:
   ```sql
   SELECT nudge_key, channel, status, COUNT(*) AS n, MAX(sent_at) AS last
   FROM wa_nudge_log
   WHERE nudge_key IN ('r8_recruiter_status_weekly','j5_jobseeker_weekly_job_alerts','r4_recruiter_no_job_posted')
   GROUP BY 1, 2, 3
   ORDER BY 1, 2, 3;
   ```
3. Expect: rows with `status='sent'` and `channel='whatsapp'`. Cross-check with a real mobile you control by including yourself in the cohort.

---

## Idempotency tests

R8 (lookback 144h): re-run `php cron/recruiter-status-weekly.php` immediately after a live run → all candidates show `skipped_already++`, no new `wa_nudge_log` rows.

J5 (lookback 144h): same as R8.

R4 (lookback `null` = forever): re-run → 100% of the candidate set shows `skipped_already++`. The lookback being null is intentional — R4 is "fire once ever".

---

## Pass criteria summary

A batch-2 nudge is considered live + verified when:
- ✅ Dry-run on UAT returns valid JSON with expected candidate count
- ✅ SCP deploy completes; files owned by www-data
- ✅ Smoke test on UAT mirrors dry-run output
- ✅ Idempotency check shows skipped_already on re-run
- ✅ Cron schedule added to UAT crontab
- ✅ One real cycle has fired with `status='sent'` rows in wa_nudge_log
- ✅ A test recipient (real mobile under our control) actually received the WhatsApp
