starl3xx.fun / algo / 006

Issue № 006 (commit b412112)

A cold-start lift for a post’s first two hours from accounts up to 50,000 followers, a fresh-post retrieval pool switched on, click credit moved to the time after the click, a heavier “not interested”, out-of-network replies built and left off, and a Brazil-only label for AI election media

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 4c5cfe8 to b412112, which is 5 commits, dated 29 September, 30 September, 1 October, 2 October, 3 October.

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

  • Post when your followers are around, and give them a reason to like it early. For accounts up to 50000 followers, the one cold-start lift in each feed build now goes only to a post under two hours old with fewer than 200 Home views, and it picks among those by their likes against those views, not by score alone.
  • Make the click worth staying for. A click into a post now weighs 0.3, down from 0.4, and the time a viewer is predicted to stay after that click, weighted 0.0 until this range, now carries 0.4, the same direction as issue 001’s switch to dwell.
  • Keep earning a reply from people who follow you. The code that would let a reply reach people who don’t is new this week, and its switch, EnablePhoenixOonReplies, is false, so a reply from an account the viewer doesn’t follow is still dropped.

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

What moved

ClickWeight0.4 → 0.3
ContClickDwellTimeWeight0.0 → 0.4
NotInterestedWeight-43.2 → -47.52
ColdStartFollowerCap1000 → 50000
ColdStartMaxPostAgeSecs172800 → 7200
ColdStartImpressionThreshold1000 → 200
LowImpressionsMaxPositionRatio0.85 → 0.97
PhoenixColdStartMaxResults0 → 200

1. The cold-start lift now reaches accounts up to 50,000 followers, and only in a post’s first two hours. Once in each feed build, author_cold_start.rs raises one eligible post to the score of the candidate ranked 16th in that build, if it was below it (ColdStartSlotMin 15 and ColdStartSlotMax 16, unchanged, so the target is always that place). Four limits on which post qualifies moved in param.rs. The author’s follower cap, ColdStartFollowerCap, went from 1000 to 50000. The post’s age limit, ColdStartMaxPostAgeSecs, went from 172800 to 7200 seconds: 48 hours before, two now. The ceiling on its Home timeline views, ColdStartImpressionThreshold, went from 1000 to 200. And the share of the scored candidates it must already rank within, LowImpressionsMaxPositionRatio, went from 0.85 to 0.97, so only the bottom three percent is now out of reach. It still has to be an original post, not a reply or a repost. Then how the post is chosen changed: EnableColdStartThompsonSampling went from false to true. Before, the lift went to the eligible post with the highest score. Now each eligible post draws a likely like rate from its likes counted against its Home views, starting from ColdStartBetaAlpha0 0.75 and ColdStartBetaBeta0 49.25, a like rate of 1.5% before it has any views; the two highest draws go forward (ColdStartTsTopK 2), and the higher-scored of those two is lifted. Expect to read “X now boosts accounts under 50,000 followers.” It is one post per feed build, raised to sixteenth place at most, inside a two-hour, 200-view window; and for an account under 1,000 followers, the window that used to last two days now lasts two hours. Takeaway under 50,000 followers, a post’s chance at the lift is its first two hours and first 200 Home views, and the likes it earns in that window now help pick it, so post when your followers are online, not when they are asleep

2. A pool of fresh posts is switched on in retrieval. PhoenixColdStartMaxResults went from 0 to 200, so phoenix_source.rs, which retrieves candidates for For You, now asks Phoenix retrieval for up to 200 posts from its cold slice alongside its usual results. In the repo’s retrieval code, retrieval_dataset.py builds that slice from the home index’s posts with fewer than 500 views and fewer than 8 likes (cold_pool_mask in cold_pool_filter.py), cut by post age, and the model’s config defaults that age to DEFAULT_COLD_START_MAX_AGE_SECONDS, 2 * 60 * 60 seconds. A week ago a post from that pool was zeroed for every viewer outside an experiment’s treatment arm, and the cap was 0 anyway. This week the function that zeroed it, apply_moe_ranking_policy, is deleted from author_cold_start.rs, and the ranking service lost the matching step in scoring.rs. Nothing gives these posts a weight of their own: they are scored on the same ladder as everything else, and they can take the lift from item 1 like any other post. Takeaway by default, a new post with little engagement yet now has a second way into other people’s For You candidates, which points the same way as item 1

3. Credit moved from the click to the time after it. ClickWeight went from 0.4 to 0.3, and ContClickDwellTimeWeight went from 0.0 to 0.4, in both param.rs and the ranking service’s params.rs, which still agree on every shared default. scoring.rs multiplies that new weight by click_dwell_time, Phoenix’s prediction of how long the viewer stays after clicking into the post. That prediction is a length of time, not a probability, so 0.4 on it is not “four-fifths of a like”: the file’s own comment says the weights multiply a predicted probability “or a continuous value”. It is the same direction as issue 001, when binary dwell was switched on. Takeaway a post that gets clicked and then read is worth more than it was a week ago, and one that gets clicked and abandoned is worth less

4. “Not interested” weighs a tenth more. NotInterestedWeight went from -43.2 to -47.52, exactly 1.1 times as heavy, in both param.rs and params.rs. Like every weight on the ladder, it multiplies a viewer’s predicted probability of tapping “not interested” on the post, not a count of taps, and that probability is driven largely by the viewer’s own history. Block, mute, and report did not move. Takeaway the cost lands on posts a viewer is predicted to dismiss, which is a reason to keep posting about what your followers came for rather than chasing a subject they skip

