diff --git a/.planning/STATE.md b/.planning/STATE.md index bab2a59..43d8032 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -116,7 +116,7 @@ All 7 phases finished. All identified UX/notification issues resolved. System is ## Session Continuity Last session: 2026-01-29 -Stopped at: Completed Quick Task 001 (UX & Notification Polish) - all 4 deferred issues resolved +Stopped at: Completed Quick Task 002 (Fix Duplicate Mentor Notifications) - bug fix for republish notifications Resume file: None **Project Status:** ✅ COMPLETE - All 7 phases finished, all polish items complete, system ready for production deployment diff --git a/.planning/quick/002-fix-duplicate-mentor-notifications-on-jo/002-SUMMARY.md b/.planning/quick/002-fix-duplicate-mentor-notifications-on-jo/002-SUMMARY.md new file mode 100644 index 0000000..10c6604 --- /dev/null +++ b/.planning/quick/002-fix-duplicate-mentor-notifications-on-jo/002-SUMMARY.md @@ -0,0 +1,163 @@ +--- +phase: quick-002 +plan: 01 +subsystem: notifications +tags: [mentor-notifications, duplicate-prevention, post-meta, bug-fix] +requires: [05-01, 05-03] +provides: + - Duplicate notification prevention using post meta + - One-time mentor notification guarantee per job +affects: [] +tech-stack: + added: [] + patterns: + - "Post meta flags for one-time event tracking" + - "Idempotent notification scheduling" +key-files: + created: [] + modified: + - includes/class-notifications.php +decisions: + - key: "Use post meta `_ddhh_mentors_notified` for duplicate prevention" + context: "Need to track whether mentors have been notified for a specific job" + options: ["Post meta flag", "Custom database table", "Transient cache"] + chosen: "Post meta flag" + rationale: "Simple, reliable, persists forever, underscore-prefix hides from UI" + - key: "Set meta flag after scheduling, not after emails sent" + context: "When to mark job as notified" + options: ["After scheduling batch", "After all emails sent", "Before scheduling"] + chosen: "After scheduling batch" + rationale: "Scheduling is synchronous and reliable; email sending is async and could fail individually" +metrics: + duration: 2 + completed: 2026-01-29 +--- + +# Quick Task 002: Fix Duplicate Mentor Notifications + +**One-liner:** Post meta guard prevents mentor re-notification on job republish after edits + +## What Was Built + +Fixed a bug where mentors received duplicate "new job" notifications when a job was edited and republished. + +**The Problem:** +The job lifecycle includes a status reset to `pending` after provider edits (decision 03-02). This means: +1. Provider submits → pending +2. Admin publishes → pending to publish → mentors notified ✓ +3. Provider edits → status reset to pending +4. Admin republishes → pending to publish → mentors notified AGAIN ✗ + +The existing guard `if ( 'publish' === $old_status )` only prevented publish→publish transitions, not pending→publish after edits. + +**The Solution:** +Added a post meta flag `_ddhh_mentors_notified` that: +- Is checked before scheduling notifications (early return if already set) +- Is set after successful batch scheduling +- Persists for the lifetime of the job post +- Uses underscore prefix to hide from WordPress custom fields UI + +## Technical Implementation + +**Modified:** `includes/class-notifications.php` + +1. **Pre-scheduling guard** (after line 439): + - `get_post_meta( $post->ID, '_ddhh_mentors_notified', true )` + - If value is `'1'`, log skip message and return early + - Prevents duplicate scheduling entirely + +2. **Post-scheduling flag** (after line 469): + - `update_post_meta( $post->ID, '_ddhh_mentors_notified', '1' )` + - Sets flag after `schedule_mentor_notification_batch()` succeeds + - Ensures future republish attempts skip notification + +**Why after scheduling, not after sending?** +- Scheduling is synchronous and happens immediately +- Email sending is async (Action Scheduler batches) +- Individual emails might fail, but we still don't want re-notifications +- The intent to notify has been recorded, which is what matters + +## Verification + +**Syntax Check:** +```bash +php -l includes/class-notifications.php +# No syntax errors detected +``` + +**Code Review:** +- ✅ `get_post_meta` check exists before mentor query +- ✅ `update_post_meta` exists after scheduling +- ✅ No other notification methods modified +- ✅ Existing publish→publish guard preserved +- ✅ Underscore-prefixed meta key (hidden from UI) + +## Job Lifecycle Flow (After Fix) + +1. **Initial submission** (pending): + - No notification (job not published yet) + - `_ddhh_mentors_notified` = not set + +2. **First publish** (pending → publish): + - ✅ Mentors notified + - `_ddhh_mentors_notified` = '1' + +3. **Provider edits** (publish → pending): + - No notification (deactivation) + - `_ddhh_mentors_notified` = still '1' + +4. **Republish after edit** (pending → publish): + - ❌ Mentors NOT notified (meta flag prevents it) + - `_ddhh_mentors_notified` = still '1' + +5. **Any future republish**: + - ❌ Mentors NOT notified (meta flag persists) + +## Impact + +**Fixed:** +- Mentors no longer receive duplicate notifications for the same job +- Reduces notification fatigue and confusion +- Maintains trust in the notification system + +**Unchanged:** +- Admin notifications for edits (still sent, as intended) +- First-time publish notifications (still sent) +- Publish→publish guard (still prevents updates to published jobs) +- Mentor opt-in/opt-out functionality + +**Edge Cases Handled:** +- Job edited multiple times: Only first publish notifies +- Job unpublished and republished: Only first publish notifies +- Job trashed and restored: Meta persists, no re-notification + +## Deviations from Plan + +None - plan executed exactly as written. + +## Testing Notes + +**To verify in production:** + +1. Create test job, submit as provider → status: pending +2. Admin publishes → Check Action Scheduler for mentor notification batches +3. Provider edits job → status: pending +4. Admin republishes → Check Action Scheduler - should see NO new batches +5. Check error_log for: "Skipping mentor notification for job #X - mentors already notified on initial publish" + +**Manual database check:** +```sql +SELECT post_id, meta_value +FROM wp_postmeta +WHERE meta_key = '_ddhh_mentors_notified'; +``` + +Should show '1' for all published jobs that have triggered notifications. + +## Next Phase Readiness + +**Blockers:** None +**Dependencies satisfied:** Builds on existing notification system (05-01, 05-03) +**Follow-up needed:** None - fix is complete and self-contained + +This was a targeted bug fix with no architectural implications. The notification system is now fully idempotent - each job triggers exactly one mentor notification batch in its lifetime.