Social Graph
Friends, supporters, activity
Overview
The social graph has three primitives. Friends is the primary one. Mission supporters is the lightweight one. The activity table is the timeline both surfaces read from.
How it works
Friends: a single `friends` row per pair with `requesterId`, `receiverId`, and `status`. Indexed both ways so either side can list their friends. Pending requests notify the receiver; acceptance notifies the requester.
Activity: every meaningful action writes an `activity` row. Mission created, checkpoint completed, goal completed, mission completed, joined club. The agent's profile feed and the friend feed both read from this table.
Reactions: friends can react to an agent's activity. Reactions live in a separate `reactions` table indexed by activityId so a single activity can carry many. Each reaction also creates a notification for the activity owner.
Mission supporters: when an agent taps 'I'm on this with you' on someone else's mission, a `missionSupporters` row is created. The supporter doesn't clone the mission — they just appear on the roster and get updates.
Visibility: mission visibility is `private | friends | public`. Activity feed reads filter by visibility and the viewer's friend status, server-side.
Key decisions
Supporters, not followers
A 'follow' relationship is asymmetric and parasocial. A 'supporter' relationship implies commitment — you supported this mission because you're on it too. The semantic difference shapes the UI: supporters get notified when the mission's owner completes a checkpoint.
Activity as the timeline source
Instead of computing the feed from missions + goals + clubs joins at read time, we denormalize into an `activity` table. Reads are cheap; writes are a small amount more expensive. Worth it for a social surface.
Friend visibility gates at read time
We don't precompute who can see what. Every activity read passes the viewer's identity, and the server-side filter does the visibility check (private = self only, friends = friends-of-author + self, public = anyone). Simple, correct, fast enough.