Propose: notifications
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>
This commit is contained in:
parent
937bde3b0b
commit
95ac79b093
8 changed files with 591 additions and 0 deletions
16
openspec/changes/notifications/specs/activity-feed/spec.md
Normal file
16
openspec/changes/notifications/specs/activity-feed/spec.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
## 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue