X's algorithm repository is open, and this time it is not the 2023 snapshot that was published once and abandoned. It is the code running the For You feed right now. Commits land through August 25, and two update notes — August 13 and August 14 — sit at the top of the README.
We covered the secondhand reporting in X's Algorithm Weights Are Public. This time we opened the repository and read it. Two things came out of that. The weight-ratio reading that spread everywhere is wrong — wrong enough that X added code comments to stop it. And the levers a marketer can actually pull are not in the weights at all. They sit on either side of them.
What Is Actually in the Repository
xai-org/x-algorithm ships under Apache 2.0. It splits into two paths: the request path that assembles a feed the moment someone pulls to refresh, and the labeling path that continuously attaches labels to posts and accounts.
home-mixer is the center of the request path. It is written in Rust, and the full pipeline — gather candidates, filter, score, cut — lives there. Phoenix, the ranking model, arrived with both its JAX training code and its Rust serving engine. X states plainly that this release is the production implementation, not a demonstration model. Synthetic data generators are included, so a shrunk version can be trained end to end on a single GPU.
The labeling path landed whole in the August 13 update: visibility-filtering, which decides whether a post can be shown at all, plus the systems that manufacture the labels it reads (grox, agatha, bdsm, user-cred-v2, scarecrow, botmaker, abuse-enforcement-service) and the image and video classifiers.
The August 14 note is a different kind of entry. It is not a feature. It is a correction.
One Feed Request, Step by Step
Candidate Retrieval
Three sources are queried in parallel.
- Thunder — recent posts from accounts the viewer follows, held in memory. Default cap 1,200.
- Phoenix retrieval — embedding search across accounts the viewer does not follow. Default cap 1,000.
- SimClusters — clusters accounts and posts by who engages with what, then pulls candidates from the clusters.
Another source contributes up to 800 more. Roughly 3,000 candidates enter the pipeline.
Seventeen Pre-Scoring Filters
Seventeen filters run in order before anything is scored. A post caught here never gets a score at all.
| Filter | What it removes |
|---|
| DropDuplicatesFilter | The same post returned by more than one source |
| CoreDataHydrationFilter | Posts whose text and metadata failed to load |
| AgeFilter | Posts older than 48 hours |
| SelfTweetFilter | The viewer's own posts |
| OONRetweetReplyFilter | Reposts and replies from accounts the viewer does not follow |
| OONNsfwSimclustersFilter | SimClusters posts from adult-flagged authors |
| RetweetDeduplicationFilter | Repeated reposts of the same post |
| IneligibleSubscriptionFilter | Subscriber-only posts the viewer cannot access |
| PreviouslySeenPostsFilter | Posts already shown |
| PreviouslySeenPostsBackupFilter | The same, from a second impression record |
| PreviouslyServedPostsFilter | Posts already served this session |
| MutedKeywordFilter | Posts matching muted keywords |
| AuthorSocialgraphFilter | Posts from blocked or muted accounts |
| VideoFilter | Video posts when the request excludes video |
| TopicIdsFilter | Posts outside requested topics, posts in excluded topics |
| NewUserMinEngagementFilter | Low-engagement out-of-network posts, for new accounts |
| InventoryHoldoutFilter | A configured share of posts, chosen deterministically |
The third row is the one to internalize. Forty-eight hours. After that, no score matters.
Scoring
Phoenix assigns a probability to each action a viewer might take: favorite, reply, repost, quote, share, share via DM, share via copy link, a range of clicks, dwell, video views, following the author — and the negative actions, not interested, mute, block, report.
RankingScorer collapses them into one number.
Final Score = Σ (weight × P(action))
Selection and Visibility
Sort, keep the top 50. Then ask visibility-filtering about each post and viewer. Whatever it says to drop comes out. The response size is 35. Three thousand candidates in, thirty-five posts out.
Blending
Items the model does not rank get interleaved: ads, Who to Follow, prompts. Who to Follow sits at a fixed slot — position 6 — and the default ads blender reorders posts once more for ad adjacency.
The Weights, as Written in the Code
Straight from home-mixer/params/param.rs.
| Action | Weight |
|---|
| Share via copy link | 20.0 |
| Reply | 5.0 |
| Quote | 5.0 |
| Share via DM | 5.0 |
| Follow author | 4.0 |
| Share | 2.0 |
| Repost | 1.0 |
| Post click | 0.4 |
| Open link | 0.2 |
| Favorite | 0.5 |
| Video open | 0.07 |
| Photo expand | 0.05 |
| Dwell | 0.05 |
| Dwell time (continuous) | 0.004 |
| Profile click | 0.0 |
| Not interested | −43.2 |
| Block author | −31.2 |
| Mute author | −58.8 |
| Report | −234.0 |
| Not dwelled | −0.02 |
A separate mutual-follow reply boost of +15.0 stacks on top. More on that below.
Why "Forty Times a Like" Is Wrong
Divide 20.0 by 0.5 and you get 40. Divide −234 by 0.5 and you get 468. Hence the claims: one copy-link share equals 40 likes, one report cancels 468 likes.
The comments X added to the code on August 14 reject that arithmetic outright.
Weights multiply predicted probabilities, not raw counts. The formula is Σ(weight × P(action)), not Σ(weight × action count). What gets multiplied is the chance that this viewer reports this post — not how many reports the post has collected.
So why is the report weight −234? Because the baseline probability of a report is more than a thousand times lower than that of a like. When a probability is that small, the weight has to be large for the prediction to move the final score at all. The big number is scale correction, not importance.
A second claim falls with it: that coordinated mass reporting or blocking can crush an account's reach. X gives three reasons in the comments.
Recommendations are personalized. Reports from bad actors mostly affect recommendations shown to users similar to those bad actors, not everyone equally.
The action has to occur on a post served in the Home Timeline to count for ranking. Passing a link around a group chat and reporting from there has no ranking impact.
And users have no reliable way to make a specific post appear in their own timeline in the first place.
Three Adjustments After the Weighted Sum
Author Diversity Decay
When one author has several posts among the candidates, everything after the first is discounted.
multiplier = (1 − floor) × decay^rank + floor
decay = 0.5, floor = 0.25
That gives 1.0, then 0.625, then 0.4375, then 0.34375, bottoming out at 0.25. Push four posts from one account into a single feed assembly and the fourth is valued at roughly a third of the first.
Out-of-Network Discount
Posts from accounts the viewer does not follow are multiplied by 0.75. For topic-based requests it is 0.5. Replies and reposts from followed accounts take the same discount.
One constant is worth staring at. For new users — account younger than a threshold, following at least five accounts — that factor drops to 0.00001. Effectively zero. A brand-new user's feed is followed accounts and almost nothing else.
New-Author Boost
Low-impression authors get lifted. The conditions are in the code: under 1,000 impressions, 1,000 or fewer followers, posted within 24 hours. The target slot is 15 to 16, and boosted placement is capped at 85% of the way down the feed.
VMRanker then calls a separate service that reorders the result with a determinantal point process over post embeddings, trading a little score for less similarity between neighbors.
What Actually Changed in July: the Mutual-Follow Boost
The repository includes a document tracing one algorithm change through its diffs — the bidirectional follow boost that drew attention in July.
On July 10, X started an A/B test assigning boost values of 5, 10, 15 and 20 to a small share of users, with everyone else at 0. On July 13, results were strong enough to roll value 20 out broadly. On July 24, after feedback during the World Cup that people were seeing too little discussion from accounts they did not follow, the value was lowered to 15. That is the default in the code today.
The mechanic is simple: if the author is a mutual follow, add 15 to the reply weight. 5.0 becomes 20.0. Four times.
The eligibility check matters more than the number. bidirectional_boost_eligible requires all three: not a reply, not a repost, and a mutual-follow author. The boost applies only to original posts from mutual follows. The same person's replies and reposts get nothing.
Ranking and Visibility Are Separate Systems
This is stated as a design principle in the repository. Ranking decides order. Visibility filtering decides whether a post can be shown at all. Different services, different inputs, different rules.
visibility-filtering returns one of three answers per post and viewer: allow, interstitial, or drop. Rules evaluate in order, and the first drop ends evaluation.
The Band Where Followers See You and Others Do Not
Here is the part worth understanding. A subset of rules applies only when the post is a recommendation from an account the viewer does not follow, and those rules can only drop. Spam caught at high recall, for instance.
The same post is shown normally to a follower and never recommended to anyone else. This is the closest thing in the code to what people call a shadowban, and the rule names say it out loud: RecommendationsOnlyRule, AbusiveHighRecallRule, DoNotAmplifyNonFollowerRule, MaliciousUrlOonDropRule.
In account analytics it looks strange. Follower response holds steady while impressions collapse. Reach down with engagement rate flat is the signature to look for.
Where the Labels Come From
The labels those rules read are produced continuously, off the request path.
- grox — classifiers that run as posts are published: spam, adult content, violent media.
- media-model-proxy / clip — image and video models, and the embeddings feeding them.
- agatha — batch jobs that score an account by how others respond to it. The key axis is blocks, reports and spam reports relative to favorites.
- bdsm — reads an account's action sequence over time to find inauthentic or abusive patterns.
- user-cred-v2 — PageRank over the follow graph and engagement edges.
- scarecrow + botmaker — apply label rules to events as they happen.
- abuse-enforcement-service — acts on model scores: label, challenge, or suspend.
Agatha's axis is instructive. It reads blocks and reports as a ratio to favorites, not in absolute counts. An account that gets a lot of response and a proportional amount of complaint is treated as normal.
What Is Not Published
X named the exclusions: Grox's LLM prompt files, and some botmaker rules. The reason is gaming. Publish the full rule text and circumvention becomes designable.
In place of that, X is piloting Under the Hood, which shows aggregate statistics on the visibility-limiting labels applied to your own account and posts — the tool covered in X Open-Sources Its Reach Code. Read the rules in the code, check your own labels in the tool. That pairing is the transparency model X is proposing.
One more entry deserves attention. The August 14 note added a Brazil 2026 election filter: under Brazilian electoral law, For You now removes posts from accounts reported to Brazil's Electoral Court, unless the viewer explicitly follows the account. The account list was updated on August 25. It is a clean example of how jurisdiction-specific law enters the code.
What a Marketer Can Do With This
Forty-eight hours is the whole window. AgeFilter cuts everything past it. There is no long tail in For You. Publication time and the first few hours are the entire lifespan.
Posting more does not scale linearly. Author diversity decay values the fourth post in a feed assembly at 34% of the first. Two or three posts spread across the day beats five stacked together.
Mutual follows are the single largest lever. Original posts from mutual-follow authors carry a 4× reply weight. Growing follower count and building mutual follows are different operations in algorithmic terms. Mutual follows with industry accounts, partners and client-side contacts have measurable value.
Reposts and replies cannot earn the boost. Eligibility is limited to original posts, and replies and reposts from non-followed accounts are cut at the pre-scoring stage anyway. A repost-heavy channel strategy is structurally disadvantaged.
Demote likes as a KPI, but skip the multiplication. A 0.5 weight is genuinely low. Still, "40 copy-link shares equals 1,600 likes" does not hold. Read the direction only: content people want to carry elsewhere, and content that draws replies, is valued higher.
Campaigns targeting new users need the follow first. The out-of-network factor for a new account is 0.00001. Reaching that feed requires a follow, not a recommendation.
Reach down with engagement rate flat means check labels, not content. That pattern points at visibility filtering rather than ranking. Checking Under the Hood is faster than rebuilding a content strategy.
Finally, X says experiments running at 10% of traffic or more should be visible in the repository, and cron scripts push production defaults back into the published code. Parameter changes leave diffs — which is how the July boost dropping from 20 to 15 is verifiable at all. For a different angle on X's tooling, see X Launches Its Ads MCP Server. Watch the repository instead of waiting for announcements, and the next change arrives before the coverage does.