Skip to content

Feature: Personalized developer activity and watchlist feed - #142

Merged
sajeetharan merged 13 commits into
sajeetharan:mainfrom
HereIsMuhammad:feature/personalized-developer-feed-v2
Aug 13, 2026
Merged

Feature: Personalized developer activity and watchlist feed#142
sajeetharan merged 13 commits into
sajeetharan:mainfrom
HereIsMuhammad:feature/personalized-developer-feed-v2

Conversation

@HereIsMuhammad

Copy link
Copy Markdown
Contributor

Closes #127

Summary

Adds a personalized, chronological feed built from a signed-in developer's watchlists (followed developers, projects, languages, countries) and DevGlobe's contribution snapshots. Surfaces meaningful changes — rank movement, milestones, new top projects, and profile updates — instead of a generic social stream.

See docs/prd/personalized-developer-feed.md for the full spec and API contract.

What's included

  • lib/feed.js — event normalization, watchlist matching, mute/privacy filtering, grouping/compaction, cursor pagination, read state
  • lib/feed-store.js — Cosmos-backed feed event store with in-memory fallback (mirrors lib/activity-store.js conventions)
  • lib/watchlist-store.js — minimal follow/mute/read-state storage the feed reads from (full watchlist UI is Feature: Developer and project watchlists with update notifications #113)
  • GET/PATCH /api/feed — list personalized feed, mark read
  • POST/DELETE /api/feed/mute — mute an entity or event type
  • POST /api/feed/ingest — internal bearer-secret endpoint for publishers to write feed events
  • tests/feed.test.js — authorization/privacy, ordering, deduplication, and pagination tests

Acceptance criteria covered

  • Feed built from explicit follows/watchlists
  • Meaningful event types: rank movement, milestones, new top projects, profile updates
  • Groups repeated changes from the same developer/refresh cycle
  • Filters by developer, project, language, country, event type
  • Read/unread state and cursor-based pagination
  • Mute an entity or event type from the feed
  • Excludes private fields, respects claimed-profile privacy
  • Defined freshness, retention, and compaction rules
  • No engagement bait, public like counts, or unrestricted posting
  • Tests for authorization, ordering, deduplication, pagination

Out of scope (documented in the PRD)

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

@HereIsMuhammad is attempting to deploy a commit to the sajeetharan's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the backend and core logic for a personalized developer feed built from a signed-in user’s watchlists, with privacy/mute filtering, grouping/compaction, cursor pagination, and read-state support, plus an internal ingest endpoint and a PRD describing the contract.

Changes:

  • Added feed event normalization + personalization pipeline (watchlist match → mute → sort/group → filter → cursor pagination + read state).
  • Added Cosmos-backed storage for feed events and per-user watchlist/mute/read-state data (with in-memory fallbacks).
  • Added API routes for listing/marking read, muting/unmuting, and internal ingestion; added unit tests for feed logic.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/feed.test.js Adds unit coverage for feed normalization, matching, muting, grouping, sorting, read-state, and pagination.
lib/watchlist-store.js Adds minimal watchlist storage for follows/mutes/read-state with Cosmos + in-memory fallback.
lib/feed.js Implements feed event model + personalization pipeline utilities.
lib/feed-store.js Adds Cosmos + in-memory feed event persistence and candidate listing.
docs/prd/personalized-developer-feed.md Documents the PRD/spec and API contract for the personalized feed MVP.
app/api/feed/route.js Adds authenticated GET feed endpoint and PATCH read-state updates.
app/api/feed/mute/route.js Adds authenticated mute/unmute endpoint for entities and event types.
app/api/feed/ingest/route.js Adds bearer-secret internal ingestion endpoint for publishing feed events.
Suppressed comments (1)

lib/feed-store.js:51

  • The Cosmos query in listCandidateFeedEvents also doesn't filter visibility, even though the docstring says it returns public events. Adding c.visibility = "public" keeps the candidate pool smaller and makes the store-layer privacy boundary harder to accidentally bypass.
  const { resources } = await container.items.query({
    query: 'SELECT * FROM c WHERE c.documentType = "feed-event" AND c.createdAt >= @cutoff',
    parameters: [{ name: '@cutoff', value: cutoff }],
  }).fetchAll();

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/feed-store.js
Comment thread lib/feed-store.js
Comment on lines +44 to +46
if (!container) {
return [...memoryEvents.values()].filter(event => event.createdAt >= cutoff);
}
Comment thread app/api/feed/route.js
Comment on lines +57 to +62
const candidates = await listCandidateFeedEvents();
const now = new Date().toISOString();
const latest = candidates.length ? candidates.reduce((max, e) => (e.createdAt > max.createdAt ? e : max)) : null;
const cursor = latest ? { createdAt: latest.createdAt, id: latest.id } : { createdAt: now, id: '' };
const watchlist = await markAllRead(session.login, cursor);
return NextResponse.json({ readState: watchlist.readState });
Comment thread app/api/feed/route.js
Comment on lines +65 to +68
if (Array.isArray(body.eventIds) && body.eventIds.length) {
const watchlist = await markRead(session.login, body.eventIds);
return NextResponse.json({ readState: watchlist.readState });
}
Comment on lines +31 to +33
try {
const result = await saveFeedEvents(body.events, { isPublicDeveloper: body.isPublicDeveloper !== false });
return NextResponse.json(result);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI mentioned this pull request Aug 13, 2026
4 tasks
@sajeetharan
sajeetharan merged commit 3f6a34c into sajeetharan:main Aug 13, 2026
2 of 3 checks passed
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.

Feature: Personalized developer activity and watchlist feed

3 participants