Unified Notifications & Activity Log, Real-Time WebSocket Delivery, and Mail Status Cleanup
Executive summary
This backend release consolidates notifications into the existing Activity Log, adds real-time delivery of notifications and comments over WebSockets (with email fallback), and enforces mutually-exclusive mail statuses (resolved/unresolved and read/unread). It also refactors the Comments data model and extends commenting to more record types. The change reached the QA environment for testing.
Why this was needed
Notifications previously lived in a separate notifications table that duplicated much of what the Activity Log already tracked, and they were not delivered in real time (email sending was an unimplemented TODO). Separately, mail records could end up tagged with contradictory statuses (both resolved and unresolved, or both read and unread), and the Comments model used legacy column and table structures that no longer matched how modules are referenced. This work unifies the data, makes alerts live, and removes those data-integrity gaps.
Client / user impact
- Users see notifications and new comments update live in the UI through new WebSocket channels, rather than only on refresh.
- Notification emails are now actually sent for relevant events instead of being skipped.
- A single Activity Log feed now backs notifications, giving a consistent history and faster unread queries.
- Mail statuses can no longer be both resolved and unresolved (or read and unread) at once; the most recent choice wins, and existing conflicting data is cleaned up.
- Comments can now be attached to more record types (Folders, Document Types, Organizations, Departments, Contacts).
Technical scope
- Notifications merged into
ActivityLog: migration adds notify/event_type/notification_type/priority/is_internal_only/metainfo/status_id columns, backfills fromnotifications, repointsuser_notificationstoActivityLog.id, drops thenotificationstable, and adds unread-feed indexes. TheNotificationmodel is removed andnotification_service.pyreads/writes Activity Log instead. - Real-time delivery: new WebSocket endpoints
/api/v1/ws/notificationsand/api/v1/ws/comments, anotification_websocket_manager, and a Redis pub/sub subscriber (channelnotifications:events) that relays events to connected clients. Wired into app startup/shutdown inmain.py, gated byENABLE_NOTIFICATION_PUBSUB. - Email notifications: new
send_generic_emailinsendgrid_email_service.py; the notification service now sends emails (previously a no-op TODO). - Mail status exclusivity:
mail_status_validation.pyandmail_user_status_service.pyenforce mutually-exclusive resolved/unresolved and read/unread groups (last choice wins); a data-cleanup migration removes existing conflicts inMails.user_statusesandMail_UserStatus. - Rule engine: status rule actions move from
MailReadStatusLookup.idto the canonicalMails.user_statusesobject{lookup_statuses, custom_statuses}; priority/status actions normalized via a data migration. - Comments refactor:
module_entity_idrenamed tomodule_id, newmodule_item_idcolumn + composite index; obsoleteCommentsModuletable/view dropped; access checks extended to Folders, DocumentTypes, Organization/Department/Contacts; API acceptsmodule_id. - Redis hardening: substantial rework of
async_redis_client.pyandupstash_rest_client.pyto support pub/sub over both TCP and Upstash REST. - Minor: a stray empty file
_was committed, an example helper file was deleted, and AGENTS.md gained internal contributor notes.
Risk & mitigation
Risk is elevated: this includes multiple irreversible data migrations (dropping the notifications and CommentsModule tables, repointing foreign keys, renaming a Comments column, and normalizing rule-action and mail-status data). Several migration downgrade paths are intentionally no-ops, so rollback would not restore original data. Mitigations: migrations are written to be idempotent and use existence checks before each step; the notifications backfill maps legacy IDs before swapping; real-time pub/sub is feature-gated via ENABLE_NOTIFICATION_PUBSUB and wrapped in try/except so failures degrade gracefully rather than blocking startup. A full database backup before applying migrations and verification on QA before any production promotion are strongly recommended.
QA validation focus
- Run all migrations on a production-like QA copy and confirm
notifications/CommentsModuleare dropped cleanly anduser_notificationsrows still resolve to Activity Log entries. - Verify notification list, unread counts, and filters (type, priority, date, read/unread) return correct data after the merge into Activity Log.
- Open the
/api/v1/ws/notificationsand/api/v1/ws/commentsWebSocket channels and confirm live events arrive (and ping/pong keepalive works); test with pub/sub enabled and disabled. - Trigger a notification event and confirm an email is sent to the correct recipients.
- Apply contradictory statuses to a mail (resolved+unresolved, read+unread) and confirm only the last choice persists; spot-check that the cleanup migration resolved pre-existing conflicts.
- Run priority and status assignment rules end-to-end and confirm the mail's priority and
user_statusesupdate as expected. - Add and fetch comments on the newly supported types (Folders, Document Types, Organizations, Departments, Contacts) using
module_id. - Confirm the stray empty
_file does not ship in production artifacts.