Feed v2
Activity-aware personalized feed with three-column layout, For You ranking, and AI-powered digest sidebar.
Problem
The current /feed page is a simple entity gallery -- a flat list of entity cards with basic sort options. It does not surface activity context (who changed what and when), offers no personalization (every user sees the same order), and provides no summary of what happened while the user was away. For a platform designed around human-agent collaboration, the feed should be the primary "what happened" surface, not a static card gallery.
Solution
Rebuild /feed into a Twitter-style activity-aware feed with a three-column layout: filter sidebar on the left, feed stream in the center, and a digest/stats sidebar on the right. Add four feed tabs (For You, New, Updated, Gallery), personalized "For You" ranking, activity context on each feed item, and an AI-generated digest for returning users.
Design
Architecture
The feed pulls data from existing tables -- no new tables are required. Entity data comes from the existing entities query path. Activity context joins from the activities table. Digest stats query activities, workflow_runs, and extraction_results tables.
Layout follows a three-column CSS grid:
- Left rail (240px): filters (entity types, tags, agents, time range)
- Center (flex-1): tab bar + feed stream
- Right rail (280px): stats card, top agents, AI digest, needs-attention items
Left and right rails collapse to sheet triggers on mobile, with the center column going full-width.
Feed Tabs
| Tab | Sort | Description |
|---|---|---|
| For You | Personalized scoring | Boosts entities the user owns, has interacted with, or that match their type affinities. No AI call -- pure SQL/JS scoring. |
| New | created_at DESC | Newest records first |
| Updated | updated_at DESC | Most recently changed |
| Gallery | created_at DESC | Existing 3-column card grid, preserved from v1 |
Feed Item Data Model
Each feed item is entity-centric with activity context layered on:
interface FeedItemV2 extends FeedItem {
recentActivity: {
action: string;
actorName: string | null;
actorType: "user" | "agent";
agentSlug: string | null;
agentIcon: string | null;
description: string | null;
createdAt: string;
}[];
lastActor: {
name: string;
type: "user" | "agent";
agentSlug: string | null;
agentIcon: string | null;
} | null;
activityCount: number;
}The recentActivity array holds the last 3 activities on each entity, providing context like "Analyst enriched 2 fields" or "Tyler updated status to Active" directly on the card.
For You Ranking
Lightweight, no AI -- pure SQL/JS scoring:
- Ownership boost (+3): User owns the entity (
owner_id = userId) - Interaction boost (+2): User has activity on this entity (from
activitieswhereactor_id = userId) - Type affinity (+1): Entity type matches user's most-interacted types (top 3 by activity count)
- Recency boost: Standard time decay
- Importance scores: Existing metadata scores from entity content
Score = sum of boosts, sorted descending with recency as tiebreaker.
API
POST /api/feed (extend existing)
Add to FeedConfig:
tab: "for-you" | "new" | "updated" | "gallery"-- replaces sortBy for tab-based feedsagentSlugs?: string[]-- filter by contributing agenttimeRange?: "today" | "week" | "month" | "all"
Response adds recentActivity, lastActor, activityCount per item.
GET /api/feed/digest (new)
Returns { stats, topAgents, needsAttention } -- all DB-driven aggregations:
stats: records updated today, new records today, workflows completed, items needing reviewtopAgents: top 5 agents by activity count in last 7 days, with icon and countneedsAttention: pending extraction results, workflow nodes waiting for human input (max 5)
POST /api/feed/digest/ai (new)
Returns { summaries: string[] } -- AI-generated topic summaries. Rate-limited per user. Only called on first visit after 4+ hours. Result cached in local storage with timestamp. Falls back gracefully to "No digest available."
Filters (Left Sidebar)
All filter state is held in URL search params for shareability:
- Entity Types: checkbox list from
entity_typestable - Tags: top tags with counts, searchable
- Agents: checkboxes with agent icon and name
- Time Range: Today / This Week / This Month / All Time
Digest (Right Sidebar)
Three sections:
- Stats Card (always shown): DB-driven counts from activities, workflow_runs, extraction_results
- Top Agents (always shown): top 5 agents by 7-day activity count with icons
- AI Digest (conditional): generated on first visit after 4+ hours, stored in local storage, 2-3 short topic summaries
- Needs Attention (always shown): pending extraction results and
waiting_humanworkflow nodes, max 5 with links
Trade-offs
Activity join vs. separate query: Joining activities onto the feed query adds complexity to the resolver but avoids N+1 queries. Activity data is fetched as a lateral join limited to 3 per entity, keeping the query efficient.
Local storage for AI digest vs. server-side cache: Local storage avoids server-side per-user caching infrastructure. The tradeoff is that the digest does not sync across devices, but for a "what happened while you were away" feature, per-device freshness is acceptable.
No new tables: All data comes from existing tables (entities, activities, workflow_runs, extraction_results, agents). This avoids migration complexity but means some queries (like top agents) require aggregation at query time rather than pre-computed counts.
URL params for filter state: Makes feeds shareable and bookmarkable. The tradeoff is longer URLs, but this matches the pattern used elsewhere in the platform.
Acceptance Criteria
- Three-column layout renders correctly at desktop and collapses to single-column on mobile
- All four tabs (For You, New, Updated, Gallery) load correct data with correct sorting
- For You tab personalizes based on ownership, interaction history, and type affinity
- Feed items show activity context (last actor, recent activities, activity count)
- Agent activities show agent name and icon distinct from user activities
- Filters apply via URL search params and persist across navigation
- Entity type, tag, agent, and time range filters all work correctly
- Digest sidebar shows accurate stats, top agents, and needs-attention items
- AI digest generates on first visit after 4+ hours and caches in local storage
- AI digest degrades gracefully when API errors or rate limits hit
- Gallery tab preserves existing v1 card grid behavior
- No regressions in existing feed functionality
- Visual verification at desktop (1280x720) and mobile (375x667)
Files
New
features/feed/types.ts-- FeedItemV2, DigestResponse, FeedTab typesfeatures/feed/server/resolve-digest.ts-- digest stats resolver (queries activities, workflow_runs, extraction_results)features/feed/server/resolve-for-you.ts-- For You scoring logic (ownership, interaction, type affinity)features/feed/components/feed-item-card.tsx-- enhanced card with actor avatars, activity context, timefeatures/feed/components/feed-filters.tsx-- left sidebar filter panelfeatures/feed/components/feed-digest.tsx-- right sidebar digest panelfeatures/feed/components/feed-layout.tsx-- three-column layout shell with mobile collapsefeatures/feed/components/actor-avatar.tsx-- agent/user avatar component with gradient backgroundsapp/api/feed/digest/route.ts-- digest stats API endpointapp/api/feed/digest/ai/route.ts-- AI digest generation endpoint
Modified
features/feed/server/resolve.ts-- join activities, add actor data, support new filtersfeatures/feed/lib/rank.ts-- add "for-you" comparator with boost scoringfeatures/feed/hooks/use-feed.ts-- add digest hook, tab state managementapp/(app)/feed/page.tsx-- new three-column layoutapp/(app)/feed/entity-feed-tab.tsx-- tabs and enhanced rendering
Entity Card System
Unified schema-driven entity cards with custom overrides, container-query responsiveness, and agent-writable card configuration.
Entity Graph Overhaul
Replace the static circular-layout entity graph with an Obsidian-inspired force-directed visualization, reusable as a block and mini-map.