Track the ranking weights behind X's For You feed, and see when they move.
X open-sourced the code that ranks its For You timeline. The numbers that decide how much a reply is worth against a like sit in a single Rust file, and X re-syncs that file from its production feature switches. rankdrift pulls those numbers into JSON, renders a readable table, and diffs one snapshot against the next.
The point is the diff. A weight table you read once goes stale; X has said the code gets updated every four weeks. Keeping snapshots means you can tell when the feed changed, and by how much.
From the 2026-08-12 sync. The figure is rendered by rankdrift/chart.py from the
same snapshot JSON as the table below, so both move when the upstream file does.
| Action | head | Weight | Weight ratio † |
|---|---|---|---|
| Copy link to share | share_via_copy_link |
20.0 | 40x |
| Reply | reply |
5.0 | 10x |
| Quote | quote |
5.0 | 10x |
| Share via DM | share_via_dm |
5.0 | 10x |
| Follow the author | follow_author |
4.0 | 8x |
| Repost | retweet |
1.0 | 2x |
| Like | favorite |
0.5 | 1x |
| Profile click | profile_click |
0.0 | — |
| Dwell | dwell |
0.0 | — |
| Report | report |
-234.0 | — |
† Ratio between weights, with favorite as 1. It is not a count equivalence, and the
upstream source says so explicitly. See How to read these numbers.
There is also a conditional boost worth more than any base weight except the first row: when a post is not a reply, not a repost, and the author and viewer follow each other, reply weight goes from 5.0 to 20.0.
Full table with negatives, multipliers, the author-diversity formula and pipeline
constants: WEIGHTS.md.
git clone https://github.com/xai-org/x-algorithm.git --depth 1 upstream
python3 rankdrift/extract.py upstream data/latest.json # 192 params + 24 constants
python3 rankdrift/report.py data/latest.json WEIGHTS.md # readable table
python3 rankdrift/chart.py data/latest.json docs/assets/weights.html
python3 rankdrift/chart.py data/latest.json docs/assets/weights.ja.html --lang ja
cp data/latest.json data/snapshots/$(date +%F).json # keep a snapshot
python3 rankdrift/diff.py data/snapshots/<older>.json data/latest.jsonTo turn the figure source into the PNG in this README:
google-chrome --headless=new --hide-scrollbars \
--force-device-scale-factor=2 --window-size=1200,1080 \
--screenshot=docs/assets/weights.png file://$PWD/docs/assets/weights.htmlThe window is taller than the page on purpose. Chrome's --screenshot drops the last
line it paints, which is the attribution footer, so the page keeps padding below the
footer and the window keeps slack below that. Check the footer is in the PNG.
The four tools need nothing beyond the standard library. The PNG step needs a headless Chrome.
They are not multipliers on your engagement counts. Phoenix predicts how likely the viewer is to take each action on a post, and the weights apply to those predictions. What scores is "predicted to get replies." The count of replies a post actually received never enters the formula.
The ratios are not count equivalences. The upstream source rejects that reading by
name, in a comment in home-mixer/scorers/ranking_scorer.rs:
One common misinterpretation is that you can read these weight ratios as count equivalences, e.g. the incorrect statement that "one report cancels 468 likes" -- this is incorrect because the weights apply to the predicted probabilities rather than raw counts.
And the baseline probability of a Report is more than 1000x lower than a Like, so it’s weighted more to allow the prediction to affect the final ranking at all.
report at -234.0 is large because P(report) sits three orders of magnitude below
P(favorite). Without the scale-up the prediction would not move the total at all. The
magnitude carries the base rate as well as the severity, which is why translating any
row into "worth N likes" breaks it.
Mass reporting does not suppress reach the way people assume. The same comment gives two reasons. The score sums predicted probabilities per viewer rather than report counts, and recommendations are personalised, so reports from bad actors mostly move recommendations for users who resemble those actors. Separately, only engagement on a post served through the Home Timeline counts at all, so coordinating through a shared link has no ranking impact.
They are a mirror of production, taken at a point in time. The upstream file opens with
mirrored from config feature-switch defaults; last sync <timestamp>. Production
reads feature switches that can override these defaults, and that overlay is not in
the public repo. Every table rankdrift emits carries the sync timestamp for this
reason.
They record which upstream revision they came from. Every snapshot carries the
upstream commit SHA, its commit date, and the SHA-256 of both files that were read.
Two commits can share a sync timestamp and still differ, and a modified clone will
not match the hashes. diff.py prints the commit range between two snapshots as a
compare link, so the change that moved a weight is one click away.
They stop when the upstream syntax changes. The parser cross-checks the param!
and pub const names found by a loose scan against the ones it managed to parse
strictly. A single miss exits with an error instead of writing a snapshot. A parser
that drops entries quietly would turn the next diff into a report about itself
rather than about the feed.
Labels in rankdrift/labels.py carry a confidence field. Anything below high is
marked in the output; those are heads whose meaning is not pinned down in the
upstream code, and the label is an inference.
Values are derived from xai-org/x-algorithm
(Apache-2.0), specifically home-mixer/params/param.rs, home-mixer/params/config.rs
and home-mixer/scorers/ranking_scorer.rs. See NOTICE.
Not affiliated with, endorsed by, or sponsored by X Corp or X.AI Corp.
Licensed under Apache-2.0.

