Closes the social loop opened by social-feed: a Pending follower has no way to know their request was approved, and a follower has no signal at all that someone they follow just posted. Adds a notifications surface for three v1 event types — follow_request_approved, follow_received, activity_published — plus a /notifications page, navbar unread count, and mark-as-read controls. Capabilities: - New: notifications (table, page, badge, generation hooks) - Modified: social-follows (approve + auto-accept emit) - Modified: activity-feed (public create fans out) - Modified: journal-landing (nav entry alongside follow-requests) Design picks: - Fan-out-on-write for activity_published (1:N) so /notifications is a flat single-table query and "mark read" composes trivially. 1:1 events insert directly. Cost ceiling documented at 10k followers × 50 activities/day = 500k/day, still trivial; revisit only if hot. - Single notifications table with loose subject_id (no per-type FK); renderer dereferences by type. Mastodon-style. - Two distinct nav entries (Follow requests + Notifications). Pending is "act on this", notifications is "this happened" — different semantics, kept separate. - Loader-driven unread count (no real-time channel). Real-time is deferred. - 90-day retention for read rows; unread kept indefinitely. Out of scope: per-type mute preferences, email/push, real-time, notifications about routes/replies/mentions, federated notifications. Each tracked as follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1.1 KiB
1.1 KiB
ADDED Requirements
Requirement: Public activity creation fans out notifications
Creating an activity with visibility = 'public' SHALL enqueue a fan-out job that inserts an activity_published notification for every accepted follower of the activity owner. The fan-out SHALL run asynchronously so the activity-creation request returns immediately.
Scenario: Public activity fans out
- WHEN a user with N accepted followers creates an activity with
visibility = 'public' - THEN a pg-boss job is enqueued, and on completion N notifications exist with
type = 'activity_published',recipient_user_id∈ accepted-followers,actor_user_id= activity owner,subject_id= activity id
Scenario: Private or unlisted activity does not fan out
- WHEN a user creates an activity with
visibility = 'private'or'unlisted' - THEN no fan-out job is enqueued and no notifications are created
Scenario: No accepted followers means no notifications
- WHEN a user with zero accepted followers creates a public activity
- THEN the fan-out job runs and inserts zero rows