Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rankdrift

Track the ranking weights behind X's For You feed, and see when they move.

日本語版 README

Successive snapshots of the ranking weights, each one offset from the snapshot before it along a time axis

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.

What the current table says

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.

Phoenix action weights from the 2026-08-12 sync. Positive: share_via_copy_link 20, reply 5, quote 5, share_via_dm 5, follow_author 4, share 2, retweet 1, favorite 0.5, click 0.4, open_link 0.2, photo_expand 0.05, video_open 0.05, dwell_time 0.004, and profile_click, dwell, click_dwell_time and quoted_vqv all at 0. Negative: report -234, mute_author -58.8, not_interested -43.2, block_author -31.2, not_dwelled -0.02

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.

Usage

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.json

To 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.html

The 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.

How to read these numbers

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.

Attribution

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.

About

Extract the ranking weights behind X's For You feed, and diff them between snapshots

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages