Skip to content

Tig 257 stabilize explore feed ranking - #38

Open
rohanmatta11 wants to merge 6 commits into
stagingfrom
TIG-257-stabilize-explore-feed-ranking
Open

Tig 257 stabilize explore feed ranking#38
rohanmatta11 wants to merge 6 commits into
stagingfrom
TIG-257-stabilize-explore-feed-ranking

Conversation

@rohanmatta11

Copy link
Copy Markdown

Summary

Stabilizes and documents Explore feed ranking (TIG-257).

Ranking formula (see docs/ranking.md for full detail)

score = 3.0 × interest_relevance (tag overlap with your onboarding interests)

  • 2.0 × time_proximity (halves every 4 days out, smooth decay)
  • 4.0 × friend_rsvp_score (friends attending, caps at 3+)
  • 1.0 × org_affinity (1.0 follow/member, 0.5 past RSVP, 0 otherwise)
  • 1.0 × recency_boost (posted in last 1–3 days)
  • 0.5 × popularity_score (log-scaled view count)
  • 0.5 × random_nudge (seeded per user-per-day)

Highest score wins. The random nudge is a deterministic hash of (userId, today's date, eventId), so the same user sees the same order all day, but the feed varies day to day. Two extra rules run after scoring: an org can have at most 3 events near the top of the ranking (rest pushed later), and the first page always includes a few events happening within 24h even if their score is weak, so nothing imminent gets buried behind a highly-social event further out.

Changes

  • apps/web/src/actions/events.ts: widened candidate pool (100 events, not just page size) so personalization has more impact; replaced the bucketed time score with smooth decay; added org diversity cap, tiered org affinity, popularity signal, and the seeded random nudge.
  • docs/ranking.md: writeup of formula, each signal, and edge cases (no interests/friends/orgs).

Testing

Verified against seeded data (two users with different interests get different, explainable top events; repeated calls produce identical order; a zero-signal user gets a full feed with no errors; forcing a small page size confirms the soon-event guarantee actually kicks in under truncation). Screen recordings of both seeded users' Explore pages attached below.

https://github.com/user-attachments/assets/6a8b485a-e733-4f08-ac5a-a12c1d4ac0b9
https://github.com/user-attachments/assets/b32cdb0b-0156-4141-ab4d-845445c82bde

@linear

linear Bot commented Aug 21, 2026

Copy link
Copy Markdown

TIG-257

Comment thread apps/web/src/actions/events.ts Outdated
Comment thread apps/web/src/actions/events.ts Outdated
Comment thread apps/web/src/actions/events.ts Outdated

@angelina-ji angelina-ji left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall this looks solid and aligns well with TIG-257. I left a few comments around production-scale behavior: the fixed candidate pool, pagination consistency with the soon-event injection, and the number of per-event DB queries now that we're scoring up to 100 events.

Since we're expecting hundreds of events, I think the two correctness issues should be addressed before merge, and I'd also like to see the query pattern either batched or validated against a realistically sized dataset. Once those are updated, I can re-review and test the ranking behavior.

@rohanmatta11
rohanmatta11 force-pushed the TIG-257-stabilize-explore-feed-ranking branch from 738e40c to 7430f6c Compare August 25, 2026 21:58
@rohanmatta11

Copy link
Copy Markdown
Author

Hey, I think I addressed the issues, though I couldn't really check some things.

I replaced the 100 events cap with a default dateRange filter of 14 days (so only events from the next 14 days would show up), and then put 5000 events as a safeguard in case there's too many events.

For the event-ordering bug, I made it finalize the order and then paginate, so it shouldn't have the bug anymore (though I couldn't check this since there's no load more feature to move to the next page).

For the benchmarking, I just counted the number of queries and latency for each method after generating 1000+ dummy events:
Old: 612 queries, 128ms latency, 100 of 1333 events checked
Updated: 13 queries, 47ms latency, and 1333 of 1333 events checked

Comment thread apps/web/src/actions/events.ts Outdated
if (soonInFront < SOON_QUOTA) {
const frontIds = new Set(front.map((e) => e.id));
const missingSoon = tail
.filter((e) => isSoon(e) && !frontIds.has(e.id))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think the soon-event injection can undo the org-diversity cap here. ranked has already deferred the 4th+ event from an org, but missingSoon pulls from tail without checking the org counts in front, so a deferred 4th event can be reinserted into the first 20 if it's imminent. That means the “max 3 per org near the top” guarantee isn't always true. Can we either preserve the cap during injection, or explicitly make/document the soon-event rule as an exception to the diversity cap?

@rohanmatta11

Copy link
Copy Markdown
Author

Changed it so that it should only drag a soon event to the top if it's not part of an org that's already there 3 times (preserve the cap during injection)

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.

2 participants