docs(quick-002): complete duplicate notification fix task
Tasks completed: 1/1 - Add post meta guard to prevent duplicate mentor notifications SUMMARY: .planning/quick/002-fix-duplicate-mentor-notifications-on-jo/002-SUMMARY.md
This commit is contained in:
@@ -116,7 +116,7 @@ All 7 phases finished. All identified UX/notification issues resolved. System is
|
|||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-01-29
|
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
|
Resume file: None
|
||||||
|
|
||||||
**Project Status:** ✅ COMPLETE - All 7 phases finished, all polish items complete, system ready for production deployment
|
**Project Status:** ✅ COMPLETE - All 7 phases finished, all polish items complete, system ready for production deployment
|
||||||
|
|||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user