<!-- AUTO-GENERATED by scripts/wi-claude-rules-sync.sh — do not edit. -->
<!-- bundle: 90-mail-cron | sources: 1 .mdc files -->
---
name: mail-cron
description: WI mail queue and cron diagnosis
---

<!-- source: .cursor/rules/wi-mail-cron-diagnosis.mdc -->

# WI Mail Cron / Reminder Diagnosis

## Agent default (autonomous when deploy/access exists)

When the user reports **missing reminder emails** or asks about WI cron:

1. **Do not** treat `DISABLE_WP_CRON` alone as "cron broken". Many hosts use **external cron** (Cloudways Cron Optimizer, system crontab) and set `DISABLE_WP_CRON` true while `webinarignition_cron_hook` still runs every ~5 minutes.
2. **Do** check **last WI cron execution**:
   - Option `wi_mail_cron_last_run`: `iso_utc`, `gap_sec`, `ts`
   - Preflight row `mail_cron` (green/yellow/red)
   - Healthy: last run **< 15 min** ago, `gap_sec` typically **~300** (five_minutes schedule)
3. **Then** check reminder scheduling: notification on, `email_notiff_status_*` not `sent`, H4-equivalent = `schedule_email_live.php` trigger window (`notif_ts` vs `now_ts`).
4. **Then** mail transport (SMTP plugin, bounce) — confirmation mail working does **not** prove reminders; confirmation is immediate on register.

## User does not need to run these checks

If staging FTP/WP access is available (e.g. `.env.livestream-staging.local`, `.cursor-wi-screenshots/env.staging.felix`, Cloudways SSH): agent runs preflight/cron check and reports `wi_mail_cron_last_run` — **do not** ask Tobias to verify routine cron health.

## Code references

- `wi_cron_record_last_run()` — `inc/wi-mail-cron-status.php`, called from `webinarignition_cron_exec()`
- `wi_preflight_item_mail_cron()` — Preflight UI

## Known reminder bugs (fixed 4.10.97)

- **45-minute window:** `schedule_email_live.php` only fired reminders if `notif_ts >= now - 2700`. After rescheduling, overdue `queued` reminders never sent. **Fix:** catch-up for `queued` before `webinar_event_start_gmt`.
- **Notification #1 never cron'd:** loop was `for ( $num = 5; $num > 1; $num-- )` — **day-before (slot 1) was skipped**. **Fix:** `$num >= 1`.
- **Day-before on webinar day:** slot 1 is auto-marked `sent` without mail when the webinar starts **today** (local campaign TZ) — expected, not a cron failure.

