Trending-pattern clone ranked by sats from kind-9735 receipts:
- js/zapTotals.js: per-pointer receipt aggregation. The feed's metric getter
returns the cached total and schedules a batched fetch (#a/#e chunks via
the L1 SubscriptionManager) for unknown/stale pointers; amounts parse via
nip57 bolt11 with the zap request's amount tag as fallback; receipts are
deduped by id so re-fetches never double-count; zero-receipt answers are
cached (2-min TTL).
- sorters.js: shared count-ranked sorter (metric desc, recency tie-break,
author spread, trusted-muted sink); Trending delegates unchanged, Most
Zapped ranks on runtime.getZapTotal.
- Full wiring: feed definition, view + info popover, change-signal re-rank
(order settles as receipts stream in), sidebar link, viewManager registry,
feedCoordinator case/loader, FEED_TYPES.MOST_ZAPPED, and a
FEATURE_MOST_ZAPPED_FEED flag (default on; off hides the tab).
Co-Authored-By: Claude Fable 5 <[email protected]>
The For You (personalized) and Trending tabs ranked strictly by tier/score and
view count, which stacked a creator's whole back-catalogue into one block.
Add spreadAuthors() to the feed sorters: a gentle diversity re-rank that keeps
the caller's ranked order as the priority but avoids runs of the same author —
at each step it takes the highest-ranked remaining item whose author wasn't
among the last `window` picks (default 1), falling back to the plain top item
when every remaining candidate is a recent author. The #1 ranked item is always
chosen first, so "best-fit"/"most-viewed" stays topmost, and each author's items
keep their relative ranked order.
Applied to createForYouScoreSorter (personalized branch) and createTrendingSorter
(live items). Unlike interleaveByAuthor (full round-robin, used only for For
You's no-signal fallback + Explore), this preserves ranking rather than
flattening it, so each tab keeps its unique ordering process — just de-clustered.
Recent (chronological) and Explore (already diversified) are unchanged.
Tests: tests/feed-engine/spread-authors.test.mjs (7 — top preserved, no
avoidable adjacent dupes, per-author order kept, single-author/short/anon edge
cases, window:2 spread).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
New FEED_TYPES.TRENDING tab: the active (recently-added) source ranked by view
count (recency tiebreak, trusted-muted last), gated by FEATURE_TRENDING_FEED
(default on). Builds on the now-trustworthy view counts (#4).
- createTrendingSorter reads counts from runtime.getViewCount, which resolves
each video's pointer via the app's canonical deriveVideoPointerInfo against the
shared viewCounter cache (new getVideoViewCountSnapshot export) — the same
counts the grid cards already load.
- Live re-rank: viewCounter now emits a coalesced onViewCountsChanged signal;
while the Trending view is active it debounce-re-runs the feed (cheap re-rank
over the in-memory active set, no relay re-fetch), so the order settles into
true trending as counts stream in. Cold cache reads as recency, then improves.
- Pipeline extracted to js/feedEngine/trendingFeed.js (feedCoordinator is at its
size cap; bumped the cap for the thin load/refresh delegations).
- Wiring: FEED_TYPES + flag, feedCoordinator cases, app.js wrappers,
applicationBootstrap registration (hides the sidebar link when flag off),
viewManager init + js/trendingView.js, views/trending.html, sidebar link.
Tests: tests/trending-sorter.test.mjs.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
For You shared the full active set with Recent/Explore and only re-ranked by a
blended score, so when a user's signals were thin it collapsed toward freshness
and looked identical to Recently-added.
Give it a structural identity:
- The scorer tags each item forYouTier: 2 = followed author, 1 = interest/
watch-topic match, 0 = other.
- The sorter leads with tier (your people first) regardless of raw score, then
ranks within tier; trusted-muted still sinks last.
- When NOTHING is tiered (logged-out / no follows / no interests) it falls back
to an author-interleaved discovery order (new interleaveByAuthor) so the feed
never reads as a chronological clone of Recently-added.
Tests: tests/for-you-tiering.test.mjs (tier beats score, tiers stack, muted
last, no-signal interleave, scorer tier tagging). Existing for-you-scorer suite
still green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The For You feed was chronological, hashtag-interest-EXCLUSIVE, and ignored
follows entirely (docs/feed-algo-audit.md). Now it is inclusive + ranked:
- createForYouScorerStage scores each video: follows-boost (author you follow)
+ interest/watch-topic affinity (cosine vs your interests+watch-history vector)
+ freshness - disinterest penalty. Reuses Explore's vector/similarity helpers.
- createForYouScoreSorter ranks by that score (muted last, recency tiebreak).
- For You pipeline: interests now BOOST not exclude (enforceInterests:false);
scorer added; chronological sorter replaced. Never empty (falls back to
recency when you have no follows/interests/history).
- buildForYouFeedRuntime now feeds subscriptionAuthors + watchHistoryTagCounts.
Watched/blocked/muted are still removed (suppression/blacklist/moderation
stages unchanged). Cheat-resistant tests (older boosted items only win via the
boost). Grid renders end-to-end (~1.3s). Weight tuning is a follow-up.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Introduced `NORMALIZED_ARRAY_MARKER` in `js/feedEngine/utils.js` to tag arrays that are already normalized.
- Updated `normalizeItems` in `js/feedEngine/engine.js` to skip O(N) verification if the input array is marked.
- Updated built-in stages and sorters to mark their output arrays using `markAsNormalized` when they safely construct new arrays from normalized inputs.
- This avoids redundant O(N) scans after each stage, improving pipeline performance by ~10% end-to-end and eliminating overhead for trusted stages.
Co-authored-by: PR0M3TH3AN <[email protected]>
Hoisted the timestamp hook resolution out of the inner loop in `createChronologicalSorter`.
This avoids allocating an array of candidate functions for every comparison during sorting (O(N log N)).
Benchmark shows a ~27% speedup (38ms -> 27ms for 100k items).
Co-authored-by: PR0M3TH3AN <[email protected]>
- Implements an Inverted Index to reduce redundant similarity checks.
- Replaces O(N) array splice with O(1) Map deletion for candidate tracking.
- Reduces runtime from ~3.5s to ~0.9s for 5000 items (3.9x speedup).
Co-authored-by: PR0M3TH3AN <[email protected]>