What makes a post get filtered out before it's even ranked?
A set of filters runs before scoring, and any one of them can remove your post from a given feed entirely — not rank it low, remove it. The released code shows filters for muted keywords (your post contains a word that viewer muted), previously-seen posts (they already saw it), age (it's older than the feed's max), conversation duplicates (a stronger post from the same thread won), and the viewer's own posts. A filtered post never reaches scoring. This is why "low reach" and "filtered out" are different diagnoses.
Scoring gets all the attention, but the filter stage that runs before it is just as decisive — and more binary. A scorer ranks you lower; a filter removes you. Understanding which filters exist tells you which reach problems are about eligibility, not quality.
The filters are a fixed set
The released pipeline registers filters as discrete components, each removing a class of candidate:
| filter | removes a post when… |
|---|---|
| Muted keyword | its text matches a word the viewer has muted |
| Previously seen | the viewer already saw it (seen IDs or bloom filter) |
| Age | it's older than the feed's max age |
| Conversation dedup | a higher-scoring post from the same conversation already won the slot |
| Self-tweet | the author is the viewer themselves |
Before scoring, Home Mixer removes: duplicates, posts that failed metadata hydration, posts older than a threshold, your own posts, repost duplicates, paywalled content you can't access, posts you've already seen or been served, posts containing your muted keywords, and posts from authors you've blocked or muted.
Muted keywords: a content match, not a judgment
The muted-keyword filter tokenizes your post and removes it if it matches any keyword the viewer muted — a pure text match, nothing to do with your post's quality:
45for candidate in candidates { 46 let tokens = tokenizer.tokenize(&candidate.tweet_text); 47 if matcher.matches(&tokens) { 48 removed.push(candidate); } else { 50 kept.push(candidate); } }
The MutedKeywordFilter tokenizes each post and removes it if its text matches any keyword the viewer has muted — a text match against the viewer's muted_keywords, independent of the post's quality.
Age: old posts age out of the feed
The age filter removes candidates older than a configured maximum — partitioning on whether each
post is within the allowed window. An old post isn't ranked low; for that feed, it's simply not
eligible.
The AgeFilter removes candidates older than a configured max_age, partitioning posts on whether they fall within the allowed age window.
Why this distinction matters
If your reach is down, the filters point to a different fix than scoring does. Filtered for muted keywords? That's viewer-side and largely out of your hands. Filtered for age? Your post needed to be seen sooner — a timing question, not a quality one. Filtered for dedup? A stronger post in the same thread beat it. None of these is the scoring formula judging your content poorly.
What the code doesn't say
The exact filter ordering and parameters — the age cutoff, which filters run for which feed
surface. These are config-and-params, not in the open release. The filter set is
code-current; the exact runtime tuning is not.
The numeric values of the current weights are not included in the open-source release: weighted_scorer.rs references a params module (e.g. p::FAVORITE_WEIGHT, p::REPLY_WEIGHT) whose values are not present anywhere in the published repository.
What to do with this
When a post underperforms, ask "was it filtered or outscored?" — they have different fixes. You can't control a viewer's mutes, but you can control freshness and avoid stacking a thread. xDoctor's diagnostics distinguish eligibility problems from ranking problems, which is the first fork in any real reach diagnosis.