Sprinter Docs

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

TabSortDescription
For YouPersonalized scoringBoosts entities the user owns, has interacted with, or that match their type affinities. No AI call -- pure SQL/JS scoring.
Newcreated_at DESCNewest records first
Updatedupdated_at DESCMost recently changed
Gallerycreated_at DESCExisting 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:

  1. Ownership boost (+3): User owns the entity (owner_id = userId)
  2. Interaction boost (+2): User has activity on this entity (from activities where actor_id = userId)
  3. Type affinity (+1): Entity type matches user's most-interacted types (top 3 by activity count)
  4. Recency boost: Standard time decay
  5. 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 feeds
  • agentSlugs?: string[] -- filter by contributing agent
  • timeRange?: "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 review
  • topAgents: top 5 agents by activity count in last 7 days, with icon and count
  • needsAttention: 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_types table
  • 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:

  1. Stats Card (always shown): DB-driven counts from activities, workflow_runs, extraction_results
  2. Top Agents (always shown): top 5 agents by 7-day activity count with icons
  3. AI Digest (conditional): generated on first visit after 4+ hours, stored in local storage, 2-3 short topic summaries
  4. Needs Attention (always shown): pending extraction results and waiting_human workflow 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 types
  • features/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, time
  • features/feed/components/feed-filters.tsx -- left sidebar filter panel
  • features/feed/components/feed-digest.tsx -- right sidebar digest panel
  • features/feed/components/feed-layout.tsx -- three-column layout shell with mobile collapse
  • features/feed/components/actor-avatar.tsx -- agent/user avatar component with gradient backgrounds
  • app/api/feed/digest/route.ts -- digest stats API endpoint
  • app/api/feed/digest/ai/route.ts -- AI digest generation endpoint

Modified

  • features/feed/server/resolve.ts -- join activities, add actor data, support new filters
  • features/feed/lib/rank.ts -- add "for-you" comparator with boost scoring
  • features/feed/hooks/use-feed.ts -- add digest hook, tab state management
  • app/(app)/feed/page.tsx -- new three-column layout
  • app/(app)/feed/entity-feed-tab.tsx -- tabs and enhanced rendering

On this page