- Sources read: 1
- Canon files updated: 1
- Radar entries: 0
1A vertical video carousel module was added to the For You feed — X
Source xai-org/x-algorithm@35650fb — xai-org, 2026-10-08T05:09:20Z, code. These notes number X commits in order after the Gen4 release, and this commit is Gen4+38. The previous commit is 78460ca (Gen4+37) from 2026-10-07. It changed 173 files, with +15,160/−2,465 lines.
Background In X's For You feed, several candidate sources collect posts, and Phoenix (a Grok-based Transformer ranker) predicts probabilities for each action such as like, reply, and video open. The value model then orders posts by a weighted sum of these probabilities. The blender in the last stage inserts non-post modules such as ads, Who-to-Follow (WTF) recommendations, and prompts into fixed slots.
This commit changes 15,000 lines, but much of it is internal code of the safety layer (visibility filtering, VF) and quack, a third-party GPU kernel utility of about 3,000 lines (CuTe DSL). The changes related to feed behavior are a new module for the blender, a changed default for the ranker training loss, and three new options for the candidate generation model (two-tower). Under the budget guard (the cap on how much a batch investigates; for large releases only the core is analyzed on the same day), the analysis centers on these three and leaves the rest to "What to check next".
What we learnedA. A vertical video carousel module was added to For You, and it is off by default
Three new parameters for the carousel entered the production default mirror verified —
home-mixer/params/param.rs.- The mirror file is
param.rsof home-mixer (the service that assembles the For You feed), and it copies X's internal feature switch defaults daily. - The three values are
EnableVideoCarousel = false,VideoCarouselFatigueMinutes = 60, andVideoCarouselPosition = 6. - The mirror sync time is
2026-10-07T16:00:42Z, and these three are the only values that changed in the mirror.
- The mirror file is
The carousel turns on under three conditions verified —
home-mixer/util/video_carousel.rs:7-11,query_hydrators/served_history_query_hydrator.rs:52-57, 110-126.- The request must be a For You request, the flag must be on, and 60 minutes must have passed since the last carousel impression.
- Fatigue is a cooldown that keeps a user from seeing the same module too often.
- This mechanism reuses what the WTF module and the feed survey already used. The unit is hours for the WTF module and minutes for the feed survey.
Carousel candidates are chosen in two stages verified —
selectors/top_k_score_selector.rs:10-27,filters/video_carousel_filter.rs,params/config.rs:17-19, 35-36.- The selection stage right after ranking originally keeps only the top 50 by score. When the carousel is on, it also keeps, among the dropped candidates, posts that are videos, are not replies, and have a
video_open(probability of opening the video) prediction, up to 30 in order ofvideo_openprobability. These retained candidates get avideo_carousel_extramark. - The retained candidates go through the VF filter and the conversation dedup filter in the same way. The last filter,
VideoCarouselFilter, pulls the extra candidates out of the main feed. Then, from the pool of "ranks 36 and below among the top 50 + the extracted extras", it picks only vertical videos (width-to-height ratio < 1.0) and puts up to 5 in the carousel invideo_openorder. If there are fewer than 3, no module is created.
- The selection stage right after ranking originally keeps only the top 50 by score. When the carousel is on, it also keeps, among the dropped candidates, posts that are videos, are not replies, and have a
The aspect ratio used to identify vertical videos is filled in by a new hydrator that reads it from media metadata verified —
candidate_hydrators/video_aspect_ratio_hydrator.rs:95-101.- The hydrator is named
VideoAspectRatioHydrator. - According to the test expectations in the same file, a 9:16 vertical video becomes 0.5625 and passes, while a 16:9 landscape video becomes 1.78 and a square becomes 1.0, and both are dropped.
- The hydrator is named
The blender inserts the carousel at the 6th slot after inserting ads, prompts, and WTF verified —
selectors/blender_selector.rs:72-85, 137-153.- It is recorded in the impression history as a
VIDEO_CAROUSEL_MODULEentry and is used for the fatigue check of the next request.
- It is recorded in the impression history as a
The key of the header string sent to the client is
VideoCarouselModule.header, and the test contains an English value verified —util/urt/video_carousel_marshaller.rs.header(Some("Videos for you".to_string()))The carousel videos appear to be designed not to overlap with posts shown in the main feed inferred. The basis is that the candidate pool consists only of ranks outside the main feed size (
RESULT_SIZE = 35) and the extras.- We did not check this time whether the final response is cut at exactly 35.
What we learnedB. The loss of the dwell time head in the default training setup changed to CREAD
The dwell time head is a regression head that predicts, in seconds, how long a user stays on a post. The value model multiplies this prediction by a weight and adds it to the score. The head's code name is dwell_time, and the weight name is ContDwellTimeWeight. Dwell time is concentrated at a few seconds and has a long tail, so simple regression such as mean squared error makes training unstable. So X has so far used the Tweedie loss together with MAE (absolute error). The Tweedie loss is a distributional loss fitted to positive values with a long tail.
CREAD is a method that Kuaishou published for predicting video watch time (CREAD: A Classification-Restoration Framework with Error Adaptive Discretization for Watch Time Prediction in Video Recommender Systems, 2024-01, paper). Instead of regressing time directly, it trains a binary classification at each of several thresholds: "did the time exceed this value?" It then chains the probabilities to restore the expected value.
- In the home default training config, the Tweedie and MAE weights became 0.0, and two CREAD losses were turned on at 0.1 verified —
phoenix/xrex/configs/xrecsys.py:272-275, 687-742.- This config is
_home_direct_packed_base(). - Previously, Tweedie (power 1.5) and MAE were each hardcoded to 0.1.
- This config is
- The two CREAD losses are split by surface verified (same file).
- The one for general surfaces has a cap of 120 seconds and excludes the gallery surface (
PRODUCT_SURFACE_GALLERY_PAGE). - The one for immersive (
dwell-time-cread-immersive) has a cap of 240 seconds and targets only the gallery surface. - The gallery surface appears to be a screen where users flip through photos and videos in full screen inferred. The basis is that the metric name is immersive and the dwell cap is doubled.
- The one for general surfaces has a cap of 120 seconds and excludes the gallery surface (
- The thresholds are 30 values at log intervals from 2 seconds up to the cap verified —
phoenix/xrex/models/loss_recsys.pycread_log_thresholds.- With a cap of 120 seconds, the thresholds are 2.0, 2.3, 2.65 … 90.5, 104.2, 120 seconds.
- The loss computes a sigmoid binary cross entropy at each threshold. The restored value is the sum of "probability of exceeding the threshold × interval width", and a Huber loss (δ=5 seconds, weight 0.5) between this value and the actual seconds is added verified —
cread_loss_computein the same file,_decode_creadinrecsys_model.py. - The paper's core contribution is to set the interval boundaries to fit the data so that the error becomes small (error adaptive discretization), but X's implementation uses fixed log intervals verified. In effect, X took only the name and the classification-restoration structure from the paper.
- The same threshold scheme is also used for input features. The option that feeds each post in the user history with its past dwell time as a bucket embedding is now on by default verified —
models/recsys_feature_prep.py:180-184, 885-910,configs/xrecsys.py:301-304.- The option is
enable_dwell_bucket_table=True. - The cap is 240 seconds, there are 30 thresholds, and it interpolates linearly between two adjacent buckets.
- Until now, the canon had not recorded in what form the dwell time of the history is fed as input.
- The option is
What we learnedC. A Mixture-of-Logits option was added to the two-tower model for candidate generation, and it is off by default
Two-tower retrieval is a structure that quickly finds candidates among hundreds of millions using the dot product of one user vector and one post vector. It is known that a single vector has difficulty capturing a user's interests when they span several directions. Mixture-of-Logits (MoL) is a method that reduces this limit by keeping several vectors on the user side and the item side and combining the scores of their pairings as a weighted sum through a small gate network.
- With the number of user query heads set to K, the user sequence output is attention-pooled with K learned query vectors to make K user vectors verified —
phoenix/xrex/models/recsys_two_tower_model.py:1629-1665.- The setting is
user_query_heads=K. - Each head learns its own decay coefficient that puts more weight on recent actions.
- The setting is
- With the number of post components set to L, a low-rank adapter is applied to the post embedding to make L component vectors. The final score mixes K×L cosines through an MLP gate (softmax) verified —
MolScorerat lines 523-598 of the same file.- The setting is
mol_item_components=L.
- The setting is
- A serving CUDA kernel was released as well, with fixed supported combinations and a fixed number of users per request verified —
phoenix/xrex/cuda/mol_epilogue/__init__.py:11-12.- The supported combinations are (K×L, gate hidden) ∈ {(4,32), (8,16), (8,32), (16,16)}.
- Up to 512 users per request are supported.
- Four training loss options were also added verified —
recsys_two_tower_model.py:2194-2224.- A fixed temperature
- False negative masking, for when the same post in a batch is picked as a negative
- A logit offset for posts on which the user took a negative action
- User weighting based on the number of positives
- All defaults are 0 or False, and none of the 6 released presets turns them on verified (grep of the configs directory).
- We cannot tell from this code whether production retrieval is already MoL inferred. The basis is that the experiment configs are not public. However, building even a serving kernel reads as a signal of preparing for deployment beyond the experiment stage inferred.
What we learnedOther changes
- topic feed requests are excluded from the new-author cold-start promotion verified —
home-mixer/scorers/author_cold_start.rs:384-386. - A function was added that looks up embeddings for the first 2-digit and 3-digit prefixes of a Semantic ID (an identifier that represents a post as a short code sequence) verified —
phoenix/xrex/models/recsys_sid.py:32-79.- We did not check whether this function is connected to the model.
- A path that records ads dropped by the ads blender (
home-mixer/ads/drops.rs) was added. VF introduced a 345-line user location lookup client and a 528-line hydrationexchange.rsverified.- Whether location-based verdicts are new is deferred to "What to check next" below.
Canon update services/x.md
- Feed surface section: added an entry for the video carousel module (off by default, conditions, position).
- Under the weight table in the ranking section: added a paragraph saying the
dwell_timetraining loss changed to CREAD. - "The final placement is not in score order" section: added the carousel insertion position and the blending order.
- Introduced a
### Gen4+38section. Added 4 deep dive items to the open question section and the reference commit35650fbto the sources section. assets/x-pipeline.svg: added "video carousel (off)" to the subtitle of the non-organic source box.
Why it matters
- Until now, every post slot in For You was ordered by the value model weighted sum. The carousel is filled by the probability of the single
video_openhead verified. It is the first post module that fills slots with an objective separate from the main feed ranking inferred. This is because the existing WTF and prompt modules did not use post ranking. The module re-exposes, with a separate objective, vertical videos that narrowly lost out in the main ranking. It appears to be an attempt to bring Reels- and Shorts-style vertical video consumption into the feed inferred. The basis is that its direction matches the video-only Thunder path (is_video_request) recorded in the canon. Thunder is a candidate source that collects the latest posts of accounts the user follows (in-network). - The change to how the dwell time head is trained went into the default config, so it is likely to apply from the next checkpoint inferred. Whether a default change is reflected in the actual production training config is not public, so we leave it as an inference. This change is in the same direction as the commit (Gen4+22) that changed
click_dwell_timefrom regression to a 10-second threshold binary classification. The trend of turning regression into classification to be robust to tail distributions has extended todwell_timeinferred.
What to check next
- Check the final response size of the video carousel. See whether the main feed is cut at exactly 35, and raise the overlap between the carousel and the main feed to verified.
- Check whether a rule that uses user location as a verdict input is new in VF — if it is a policy change, it must be folded into the safety section of the canon.
- The files to look at are
hydration/sources/exchange.rsandclients/user_location_client.rsin VF.
- The files to look at are
- A hydration scheduler for the offline rank-all job is new. It needs a deep dive.
- The path is
phoenix-rankall/src/hydrate/*(about 1,300 lines).
- The path is
- Check the mirror daily to see whether
EnableVideoCarouseland the MoL options get turned on.
Source check notes
No new material to fold in came from Meta, Netflix, or arXiv cs.IR.
- Meta: the latest post on engineering.fb.com is still "NTS: Authenticated Time at Meta" from 2026-10-06, which is unrelated to feeds. The latest post on the first page of ai.meta.com/blog is from 2026-07, so there is nothing new. The WebSearch cross-check also found no new primary material on Instagram, Threads, or Facebook ranking.
- Netflix: the latest post in the netflixtechblog.com RSS is from 2026-09-25, so there is nothing new. The recommendations area page of research.netflix.com also opened, and nothing was new.
- arXiv cs.IR: 30 papers were parsed, and 0 passed the acceptance criteria. Two came close.
- 2610.10483 is a NeurIPS 2026 paper on correcting sampling bias in two-level softmax. It has no production evidence.
- 2610.10124 covers how to train on missed targets in generative recommendation and experimented with the public OneRec model. It has no platform authors or deployment claims.