starl3xx.fun / algo / 003

Issue № 003 (commit 6bb4594)

Per-viewer jitter on every engagement weight, the shape of your text as a model input, line breaks, external links, and the quoted post scored on its own content

X Algo Watch

Every Sunday I diff the open-source X ranking code, xai-org/x-algorithm, against last week’s mirror and write down what actually moved. This issue covers 902a06f to 6bb4594, which is 4 commits, dated 8 September, 9 September, 10 September, 12 September.

Everything below is a default in the code, not a claim about production. The repo’s own README says the two can differ. Every number is copied from the diff; where I’m inferring what a change means for posting, I say so.

If you change one thing this week

  • Stop tuning to the exact ratios. The scorer can now multiply every one of the 26 engagement weights by e to the plus or minus sigma, per viewer, so the gap between a reply and a like is not the same for every reader. The default is 0.0, so it is off today.
  • Your post’s shape is now an input, not just its text. Weighted length, line-break count, whether there is an external link, how many media items and how long the video runs are all sent to the model, along with the same set for the post you quote. None of it carries a weight: no scorer in the open-source code reads it yet.

Everything below is the evidence for those two, plus what else moved.

What moved

Not one weight changed value this week. The ladder in param.rs is exactly where it was. What moved is the machinery around it, and one of the two changes is the reason a ladder was never quite the right picture.

1. Every engagement weight can now be jittered per viewer. ranking_scorer.rs gained a perturbed step, and it runs on every scoring pass: ScoringWeights::from_params(&query.params).perturbed(query). When WeightPerturbationSigma is above zero, each of the 26 weights is multiplied by e raised to sigma times plus or minus one, and the sign comes from md5 of salt:user_id:head. So the direction is fixed per viewer and per weight, not random per request: the same reader gets the same tilted ladder every time, and two readers get different ones. The positive and negative sums are recomputed afterwards, so the normalization follows the jitter rather than fighting it. Both new switches are off by default, WeightPerturbationSigma at 0.0 and WeightPerturbationSalt empty. Takeaway treat the published weights as a center point rather than a ladder, because the code can now spread them per reader without any number in this issue changing

2. The model now sees the shape of your post, and of the post you quote. content_features.rs is new, and it builds a feature set straight off the candidate: whether there is video and how long it runs, whether there is a photo, how many media items, the weighted text length, the number of line breaks, and whether there is an external link. Weighted length counts a URL as 23 regardless of its real length, skin-tone modifiers and variation selectors as 0, characters in four codepoint ranges as 1, and everything else, which is most emoji and most non-Latin script, as 2. An external link is only counted when it is not the media attachment, so a photo does not read as a link. build_quoted runs the same construction over the quoted post, and quote_hydrator.rs now fetches the quoted post’s text and media to feed it, on a 200 millisecond budget. candidate.rs attaches both sets to the candidate and they go out on the wire as proto field 36, but ranking_scorer.rs does not read them: they are inputs to the model, not a new rung on the ladder. Takeaway line breaks and an external link are now explicit inputs rather than incidental, and quoting puts the other post’s own content in front of the model alongside yours, with no weight attached to any of it

3. Retrieval moved to a second experiment cluster. In param.rs, PhoenixRetrievalMOEInferenceClusterId went from "Experiment1Fou" to "Experiment2Memy04". This is a deployment target, not a behavior: it says which cluster serves the retrieval model, not what the model does. PhoenixInferenceClusterId, the ranking side, is untouched at "Experiment1Fou". Takeaway nothing to act on, and worth recording only because a retrieval change is where a feed shifts without a weight moving

Also in this range, and not folded in above: a new clock_cache.rs of 446 lines and heavy work across the visibility-filtering hydrators, a brazil_2026_election_filter.rs grown by 334 lines, and a large multimodal-embedding effort under mm_emb aimed at search rather than the timeline. None of it changes a ranking default.

The standing numbers

For anyone new here, read from param.rs at this commit: a share via copy link 20.0, a reply between two accounts that follow each other, on top of the reply weight 15.0, a reply 5.0, a quote 5.0, a like 0.5, each extra post of yours in one viewer’s feed build 0.5, and that decay stops here 0.25. From config.rs, MAX_POST_AGE is 48 * 60 * 60 seconds, which is 48 hours, so nothing older than that enters the For You feed at all.

Caveats

These are defaults in open-source code, overridable per experiment at runtime. The ranking model itself (Phoenix) is trained, not rule-based; the weights blend its per-viewer predictions and don’t describe what it learned. Reading weights as tactics is inherently lossy. I’d rather you know that than not.

Sources: param.rs, config.rs, ranking_scorer.rs, brazil_2026_election_filter.rs, vf_filter.rs, core_data_candidate_hydrator.rs, filtered_topics_hydrator.rs, candidate.rs, content_features.rs, quote_hydrator.rs, clock_cache.rs, config.rs, config.py, data_types.py, all at 6bb4594.