When Switzerland knocked Colombia out of the World Cup, 26 outlets filed 54 articles. FeedBagel showed one card. Here is how that grouping works, and how a story earns its spot on a topic page.
From 54 articles to one card
Every article that comes in gets an embedding: a numeric fingerprint of what it is about. When two articles land close enough together, they join the same cluster. The cluster, not any single article, is the story.
A cluster only renders as a grouped story card once the coverage is corroborated: at least three articles from at least three different publishers. Two outlets writing similar pieces is a coincidence. Three is a story.
Ranking the stories
Ranking is arithmetic, not an editor and not a language model. Each story's position comes from a handful of signals:
- Corroboration. Distinct publishers, saturating at three. Once a story is confirmed by three independent outlets, a twenty-outlet pile-on only earns a small bonus. Freshness decides among real stories.
- Freshness. Scores decay with the age of the coverage and with how long ago the story first broke. Late syndicated copies piling onto a two-day-old story cannot make it look new again, and a story that broke in the last few hours gets a boost that a bigger, older one cannot match.
- Velocity. Articles per hour since the story first appeared. A burst of coverage is the clearest sign something is news right now.
- Quality and reach. Average human rating of the pieces, whether featured publishers picked it up, and engagement on shares.
Which page does a story belong on?
This is the part we just fixed. Articles carry tags, and until this week a story could appear on a topic page if just two of its articles carried the topic's tag. That sounds reasonable until you meet the World Cup.
That Switzerland cluster had 23 articles tagged sports, 9 tagged football, 7 tagged World Cup. It also had exactly two tagged technology: guides to streaming the match with a VPN. Two articles cleared the old bar, so a penalty shoot-out headlined the technology page.
The rule is now proportional. A story lands on a topic page when the topic genuinely characterises the cluster:
- the topic is the story's leading tag, or
- it covers at least a fifth of all the tags across the story's articles, or
- the story is small enough that a single tagged article is real signal rather than noise.
For the World Cup cluster, technology was 2 tags out of 77: under 3 percent. It stays on sports, where 23 of 77 puts it comfortably in first place. We checked the new rule against three days of production data: of 7,044 story-to-technology placements it removed 13, every one a leak like this, and it left sports, design, gaming and programming placements untouched.
Making it instant
Grouping and ranking used to run while you waited for the page. For a busy topic that could mean the server spent over twenty seconds sorting clusters before sending a byte of HTML.
The fix is to stop doing that work during pageloads entirely. The grouped ranking is the same for every reader and only changes when new articles arrive, so the ingestion pipeline now becomes the place it gets computed: one global pass, a few seconds of background work, refreshed within minutes of new coverage landing. The page itself just reads the result.
Story membership stays live: when a new outlet joins a story you are looking at, it shows up on the card immediately. Only brand-new stories wait for the next pass, a couple of minutes at most.
A story is a cluster of corroborated coverage. It ranks by evidence and recency, lands on the pages its tags actually characterise, and the sorting happens in the pipeline so the page never makes you wait for it.