5. Out-of-network replies are built and switched off. A new param, EnablePhoenixOonReplies, is false. If it were on, oon_retweet_reply_filter.rs would stop dropping a reply from an account the viewer doesn’t follow when Phoenix retrieval found it, and a new hydrator, phoenix_reply_ancestors_hydrator.rs, would fetch each such reply’s parent and the post that started the conversation, so the checks that read a reply’s chain, the Brazil filter and the visibility check in vf_candidate_hydrator.rs, could see them. Reposts from accounts the viewer doesn’t follow would still be dropped either way. At the default, nothing changes: a reply from an account the viewer doesn’t follow is removed, as it was last week. Expect “X now shows your replies to strangers” anyway, from anyone who reads the filter’s diff without the param. Takeaway replies still earn their weight from your followers; if the switch is ever turned on, a reply could reach strangers through retrieval, and nothing in this range gives it a different weight when it does

6. Brazil: nine more accounts, and a new label that applies only in Brazil. In brazil_2026_election_filter.rs the test that counts the listed accounts went from 2786 to 2795. That filter matches accounts and applies to viewers everywhere, as issue 005 described. New in this range is a post label, BRAZIL_ELECTION_LEGAL, which the repo’s label glossary, underTheHoodLabels.strato, describes as a post “that may contain AI generated media related to electoral processes in violation of Brazil local law during silence period”, citing Superior Electoral Court Resolution 23.610, with the effect “In Brazil, post hidden from recommendations.” The rule in tweet_rules.rs drops a labeled post at the recommendations level when the viewer’s request comes from Brazil, for everyone but its author. The test corpus in oon_tweet_label.rs pins it: dropped for a viewer in Brazil, allowed for a viewer in the US, and allowed at the level home-mixer uses for accounts the viewer follows. Takeaway the account list follows a listed candidate’s posts everywhere, the new label follows one post only into Brazil, and neither touches a post that just discusses the election

Also in this range, and not worth an item each. Three new switches are off. VMRankerSendPacingInputs false would send each post’s view count and its author’s follower count to the ranking service, and nothing in the repo’s service reads them yet. PhoenixRetrievalExcludeSeenPosts false would ask retrieval to skip posts the viewer has already seen. And two new candidate sources are written and off: sid_source.rs (EnableSidSource false) would find posts close to ones the viewer engaged with, and popular_posts_source.rs (EnablePopularPostsSource false) would pull up to 3 recent original posts each from the 1000 most-followed accounts on a stored list, with no replies or reposts. The ranking service gained AuthorExplorationBonus in its params.rs, an amount value_model.rs adds to every post by an author before the multipliers, assigned by experiment rules keyed on the author; its default is 0.0, so it adds nothing, and the repo does not show which authors any experiment would pick. EnableAdsBrandSafetyVerdictV2 went from false to true and EnableAdsAuthorBrandSafetyFallback is new at false; both decide which posts an ad may sit next to in ads_brand_safety_vf_hydrator.rs, and neither changes a post’s score. UseEngagementCounterViewCountForImpressionBoost was removed, and nothing read it a week ago; FeedSurveyFatigueHours 24 became FeedSurveyFatigueMinutes 1440, the same day. The visibility filter’s rules were rewritten again, with new names; in its test corpus no existing For You verdict changed, and the new cases for age verification and local regulations sit at the level for followed accounts and at a hydration level, not at the recommendations level. A new service, abuse-ledger-service, keeps an enforcement that was overturned on appeal from being applied again for a fixed period, per the repo’s README.md, which also now dates the Brazil list September 29.

The standing numbers

For anyone new here, the weights that matter most didn’t move this week. From param.rs, and declared again with the same defaults in the service’s params.rs, each one against a like:

  • share via copy link: 20.0, forty times a like
  • reply between two accounts that follow each other: 5.0 plus a 15.0 boost, also forty times
  • reply or quote: 5.0, ten times
  • like: 0.5, the unit

Nothing older than 48 hours enters the For You feed at all: MAX_POST_AGE in config.rs is 48 * 60 * 60 seconds. A reply or a repost from an account a viewer doesn’t follow is dropped outright by oon_retweet_reply_filter.rs at its default, so replies earn their weight from your existing followers rather than from new reach. A quote carries the same weight and does travel out of network. And where the ranking service applies them, two multipliers follow the sum: the repeat-author factor, which keeps 0.625 of your second-best post and 0.4375 of your third-best, and OonWeightFactor 0.75, on a post from an account the viewer does not follow and on a reply or repost from one they do.

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, params.rs, author_cold_start.rs, phoenix_source.rs, retrieval_dataset.py, cold_pool_filter.py, scoring.rs, value_model.rs, oon_retweet_reply_filter.rs, phoenix_reply_ancestors_hydrator.rs, vf_candidate_hydrator.rs, brazil_2026_election_filter.rs, underTheHoodLabels.strato, tweet_rules.rs, oon_tweet_label.rs, sid_source.rs, popular_posts_source.rs, ads_brand_safety_vf_hydrator.rs, README.md, all at b412112.