diff --git a/CLAUDE.md b/CLAUDE.md index 4e52f95..0095632 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -120,6 +120,18 @@ API/contract.md — REST + WebSocket API spec - Pagination: cursor-based via `nextCursor`. - `StoryStore.setPill()` resets `lastSyncedAt` to bypass the 30s rate-limit on filter switch. - `sectionSupplement` fetches populate sparse regional sections in the All digest independently. +- **Age must be read from `firstSeenAt`, never `updatedAt`.** The backend bumps `updatedAt` + every time it attaches another article to a cluster, however loosely related — a story + can read "1 hr ago" while being weeks old (see `docs/BACKLOG.md` #15). `firstSeenAt` is + the honest age and is already decoded on `StorySummary`. +- **Stale-news control (client-side, `Views/Shared/Theme.swift`):** `clientRank` applies a + soft time-decay to `signalScore` so fresh stories outrank stale high-scorers in + `feedOrder` — it computes age from `firstSeenAt`, not `updatedAt` (was silently broken + by the same bug above until fixed). `StorySummary.isFossil` (`firstSeenAt` > 5 days) is a + hard backstop on top of that: `SignalFeedView.mainStories` and `.makeDigest()` sink + fossils below current stories so one can never take the hero/lead slot, regardless of + score. Fossils still appear in the feed — just not in the most prominent position. This + is a display/ordering patch; the server's own ranking is still wrong at the source. ---