Follow-up from the sj-audit nav work (#89 shipped the Activity tab). The Activity tab today is an honest v1: agent events only (non-terminal sessions from the existing sessions feed), with a "Mentions & reactions are coming soon" note. This issue tracks making it a real notifications hub.
Goal
Activity surfaces, in one place: @mentions of the current user + reactions on their messages, merged with the existing agent events, newest-first, with read/unread state — on both web and mobile.
What exists today
- Reactions are events (
reaction.added / reaction.removed) in the events table; aggregation lives in surface/server/src/events.ts (reactions json_agg ~:589, net-count ~:488). A reactions feed is queryable: reactions whose target message's author = me.
- Mentions: nothing server-side — the app only parses
@agent for spawns. Mentions need a detection strategy.
- No activity/notifications endpoint or client api method exists.
Design fork (decide before building)
Mention detection — write-time index vs read-time scan:
- A) Write-time mentions index (recommended): on message create, parse
@handle, resolve to user ids, insert into a new mentions(message_event_id, mentioned_user_id, channel_id, created_at, read_at) table. Cheap reads, clean unread state + pagination. Cost: migration + write-path hook + backfill.
- B) Read-time scan (cheaper, weaker): scan recent channel messages for
@<myhandle> on load. No migration, but O(messages)/load, no durable read-state.
Scope when built
- Server:
GET /api/activity → { mentions[], reactions[] }, cursor pagination; reactions = events on my-authored messages; mentions per chosen strategy.
- Client:
api.activity(...) in shared/src/api.ts.
- UI: merge mentions + reactions + agent events in
surface/mobile/app/(app)/activity.tsx + a web equivalent; wire the Activity tab badge to the unread count.
- Tests: server query + web/mobile vitest.
Why deferred
Multi-layer (server + DB + 2 clients) with the architecture decision above — too big to land safely alongside the UI follow-ups. Decide A vs B first.
Follow-up from the sj-audit nav work (#89 shipped the Activity tab). The Activity tab today is an honest v1: agent events only (non-terminal sessions from the existing sessions feed), with a "Mentions & reactions are coming soon" note. This issue tracks making it a real notifications hub.
Goal
Activity surfaces, in one place: @mentions of the current user + reactions on their messages, merged with the existing agent events, newest-first, with read/unread state — on both web and mobile.
What exists today
reaction.added/reaction.removed) in the events table; aggregation lives insurface/server/src/events.ts(reactions json_agg ~:589, net-count ~:488). A reactions feed is queryable: reactions whose target message's author = me.@agentfor spawns. Mentions need a detection strategy.Design fork (decide before building)
Mention detection — write-time index vs read-time scan:
@handle, resolve to user ids, insert into a newmentions(message_event_id, mentioned_user_id, channel_id, created_at, read_at)table. Cheap reads, clean unread state + pagination. Cost: migration + write-path hook + backfill.@<myhandle>on load. No migration, but O(messages)/load, no durable read-state.Scope when built
GET /api/activity→{ mentions[], reactions[] }, cursor pagination; reactions = events on my-authored messages; mentions per chosen strategy.api.activity(...)inshared/src/api.ts.surface/mobile/app/(app)/activity.tsx+ a web equivalent; wire the Activity tab badge to the unread count.Why deferred
Multi-layer (server + DB + 2 clients) with the architecture decision above — too big to land safely alongside the UI follow-ups. Decide A vs B first.