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
|
||||
|
||||
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
|
||||
|
||||
@@ -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