Bizcom Portal — Notification Routing Audit
Author: Apolinario Ratio
Date: 2026-03-25
Status: implementation-ready
Target branch: bizcom/notification-routing-audit
Summary
Outbound email logs from the last 7 days indicate two likely defects in the Bizcom notification subsystem:
- Recipient targeting is too broad for some notification classes, especially feedback and digest/account-update emails.
- Duplicate or repeated sends are likely occurring for the same subject-recipient combinations over time.
This is a user-trust, privacy, and operational-noise issue. The routing logic should be made record-scoped and default-deny, and the send pipeline should be made idempotent and auditable.
Evidence reviewed
Source file analyzed from NAS:
nas:agents-shared/ventures/bizcom/Bizcom Portal/emails-sent-1774418369547.csv
Observed patterns:
| Finding | Evidence | Implication |
|---|---|---|
| Feedback notification sent to multiple recipients | New Feedback: other from Alex Gari went to 7 recipients; same for Angelyn Porta | feedback routing is broader than necessary |
| Digest/account-update subjects sent to many recipients | [BIZCOM] 4 updates on your account reached 13 recipients | digest grouping/routing likely not partitioned tightly enough |
| Same subject-recipient pair repeated many times | e.g. [BIZCOM] 3 updates on your account sent 46 times to one recipient over the 7-day window | dedupe / send-state / replay issue likely exists |
| Some alerts are single-recipient | overdue alerts for specific tasks went to one recipient only | not all notification paths are broken; issue appears concentrated in digest/feedback flows |
Problem statement
The notification subsystem appears to be mixing two failure modes:
1. Over-broad routing
Some notifications are being sent to role groups or broad recipient sets instead of only the users directly relevant to the underlying record or event.
2. Duplicate delivery
Some notifications appear to be re-sent repeatedly to the same recipient for the same subject/event class over time, suggesting weak idempotency or incorrect digest rebuild behavior.
Desired behavior
Routing principle
Default deny, then include explicit recipients only.
The system must derive recipients from the record/event itself, not from a broad role group unless that is explicitly the intended behavior.
Allowed routing by notification type
| Notification type | Intended recipients |
|---|---|
| Client-specific update | authorized contact(s) for that client only |
| Staff task/activity update | assigned staff only |
| Admin oversight event | admins only |
| Feedback submission | configured reviewers / admins only |
| Internal workflow event | only users attached to that workflow record |
Failure rule
If the system cannot confidently derive recipients:
- do not broadcast
- do not silently fallback to everyone
- log a routing-resolution failure
- optionally notify admins as a system exception only
Engineering goals
- Targeted delivery — only relevant users receive a given event.
- Idempotency — the same event-recipient pair should not be re-sent unintentionally.
- Auditability — every outbound email can be traced to an event, a resolver, and a recipient reason.
- Safe fallback behavior — unresolved routing should degrade safely, not noisily.
Likely root-cause areas to inspect
A. Recipient selection logic
Check whether current logic:
- expands too early to all admins/staff
- uses role buckets where record-scoped recipients should be used
- merges unrelated recipients into a shared digest/send list
- treats "account updates" as org-global rather than recipient-partitioned
B. Digest builder / aggregation
Check whether digest generation is incorrectly grouped by:
- organization instead of recipient
- client instead of authorized contact
- event class without recipient partitioning
C. Queue replay / sent-state handling
Check whether repeated sends happen because:
- sent status is not persisted reliably
- retries lack idempotency protection
- scheduler re-collects previously included events
- digest windowing allows old events to be rebuilt into new sends repeatedly
D. Feedback notifications
Check whether feedback submission currently notifies a broad internal audience rather than an explicit configured reviewer set.
Recommended implementation approach
Phase 1 — Trace current behavior
Add structured internal logging around outbound notification resolution:
- notification type
- source record id / event id
- resolved recipients
- why each recipient was included
- dedupe key / idempotency key
- send vs skip decision
Goal: confirm the exact failure point before invasive changes.
Phase 2 — Isolate recipient resolution
Refactor recipient selection into explicit resolver functions/modules per notification type.
Requirements:
- no broad fallback by default
- record-scoped resolution where applicable
- explicit admin-only and reviewer-only paths
- deterministic output for the same input event
Phase 3 — Add idempotency protection
Introduce stable dedupe keys such as:
<notification_type>:<source_event_id>:<recipient_id>:<window>
Use them to prevent:
- duplicate queue processing
- digest rebuild duplicates
- retry duplication after partial failure
Phase 4 — Add observability and tests
Create tests and/or fixtures covering:
- one client, one staff, one admin
- multiple clients in the same org
- repeated scheduler runs
- retry after transient provider failure
- feedback notification reviewer scoping
Acceptance criteria
Routing correctness
- Feedback submission notifies only configured reviewers/admins.
- Client update notifications reach only that client's allowed contacts.
- Staff task notifications reach only assigned staff.
- Admin-only events do not reach clients or unrelated staff.
Deduplication correctness
- Re-running the same queue job does not resend the same event-recipient pair.
- Rebuilding a digest does not duplicate already-sent items.
- Retries do not create multiple successful sends for the same recipient/event.
Auditability
For any sent email, engineering/admin should be able to answer:
- what event triggered it
- why this recipient was included
- whether it was first-send or retry
- which dedupe key applied
Proposed testing plan for staging
- Seed one client with one authorized contact.
- Seed one assigned staff user and one admin.
- Trigger each relevant notification class:
- feedback submission
- task/activity update
- client-facing update
- admin-only event
- Verify exact recipients.
- Re-run scheduler / queue job.
- Verify no duplicate sends.
- Inspect logs for recipient resolution trace and dedupe keys.
Suggested implementation ticket title
Fix over-broad notification routing and duplicate email sends
Codex task framing
Use this document as the implementation source of truth.
Primary objective:
- audit current recipient resolution and dedupe behavior
- implement targeted routing and idempotency protections
- preserve existing intended single-recipient alert behavior
- keep changes staging-safe and testable tomorrow
First-pass implementation notes
Current branch implementation now applies these staging-safe controls:
- Feedback submission emails resolve to explicit reviewers: active
admin/superadminusers plus staff explicitly grantedfeedback.manage. - Feedback email routing now default-denies when no reviewer recipients can be resolved, and logs the routing failure instead of broadcasting.
- Queue insertion now supports optional dedupe keys and logs duplicate-skip decisions.
- Queue insertion also suppresses exact duplicate pending/processing rows for the same recipient + content within the dedupe window.
- Client-facing task/compliance digest contributors now enqueue with recipient-scoped dedupe keys over an 8-hour window:
task_waitingtask_completedapproval_neededdocuments_neededdocument_reviewed
Remaining gaps for a later pass:
- Other notification producers still rely on their existing caller-side guards and have not yet been moved to dedicated resolver modules.
- Queue idempotency is currently opt-in for sent-row replay prevention; broader event-id based coverage would require standardizing dedupe keys across all producers.
