- Sources read: 4
- Canon files updated: 2 (services/x.md, radar.md)
- Radar entries: 2
Other changes reviewed today:
- In the same X commit, the VM Ranker (a remote service that re-sorts a bundle of candidates) path drops the local diversity computation and now uses the scores the server returns as they are.
- X's Following feed also filters out posts that quote or retweet an original author who has blocked the viewer.
- There were no new sources for Meta and Netflix. On arXiv, WeChat's Tlow and TAGR from an anonymous live-ads platform passed the radar.
1Scorers can now request account actions directly — X
Source xai-org/x-algorithm@0d3cdd8 — xai-org public repository, commit of 2026-08-25T23:20:04Z (47 files, +5,850/−586), public source code. These notes number the X commits after the Gen4 release in sequence, and this commit is Gen4+8. The commit message is only the single line "Open-source X Recommendation Algorithm", as is the convention of the generation series, so the commit content was read only from the file diffs.
Background The Gen4 open-source release of the X For You timeline has had follow-up commits at one- to three-day intervals since the first release on 2026-08-13 (47c1bcd, +363,246/−11,640). This commit landed at 08-25 23:20 UTC; yesterday's (08-26) batch missed it, and today's 27th batch caught up. Whereas the previous commit, Gen4+7 (d011592), deepened the safety stack vertically, Gen4+8 touches six areas side by side.
- Gen4+7 deepened the safety stack in three ways:
- The VF (visibility filtering, a safety layer that decides whether content can be shown) verification reference_compare
- The brand-safety tier extension HighRisk
- PTOS (policy-based violation category verdict) judge, 3 categories
- The six areas Gen4+8 touched are enforcement, the ranker, Following, Brazil compliance, Phoenix rankall, and Phoenix training infrastructure.
- Phoenix is a Grok-based Transformer that predicts the engagement probability of each candidate in X For You.
- By scale (5,850 lines) it is the largest since Gen4+4 (+3,303). However, 3,379 of those lines are the account data update of the Brazil 2026 election filter.
- It is a release that is wide because new connections went into 6 subsystems side by side, rather than because the code is new.
This commit (0d3cdd8, 2026-08-25, +5,850/−586, 47 files) opened a path for scorers to request account actions directly, and also touched the ranker, Following, the Brazil filter's account list, and the Phoenix index and training infrastructure.
The README heading also changed.
## Latest Updatesbecame## Notable Updates.- Under it, the Brazil item in the August 14th section gained the text
*(Account list updated August 25, 2026.)*. - We interpret this heading change as meaning that the section will be kept focused on notable changes rather than always kept current inferred. With follow-up commits arriving daily, it declares that the README will not be updated every day.
1-1. A general path for scorers to request account actions directly was added
AES (abuse-enforcement-service) now accepts and runs action requests that scorers attach directly, in addition to static YAML rules. Only requests that pass the action kinds fixed in code and the dynamic allowlist run, and rejected requests are logged with a reason.
Background abuse-enforcement-service/ (AES) is a sub-repo that was fully revealed at the first Gen4 release. AES is a rule engine that takes model scores and maps them to actual account actions.
- Examples of the model scores it receives are the BDSM (fraudulent-account detection over account behavior sequences) bot head, the LLM slop label, and PandaReports.
- The account actions are label / bounce_captcha / bounce_arkose / spam_liveness_check / suspend.
Previously, the AES rules were statically encoded in abuse-enforcement-service/service-lib/rules/enforcement_user.yaml and enforcement_post.yaml. The approach states explicitly, as CEL expressions, a mapping of which action to take when a label is attached. For example:
- id: anchor_campaign_suspend_cse
when: '"anchor_campaign_suspend_cse" in score.labels'
then: { kind: act_suspend_user, perm: true, policy: "Cse" }
This approach had the limitation that the AES YAML had to be edited every time a new scorer was added. Gen4+8 broke that coupling.
If a scorer attaches a list of requests directly, AES runs only those that pass the fixed allowlist.
- This general passthrough is connected by
generic_actions.rs(663 lines, new) and two corresponding YAML rules (act_requested_actionsandplatform_row_without_requested_actions). - The full path of the new file is
abuse-enforcement-service/service-lib/src/generic_actions.rs, and all 663 lines are new. - The request list a scorer attaches is the
requested_actionslist.
- This general passthrough is connected by
The action kinds a scorer can request are fixed as code constants.
Only 5 kinds can be requested for the user entity and 2 for the post entity. A client cannot invent an arbitrary kind string.
At most 16 actions can be requested per message.
pub const USER_KINDS: &[&str] = &[ "suspend", "label", "bounce_captcha", "bounce_arkose", "spam_liveness_check", ]; pub const POST_KINDS: &[&str] = &["post_label", "suspend_author"]; pub const MAX_REQUESTED_ACTIONS_PER_MESSAGE: usize = 16;
Requests go through a double fail-closed (reject if a condition is not met) verification. The verification in
action_to_spec(entity_type, action, allowlist)has the following five steps:- The request must pass
hardcoded_kinds. If it does not, the result is"unknown_kind", and if the kind belongs to a different entity type, it is distinguished as"entity_type_mismatch". - The kind must be in the dynamic
GenericActionAllowlist.kinds. If it is not, the result is"kind_not_allowlisted". - For the
suspendorsuspend_authorkind, the policy must be inallowlist.suspend_policies. If it is not, the result is"policy_not_allowlisted". - For the
labelorpost_labelkind, all three conditions must be met.action.labelsmust not be empty ("no_labels").- Every label must be in
allowlist.labels("label_not_allowlisted"). ttl_msec >= 0must hold ("invalid_ttl").
- Duplicates are removed. If the same spec is already in the list, the result is
"duplicate_action".
- The request must pass
Rejected actions are not simply discarded but observed.
- Rejected actions are collected into a
SkippedRequestedAction { kind, head, reason, count }struct and attached as JSON tooutcome.info["requested_actions_skipped"]. - They are also aggregated in a separate metric,
GENERIC_ACTION_TOTAL{entity_type, kind, outcome=resolved|skipped, reason}. - In other words, the reason a request failed to pass is left in the log as is. The scorer team can immediately see why its request was rejected.
- Rejected actions are collected into a
Two YAML rules were added as a pair to enforcement_user.yaml.
- id: platform_row_without_requested_actions
when: '"abuse_platform_requested_actions" in score.labels && size(score.requested_actions) == 0'
then:
kind: skip
reason: platform_row_without_requested_actions
- id: act_requested_actions
when: "size(score.requested_actions) > 0"
then:
kind: act_requested_actions
- If a scorer has declared the general action pipeline with its own label (
abuse_platform_requested_actions) but the actual action list is empty, the request is skipped. - If there is at least one action, it is routed to the verification pipeline above through the
act_requested_actionsdecision. enforcement_post.yamlgained only one symmetric rule; the post side has no platform_row skip rule.
The AES decision also gained a new variant that handles the request list.
A new
Decision::ActRequestedActionsvariant was added toabuse-enforcement-service/service-lib/src/lib.rs(+276/−19).When
run_enforcement_innermeets this variant, it handles it as follows:let (decision, mut requested_actions_skipped) = match decision { Decision::ActRequestedActions => expand_requested_actions_decision( facts.entity_type, &facts.score, &ctx.dynamic_config.generic_action_allowlist(facts.entity_type), ), Decision::Skip(reason) => (ExpandedDecision::Skip(reason), None), Decision::Act(specs) => (ExpandedDecision::Act(specs), None), };If no action passes the allowlist, the result is
Skip(REQUESTED_ACTIONS_DENIED), and if at least one does, it isAct(specs).In other words, even when all requests are rejected, the outcome is explicitly skip, so what was requested and why it was rejected remain in the JSON in outcome.info.
A path from dynamic_config to generic_action_allowlist was also added.
- A new method was added to
growthbook.rs(+126). - GrowthBook appears to be X's dynamic config and feature-flag system inferred.
- If so, the allowlist, that is, which kinds a scorer may request and which policies and labels it may use, can be gated by A/B at runtime.
- When attaching a new scorer, allowlist entries can be switched on and off in the GrowthBook console without deploying code.
The first client of this structure, panda_reports_embedding_v10_rough_spam, came in with it. A new rule was added to enforcement_user.yaml.
- id: panda_reports_embedding_v10_rough_spam
when: '"panda_reports_embedding_v10_rough_spam" in score.labels'
then: { kind: act_suspend_user, perm: false, policy: "PlatformManipulation" }
- This rule itself is a static mapping in the old style. However, the label name itself carries new information.
pandaappears to be the name of X's internal spam detection system inferred.reports_embedding_v10is the 10th version of a detector based on report embeddings.rough_spamis a verdict that the account is likely spam, though with low confidence.
- The action is a temporary (perm: false) suspend with the PlatformManipulation policy.
- In other words, this detector is embedding-based, and its version has reached v10. It can be seen as a continually retrained pipeline.
Canon update Three places in services/x.md were edited.
- Added a Gen4+8 row to the Gen4 table.
- Expanded the one-line summary paragraph to describe the Gen4+8 commit.
- Added a subsection "Gen4+8 (2026-08-25) — AES
generic_actionspassthrough wiring" after the BDSM section.
Why it matters The meaning of this change is summarized at the end of 1-7, together with the three axes of the whole Gen4+8 release.
1-2. VM Ranker drops local computation and uses the scores the server returns as they are
In the VM Ranker path, the local computation layer that home-mixer (the service that serves X home timeline feed requests such as For You and Following) used to run is gone. Now home-mixer uses the scores returned by the rank server as they are, and diversity is handled by the server-side DPP (a diversity re-ranking technique).
Background VMRanker is a remote gRPC service that acts as the slate reranker in home-mixer. It performs DPP (Determinantal Point Process)-based diversity re-ranking over the candidate scores that Phoenix produced, with parameters θ=0.65, K=150. Until now, home-mixer/scorers/vm_ranker.rs supported two value models.
- For
author_diversity, home-mixer computed locally a value that applies author-diversity decay based on the MPN fold-back and passed it to the rank server. - For default, the score returned by the rank server was used as it was.
- The 7 SID field connections of Gen4+3 (2026-08-18) were attached to this vm_ranker. SID (semantic ID) represents an item as a sequence of discrete token IDs.
Gen4+8 greatly simplified this path. It removed the 88 lines of ranking_scorer::effective_head_weights and the 128 lines of MPN fold-back.
The author-diversity value model and the MPN fold-back logic were deleted from the VM Ranker scorer (
home-mixer/scorers/vm_ranker.rs, +11/−128).The deleted code:
// Deleted: const AUTHOR_DIVERSITY_VALUE_MODEL_ID: &str = "author_diversity"; const MPN_FOLD_MULTIPLIER_MAX: f64 = 10.0; pub struct VMRanker { pub client: Arc<dyn VMRankerClient>, pub xds_client: Option<Arc<dyn VMRankerClient>>, pub author_cold_start: AuthorColdStart, // deleted } // entire MPN fold-back logic inside score() deleted: let fold_weights = (query.params.get(EnableMpnScoring) && query.params.get(VMRankerValueModelId) == AUTHOR_DIVERSITY_VALUE_MODEL_ID) .then(|| ScoringWeights::from_params(&query.params)); // ...computes MpnParts.pos/neg × (returned/sent).clamp(0, MPN_FOLD_MULTIPLIER_MAX) // ...post-processing with AuthorColdStart.apply
The only newly added constant is
const DPP_VALUE_MODEL_ID: &str = "dpp";. That is, one value model ID (author_diversity) was deleted and replaced with another (dpp).The new score() passes the score returned by VM Ranker straight through and does not recompute it locally. Both the MPN fold-back and the AuthorColdStart post-processing are gone.
candidates.iter().map(|c| Ok(PostCandidate { score: score_map.get(&c.tweet_id).copied().or(c.score), ..Default::default() })).collect()The 88-line method that combined weights for each candidate and sent them to VMRanker was also deleted (
home-mixer/scorers/ranking_scorer.rs, +28/−201).- The deleted method is
ScoringWeights::effective_head_weights. - This method was logic that built an
xai_vm_ranker_proto::HeadWeightsprotobuf for each candidate and sent it to VMRanker. - It combined weights per candidate, for example the post_unexplored multiplier computation, the low-fav-rate penalty on click_dwell_time, the quoted_vqv duration check, and the per-candidate adjustment of the reply weight, and all of it is now gone.
- The deleted method is
This simplification has four implications:
- Diversity computation moved from the client, home-mixer, to the server, VMRanker.
- Previously, home-mixer built head weights carrying the diversity requirements for each candidate and sent them to the rank server.
- Now the rank server handles diversity with its own DPP logic, and home-mixer only consumes the returned scores.
- What the rename from
author_diversitytodppactually amounts to is still unclear.- This commit alone cannot settle whether
dppreally refers to the DPP of the existing re-ranking section (θ=0.65, K=150) or to a different DPP instance used only inside the rank server inferred. - Traces of tidying up the VMRankerValueModelId parameter also remain in
home-mixer/params/param.rs(a net decrease, +10/−29).
- This commit alone cannot settle whether
AuthorColdStartdid not disappear; it changed place.- The file
home-mixer/scorers/author_cold_start.rsstill exists and was modified by +12/−9. - Because it was removed only from the VMRanker post-processing, it may still be applied at other points such as RankingScorer or the Phoenix pipeline.
- The file
- The values the deleted
effective_head_weightscomputed now appear to be computed by the server with its own logic, or the concept of head weights itself appears to have been replaced in another way.- The head weights this method computed are favorite, reply, retweet, share, dwell, quoted_vqv, and so on.
- It is more natural to interpret that VMRanker consumes the original phoenix score as it is and applies only its own DPP inferred.
This simplification extends the main direction of Gen4, that is, the trend of stripping local computation and caches from home-mixer and preferring served outputs. It pairs with how the UseServedSlateContext gate of Gen4+5 moved slate context computation from local to Phoenix serving.
"AUTHOR_DIVERSITY_VALUE_MODEL_ID: &str = "author_diversity"" ← deleted "DPP_VALUE_MODEL_ID: &str = "dpp"" ← new —
home-mixer/scorers/vm_ranker.rs
Canon update Three places in services/x.md were edited.
- Reflected the value model rename and the deletion of effective_head_weights in the VMRanker description of the ranking section.
- Reflected in the diversity section that DPP became the only value model of VMRanker.
- Recorded the parameter −29 lines (cleanup of MPN and author_diversity constants) in the parameter change practice.
1-3. The Following feed also follows blocks by quoted and retweeted original authors
The Following feed extends its block-relationship check to quoted authors and retweeted authors. If the original author has blocked the viewer, the viewer does not see the post even when someone the viewer follows quotes or retweets it.
Background The Following surface is a reverse-chron pipeline that shows only the latest in-network posts. Since Following got its own VF hydrator branch in Gen4+3, this pipeline has kept being refined. This time, work was added to extend block-relationship handling to quoted authors and retweeted authors.
The new hydrator (a stage that fills in fields on each candidate),
FollowingBlockedByHydrator, asks SocialGraphClient which of the quoted author and the retweeted author is blocking the viewer.The file is
home-mixer/candidate_hydrators/following_blocked_by_hydrator.rs(65 lines, new).For each candidate, it collects the user_id lists of the quoted author and the retweeted author and asks in one call.
let user_ids: Vec<u64> = candidates .iter() .flat_map(|c| c.quoted_user_id.into_iter().chain(c.retweeted_user_id)) .collect(); let blocked_by_user_ids = self.socialgraph_client .check_blocked_by(query.user_id, &user_ids) .await?;The result is set on two candidate fields.
author_blocks_vieweris true if the retweeted author blocks the viewer.quoted_author_blocks_vieweris true if the quoted author blocks the viewer.
A new condition was wired into
AuthorSocialgraphFilter, a filter already in Gen4, and it now removes the candidate if any of 6 conditions is true.The filter file is
home-mixer/filters/author_socialgraph_filter.rs.Previously, it checked only block and mute from the viewer toward the author. Now it expands to six branches, including the direction from author to viewer and the original authors of quote and retweet chains.
if muted // viewer mutes the author || blocked // viewer blocks the author || author_blocks_viewer // author (= retweeted author) blocks the viewer || quoted_author_blocks_viewer // quoted author blocks the viewer || viewer_blocks_quoted_author // viewer blocks the quoted author || viewer_blocks_retweeted_user // viewer blocks the retweeted author { removed.push(candidate); } else { kept.push(candidate); }
The two stages were wired into the reverse-chron pipeline, and the filter was placed before the VF filter (
home-mixer/candidate_pipeline/reverse_chron_posts_pipeline.rs, +27/−2).let post_selection_hydrators = vec![ Box::new(FollowingBlockedByHydrator::new(socialgraph_client).await), // new Box::new(VFFollowingCandidateHydrator::new(...)), // ... ]; let post_selection_filters = vec![ Box::new(AuthorSocialgraphFilter), // new Box::new(VFFilter), Box::new(AncillaryVFFilter), ];AuthorSocialgraphFiltersits before the VF filter. That is, candidates filtered out by block or mute skip the VF verdict lookup itself, so performance also improves.SocialGraphClientis a client in the XAI candidate_pipeline component_library. It connects to flock over TLS S2S, and the service itself is not public.
This change has three implications:
- The meaning of a block has widened transitively.
- Previously it was "if you do not follow A, A's posts are not shown to you", but now it extends to "even if you follow A, if A retweeted original author B and B has blocked you, the post is not shown".
- B's block of you is respected regardless of the following relationship.
- It is a broad-scope policy decision, and we interpret it as a signal that X has started to explicitly block bypass exposure through quotes and retweets inferred.
- On performance, one more remote lookup to SocialGraphClient is added per request.
- However, because it is a batch lookup that asks
check_blocked_byfor the quoted and retweeted user_ids of all candidates at once, the cost grows sub-linearly with the number of candidates rather than being fixed.
- However, because it is a batch lookup that asks
- This hydrator is wired only into Following.
- The hydrator list of the For You (algorithmic) pipeline does not yet include this hydrator.
- If the wiring is symmetric, it may be attached to For You in the next commit inferred.
Canon update Two places in services/x.md were edited.
- Added a block-extension subsection to the Following surface description.
- Updated the
AuthorSocialgraphFilterdetail in the 21-filter table. It is a filter that already existed, but the update reflects that its meaning was extended.
1-4. The Brazil election filter's account list grew substantially from the TSE source data
The account list of the Brazil 2026 election filter gained +3,379 lines based on public data from the Brazilian electoral authority. The filter's behavior itself did not change.
- The account list in the file
home-mixer/filters/brazil_2026_election_filter.rsof the filterBrazil2026ElectionFiltergrew substantially (+3,379/−5, 4,947 lines in the whole file). - The README notice also states this update: "Account list updated August 25, 2026."
- The data source is dadosabertos.tse.jus.br/dataset/candidatos-2026. It is the 2026 election candidate dataset published by the Brazilian electoral court (TSE).
The filter logic is unchanged from its introduction in Gen4+1 and works in three steps.
- It hardcodes the list of reported accounts from
dadosabertos.tse.jus.br. - It removes posts from these accounts in the For You pipeline.
- It makes an exception for accounts the viewer explicitly follows.
This is a measure to comply with Art. 28 § 1º-A of Electoral Resolution 23.610.
The way the list is included reflects a transparency practice.
// User ids below are obfuscated; usernames are included for transparency.
// @OmarAzizSenador deleted his account at the time this code was written.
// @_ANDREDOPRADO no live account was found.
// @_EDUARDOMANTOAN no live account was found.
// @ADALBERTO_1111 no live account was found.
...
- The user_id is obfuscated so the internal X ID is not exposed, while the @username stays in the code as is.
- Next to each account there is even a status comment such as "deleted his account" or "no live account was found".
- As a compromise between open source and user privacy (preventing leaks of internal user_id), it keeps verifiability while hiding X's internal user_id mapping.
- The scale appears to be much larger than the 665 accounts in Gen4+1 inferred.
- The exact number of accounts has to be computed separately by counting diff lines. If each account takes 2–4 lines in a 4947-line file, the scale is roughly 1200–2000 accounts inferred.
Canon update Updated the Brazil2026ElectionFilter entry in services/x.md to state explicitly that the account list is updated on a regular basis.
1-5. The Phoenix index also loads data from before the minimum-age condition is met
For each window that has a minimum age since posting, Phoenix's offline indexing pipeline now also loads data that has not yet met that condition, as a separate snapshot. This change went into the base repository of the Phoenix rankall pipeline.
Background phoenix-rankall/ is Phoenix's offline indexing and aggregation pipeline. It maintains the candidate pool split into several windows, for example 1fav_uec_1day, video_14day, and evergreen_video_1825day. Gen4+8 continued the flow of the two changes below and expanded the base repository of phoenix-rankall itself.
- Gen4+1 added the
video_14daySID window. - Gen4+6 shrank the
EVERGREENretrieval dataset fromevergreen_video_1825daytovideo_4to14day.
The change consists of new constants and loading logic in phoenix-rankall/src/store/base.rs (+202/−61).
const PREFLOOR_SLACK_SECS: f64 = 6.0 * 3600.0;
// if the window config has min_age:
if wc.min_age.is_some() {
let pf_name = format!("prefloor_{window_name}");
let pf_link = self.config.output_dir.join(format!("{pf_name}.parquet"));
let pf_path = if pf_link.exists() {
Some(pf_link)
} else {
find_latest_versioned_file(&self.config.output_dir, &pf_name)
};
// load batches from pf_path...
}
- If a window has a
min_agecondition, the data from before min_age that has not yet passed the condition is kept in a separateprefloor_<window>.parquetfile and loaded together.- An example of a
min_agecondition is "this pool includes only items at least N hours past posting".
- An example of a
PREFLOOR_SLACK_SECS = 6 hoursis the slack time during which prefloor data is kept until it crosses the min_age threshold.
Canon update Added the subsection "Gen4+8 (2026-08-25) — rankall prefloor snapshot wiring" to the Phoenix training and index section of services/x.md.
Why it matters This change continues the flow of Gen4+2 removing the favoriteCount>=1 gate in phoenix-rankall-strato. Previously a candidate had to pass the condition to enter the index, but now rankall keeps candidates that have not yet passed in a separate pool so they can reach production immediately when they pass. This is an improvement for cold start and freshness, and it is likely to reduce index latency for new posts inferred.
1-6. The Phoenix download client now actually respects its concurrency cap
An issue was fixed where the actual number of concurrent downloads could exceed the configured cap when Phoenix serving downloads embedding tables and checkpoints. 2 new regression tests were also added.
copy_portis the client that downloads embedding tables and checkpoints from object storage (S3/xai-o2) in Phoenix serving.- The file is
phoenix/crates/serving/xai-recsys-engine/src/copy_port_client.rs(+155).
- The file is
The previous code ran several downloads in parallel with
join_all(futures).await.- However, when
join_allpolls the futures sequentially inside a rate limit batch, the actual in-flight concurrency could exceedmax_concurrent.
- However, when
After the fix, each future is wrapped in a separate task with
tokio::task::spawn. Thistokio::spawnhardening fixes the rate limit so it respects the in-flight cap without parallel polling.// Before: let batch_results = join_all(batch).await; // After: let batch_results = join_all(batch.into_iter().map(tokio::task::spawn)) .await .into_iter() .collect::<Result<Vec<_>, _>>() .map_err(|e| CopyPortError::Other(format!("copy_port download task join: {e}")))?;- Wrapped this way, the runtime scheduler actually runs them in parallel.
- The rate limit logic (
join_rate_limited) decides precisely when to issue the next batch based on the in-flight count.
The two new regression tests check the following.
rate_limit_caps_in_flight_despite_spawnchecks whether peak in-flight is exactly 2 when a concurrency=2 cap is applied to 6 downloads.rate_limit_paces_between_spawned_batcheschecks whether pacing between batches is maintained.
Why it matters This change is hardening that fixes the accuracy of parallel download throttling at serving time, and it pairs with Gen4+7's multi-node hardening of the Phoenix checkpoint loader, which fixed an OOM in the restore path. Both are an extension of the maturation cycle of training and serving infrastructure that followed Muon and GB300 training being formally included in the export in Gen4+6. The fact that such stability fixes attach to commits every day is itself evidence that X keeps devoting organizational resources to keeping the Gen4 open-source release verifiable and trustworthy in production.
1-7. 4 minor changes in grox, engagement counts, and URT, and other cleanups
The rest are small cleanups and extensions.
What we learnedOther changes
- Made minor cleanups to the reply_spam pipeline in grox (Grok-based nearline annotation pipeline).
- The changed files are
grox/flows/reply_spam/task_rate_limit.py−10 lines,plan_reply_ranking.py−6,plan_spam_comment.py−6, andtask_filter.py+2/−2. - Because
task_rate_limitis −10 lines, part of the rate limit logic appears to have been removed. However, only the size of the pure deletion is visible, so the exact change must be checked separately in the diff. task_filter.pyonly adjusted the execution flow slightly without changing the threshold. The follower threshold relaxation flow that went from 15k to 100k over 11 days is still maintained in this commit.
- The changed files are
- 2 new constants and 1 new generator were added to grox's
upa/(which appears to be grox's profile and user analysis pipeline inferred).- The changed files are
grox/flows/upa/constants.py+2 andgenerators.py+11. - What this subflow does has still not been stated in the canon since it was released in Gen4, so we need to keep watching the next commits.
- The changed files are
- Made a minor extension to the hydrator that attaches engagement counts (favorite/reply/retweet counts) to candidates.
- The changed file is
home-mixer/candidate_hydrators/engagement_counts_hydrator.rs+16/−1. - The newly added logic needs more checking, but it appears to add one field.
- The changed file is
- Made a minor extension to the URT (Universal Response Tree, X's response rendering schema) marshalling layer.
- The targets are the ads, WTF (Who To Follow), and client event marshalling logic.
- Five files changed.
home-mixer/util/urt/mod.rs(+13/−8)ad_marshaller.rs(+18/−8)client_event.rs(+27/−7)wtf_marshaller.rs(+14/−3)reverse_chron_following/mod.rs(+12/−5)
- The distinguishing point is not the individual changes but that this release touched the URT layer evenly across the board.
- Made minor touch-ups to the Phoenix training and indexing infrastructure as a follow-up to the Gen4+7 restore hardening.
- Six files changed.
- The AuthorColdStart scorer remained and was tidied up, separately from its consumption disappearing from VMRanker.
- The changed file is
home-mixer/scorers/author_cold_start.rs+12/−9. - Where the new consumer is needs more checking.
- The changed file is
- Made a minor extension to the Night Owl (night-time fatigue adjustment) source.
- The changed file is
home-mixer/sources/following_night_owl_source.rs+5.
- The changed file is
Why it matters Across the whole Gen4+8 release there are three axes.
- Enforcement practice became more flexible.
- In addition to hardcoded YAML rules, AES also supports a general action pipeline that scorers request directly. The deployment cycle for attaching a new scorer got shorter.
- The infrastructure for attaching embedding-based spam detectors such as
panda_reports_embedding_v10_rough_spam, which are continually retrained, has settled in.
- The ranker path became much simpler.
- The local computation layer of VMRanker was removed and delegated to the server-side DPP.
- This is an architecture refactor paired with the Gen4+5 slate context migration; home-mixer plays only the role of a thin aggregator, and the gRPC servers handle the computation.
- The policy of the Following surface widened.
- It also respects block relationships of authors in quote and retweet chains. It explicitly closes the block bypass via quotes.
The overall flow of Gen4 still traces a maturation curve of code release, verification infrastructure hookup, experiment tracking, architecture cleanup, and policy expansion. The fact that commits at intervals of at most three days continued for 12 days through Gen4+8 (08-25) is itself evidence that X maintains this open-source release as a sustained code mirror relationship rather than a marketing event.
2Tlow raised production CTR with a flow-based tokenizer — WeChat
Source Tlow: Flow-based Item Tokenizer for Recommendation — arXiv 2608.24176, 2026-08-25 v1, paper. There are 6 authors (Nian Li, Chonggang Song, Jingtao Ding, Lingling Yi, Yong Li, Qingmin Liao). The HTML version of the abstract states a production A/B at China's largest social media platform WeChat, so we confirmed the affiliation.
Background The item tokenizer, a core component of the Semantic ID (SID) line, is the module that converts each item's semantic embedding into a sequence of discrete token IDs. The de facto standard is RQ-VAE (Residual-Quantized VAE). After Google's 2023 TIGER paper, several production systems adopted it, including Netflix Foundation Model and Kuaishou PushDualGen. However, RQ-VAE has three known problems.
- Codebook collapse occurs, so most code slots die and only a few are used.
- Quantization error accumulates in the residual, so the error grows in deep quantization layers.
- Because of discontinuity, similar items map to completely different token sequences, which makes training difficult.
A flow-based model transforms the embedding space into a learnable distribution with an invertible neural network and inverts it back. This paper (Tlow) approaches the problems above with a normalizing flow, and the authors claim that flow-based tokenization is better than RQ-VAE on two axes: codebook utilization and semantic locality.
What we learnedA. In the production A/B, the CTR gain was larger for new items
In the production A/B, global CTR rose +10.32% and new-item CTR rose +11.64% verified.
"The retrieval model based on token IDs improves user CTR by 10.32% globally and by 11.64% for new items." — Tlow abstract, 2026-08-25
- A larger gain on new items suggests it is especially strong at cold start verified — same abstract.
- The key benefit of the semantic tokenization line, namely obtaining meaningful representations even for cold items, was confirmed in production metrics verified — same abstract.
What we learnedB. The evaluation surface is the WeChat retrieval model, and the detailed surface is not public
- The evaluation surface is the WeChat retrieval model verified.
- The abstract does not disclose whether it is ads or organic content, or which detailed surface among Look/Video Channels/Moments verified.
What we learnedC. The author affiliation was confirmed as the Tencent WeChat rec group
- There is a Tencent-related author name and the abstract states WeChat as China's largest social platform, so we confirmed it as the Tencent WeChat rec group.
- The Tencent-related author name is
Chonggang Song.
- The Tencent-related author name is
Canon update Added the section ### Tlow (WeChat/Tencent) — A flow-based item tokenizer as an RQ-VAE alternative, CTR +10.32% in production to "new this month" in radar.md. It is placed above HEGM and links to the SID thread.
Why it matters
- It is a new axis for the SID/generative retrieval thread: the tokenizer architecture itself has begun to diverge in production.
- Until now the SID line has mostly combined RQ-VAE with a downstream model.
- Tlow is the first statement that replacing the tokenizer layer with a flow-based one still yields effective metric gains in production.
- It is circumstantial evidence that RQ-VAE's codebook collapse was a real problem in serving inferred, and we interpret it as Tencent having hit this problem and offering an alternative.
- It is Tencent WeChat's first production claim in a while.
- WeChat rec has been quiet in recent years.
- With Tlow, this appears to be a signal that real architecture statements for the organic content surface are resuming.
- It is worth watching whether a paper on the rec stack of Weixin Video Channels (WeChat's short-video surface) follows.
- There are two checkpoints to watch.
- Watch whether code and the detailed block-wise flow structure appear in follow-up material.
- If a second company such as Kuaishou, ByteDance, or Xiaohongshu states a similar tokenizer alternative, it becomes a
topics/promotion candidate.
3TAGR raised revenue by +16.1% in live-commerce ads — anonymous platform
Source TAGR: Temporally Adaptive Generative Recommendation for Industrial Live-Streaming Advertising — arXiv 2608.24034, 2026-08-25 v1, paper. There are 10 authors (Wencai Ye, Guangyi Liu, Chaoyi Wang, Wenbin Luo, Shengyu Wang, and others). The author affiliation and platform name are not stated in the abstract. The abstract says only "a large-scale e-commerce live-stream advertising platform".
Background Live-streaming ads place strong freshness requirements on the recommender, because content, products, and user feedback change in real time. So far the generative recommender (SID-based) line has mostly assumed a static catalog. In live streaming, the time scale at which streams open and close is in minutes, so a static token vocab stays valid only briefly. This paper (TAGR) presents generative recommendation tailored to rapidly changing live content, products, and user feedback.
What we learnedA. In the production A/B, entry rate, cart click rate, and revenue all rose
- The production A/B results are as follows verified.
- live-room entry rate +8.5%
- shopping-cart click rate +7.4%
- revenue +16.1% over production baseline
What we learnedB. The abstract does not give the concrete mechanism for adapting to the time axis
- The architecture is Temporally Adaptive Generative Recommendation.
- The abstract describes it as "designed for rapidly changing live content, promoted products, and user feedback".
- Concrete time-axis adaptation mechanisms such as the tokenizer retraining cycle or online updates do not appear in the abstract. The body text needs to be checked, but we skipped it in this batch.
What we learnedC. The platform cannot be confirmed from author names alone
- The leading candidates for Chinese e-commerce live-streaming are Kuaishou (Kwai), Alibaba (Taobao Live), Douyin/ByteDance (Douyin E-commerce), and Xiaohongshu, but the author names do not directly match any of these companies inferred.
Canon update Added the section ### TAGR — Anonymous large e-commerce live-stream ads platform, revenue +16.1% to "new this month" in radar.md.
Why it matters
- A third company joined the live-streaming ranking thread.
- Following the existing Twitch (Amazon) Multi-Objective and Netflix's live cold start, TAGR adds a third angle: an ads surface and Chinese e-commerce live streaming.
- Generative recommenders are accumulating production claims in fast-changing domains such as ads and live streaming.
- Previously many claims were based on static short-video or e-commerce catalogs. This one comes from a domain that needs time-axis adaptation.
- Not being able to confirm the platform is the biggest weakness.
- If the affiliation is released after v2 or the author email domain can be confirmed, we will reconsider the canon promotion.
Corrections
None. The new sources in this batch (X Gen4+8, Tlow, TAGR) do not overturn the existing canon.
- The VM Ranker simplification (Gen4+8) fits the architecture cleanup flow of Gen4.
- The Following block expansion widens the policy direction, and the Brazil account update maintains the existing compliance practice.
- Tlow and TAGR attach as new data points to their respective watch threads (SID/generative retrieval, live-stream).
Radar
- 2 new:
- Tlow (Tencent WeChat, arXiv 2608.24176): flow-based item tokenizer, production CTR +10.32%
- TAGR (anonymous e-commerce live-stream, arXiv 2608.24034): temporally adaptive generative recommendation, revenue +16.1%
Of 30 arXiv cs.IR triage items, 2 passed. The ones that did not pass were mostly offline-benchmark-centered and had no industry provenance. 4 of the failed candidates drew attention.
- Native Multimodal for CTR: presumably Alibaba, but we could not confirm a production claim.
- RecGPT-Mobile-V2: Alibaba on-device query prediction. The earlier V1 was not tracked in the canon.
- WeMM-Embedding: a Tencent WeChat multimodal embedding technical report. It has no retrieval production claim.
- RetrievalFormer: an index that unifies search and rec with a dual-encoder. The production scale is unclear.
What to check next
- X Gen4+9 (next commit) — Commits landed on 9 of the 13 days from 08-13, when the Gen4 original was released, to 08-25, and the longest gap is three days. There are six points to check.
- See which scorers the AES
generic_actionsallowlist opens to through GrowthBook. - See whether VMRanker's
dppvalue model settles in as the only option or other value models are added. - See whether
AuthorSocialgraphFilteris also connected to the For You pipeline. - See when v11 follows
panda_reports_embedding_v10. This is a checkpoint for observing the retraining cycle of the embedding detector. - See in which window the
prefloorsnapshot is first activated. - See whether Gen4+7's
SafetyPtosPolicyCrossValidatorexpands to the spam and hate speech categories.
- See which scorers the AES
- Tlow follow-up — Track whether the code is released and any statements about other Tencent WeChat surfaces (Video Channels, Moments, Look).
- TAGR platform confirmation — If the author affiliation is released after v2, decide on the canon promotion. Recheck the Kuaishou / Xiaohongshu / Douyin match using the author names.
- Monday (2026-08-31) industry blog sweep — Check Pinterest engineering, research.atspotify.com, Google Research blog, and LinkedIn engineering.