Skip to content

docs: design notification system#1135

Draft
mistercrunch wants to merge 3 commits into
mainfrom
design-notification-system
Draft

docs: design notification system#1135
mistercrunch wants to merge 3 commits into
mainfrom
design-notification-system

Conversation

@mistercrunch
Copy link
Copy Markdown
Member

Summary

Design doc (no code) for a centralized notification system in Agor: a bell + panel on the navbar, a durable per-user inbox, and a clear toast-vs-notification rule. Lives at docs/notification-system-design.md.

Key positions

  • v1 taxonomy: three types only — mention, session_returned, global_admin. Worktree-created / session-created / environment / schedule events are explicitly out (too noisy or covered elsewhere).
  • Subscription model: interaction-based, not visibility-based. You're auto-subscribed to a session if you created or prompted it (or were @-mentioned in a comment on it). Archive = unsub. Refines the brief's "every session until archived" to handle the multi-user spam path.
  • Read vs dismissed: separate columns. Click marks read, but does not dismiss. Mark-all-read affordance ships.
  • Collapse: session_returned collapses per (user, session, type); mentions and broadcasts don't collapse.
  • Link target: source_session_id (where you go); source_task_id is metadata.
  • Delivery: piggybacks on the existing per-user socket room (app.io.to(userRoomName(userId)) at socketio.ts:193,379). No new auth/channel infra.
  • Navbar message ticker: cut from v1. Distraction-to-utility ratio is poor; revisit as a separate flag-gated experiment if Max wants it.
  • Sticky admin banner: lives in the currently-empty navbar center slot. One slot, dismissible, expirable.
  • Admin authoring: new SettingsModal → Announcements tab, admin-role-gated.

Things flagged for Max's call (in §10 of the doc)

  • Q-A: interaction-based subscription vs literal "every session you can see"
  • Q-B: explicit data.mentions[] vs current substring detection for mention notifs
  • Q-C: assistant-vs-worktree session visual differentiation deferred until an is_assistant marker exists
  • Q-D: retention sweep (recommended for v1.5)
  • Q-E: session_returned should also fire on FAILED (recommended yes)

What's in the doc

  • Inventory of existing toast / push / archive / mention infra with file:line citations
  • Schema sketch (sqlite + postgres parallel) with indexes
  • ASCII mocks for bell, panel, three card variants, banner, empty state
  • Subscription model with the producer-side recipient query
  • Admin authoring flow + compose modal sketch
  • Phased plan: v1 (core inbox) → v1.5 (mute, snooze, retention) → v2 (email/push/Slack)
  • Rough effort estimate: ~7–8 eng-days for one contributor

Test plan

  • Max reads the doc and leaves inline comments
  • Resolve Q-A through Q-E
  • Decide whether to ship before or after the Phase-4 mention work in board-comments
  • Once aligned, open implementation PR(s) per the v1 phase in §11

🤖 Generated with Claude Code

mistercrunch and others added 3 commits May 8, 2026 16:55
Add design doc for a centralized notification system: bell + panel on
the navbar, durable per-user inbox, three notification types in v1
(mention, session_returned, global_admin), and a toast-vs-notification
rule. Takes positions on the open questions in the brief — collapse
session-returned per (user, session, type), interaction-based default
subscription, click marks read but doesn't dismiss, navbar message
ticker cut from v1, sticky admin banner in the navbar center slot.

No code yet — design + open questions + phased plan + effort estimate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… subs

Per Max's review:

- Drop `dismissed_at` column. Dismiss = hard-delete the row. No
  soft-delete state, no use case for keeping dismissed rows. Simplifies
  schema and queries.
- Add §6.3 explicitly contrasting three subscription layers: implicit
  (v1, derived from interactions), explicit mute (v1.5, tiny opt-out
  table), full subscription primitive (v2 or never).
- Fix §10 row 8 — earlier draft incorrectly said `data.muted_at` at the
  session level, which would mute it for everyone. Mute is per-(user,
  session); needs the new table, not a session field.
- Add archive-time cleanup: when a session/worktree is archived, sweep
  its notification rows (and v1.5 mute rows) so users don't see stale
  invitations to retired sessions.
- Add latest-task-only note in §5.2: collapsed `session_returned`
  surfaces only the most recent completion; older ones are not logged
  in the panel. The session detail panel is the log.
- Simplify indexes (drop dismissed_at column from primary read index).
- Narrow v1.5 retention scope to "aged read rows" (dismissed are gone
  immediately now).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…A..Q-G

Generalize mute table and lock in remaining open questions.

Schema/model changes:

- Rename `session_notification_mutes` → `notification_mutes` with a
  polymorphic `(scope_type, scope_id)` key. Producer subtracts users
  who muted any scope an event falls under (session, comment_thread,
  worktree, board). Catches comment threads, which the previous
  session-only design missed.
- Add §4.1 making explicit that `session_returned` fires per task
  completion (not per message), and the preview is the last assistant
  message of the completed task — same fetch logic as the parent-
  callback design. Individual messages never trigger notifications.
- §3.6: client suppresses the toast when source page = current route
  (notification still created server-side; just no double-signal).

Decisions made (all moved from "deferred" to "decided" in §10):

- Q-A: Interaction-based subscription. Decided.
- Q-B: Explicit data.mentions[] preferred; substring acceptable
  stop-gap. Decided.
- Q-C: Assistant-session variant deferred until is_assistant marker
  exists. Decided.
- Q-D: Retention sweep promoted to v1 (90-day aged-read delete).
  Decided.
- Q-E: Fire on FAILED. Decided.
- Q-F: Context-aware toast suppression. Decided.
- Q-G: Skip self-mentions (recipient ≠ author). Decided.

Effort estimate bumped to ~8d (was ~7-8d) to absorb retention sweep
and toast-suppression line items.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant