In one lineX added a holdout filter that removes popular posts from the home feed for each viewer at a ratio set by fav-count bracket. It is off by default.
Other changes reviewed today:
In the same commit, the click-dwell ranking term was redefined as the product of dwell and click instead of the low-fav penalty (the gate is off by default).
Two LLM-judge search relevance actions added five days earlier were reverted, and ads delayed conversion attribution was widened to 3 tiers.
Added Meta's LIGE-GR paper to the radar as an item that needs a deep dive.
1A holdout filter that removes some popular posts per viewer was added — X
Sourcexai-org/x-algorithm@42266f3 — source code, 2026-09-17 05:09Z, GitHub commit. 64 files changed (+2288/−3627). These notes number X commits after the Gen4 release in sequence, and this commit is Gen4+23.
Background The 64 files are more than double Gen4+22 (26 files), and the 3627 deleted lines far exceed the 2288 added lines. VF (visibility-filtering) is the service that decides, per post, whether it may be shown to this viewer. Most of the large deletion came from the removal of the VF sub-repo's own twemcache client folder (3,234 lines) in full. This commit put three kinds of changes together into the ranking and filter stack of home-mixer (the service that receives For You and Following feed requests).
What we learnedA. A filter that removes a fixed share of popular posts per viewer was added at the end of the pipeline
A completely new filter was attached at the end of the home-mixer pipeline. This filter, which drops candidates, is named FavHoldoutFilter. It maps fav_count (a post's cumulative favorite count) to a holdout probability in steps. Through user-post deterministic hashing, it removes only a fixed share of popular posts per viewer.
Added a new filter FavHoldoutFilter to home-mixer that sets the holdout probability by fav count step verified — home-mixer/filters/fav_holdout_filter.rs (166 lines, new).
The share is 15% when fav_count exceeds 10,000, 15% above 5,000, 14% above 2,000, 13% above 1,000, 12% above 750, 11% above 500, 7% above 100, 5% above 20, ..., 3% above 2, and 2% above 1.
If fav_count is 1 or less, the share is 0%, and all viewers always see the post.
The holdout decision reuses InventoryHoldoutFilter::is_held_out(post_id, viewer_id, percent).
This function computes a 100-modular bucket for the (post_id, viewer_id) pair with a splitmix64-family double-mixing hash, and holds the post out if the bucket is below percent.
That is, the same user deterministically holds out the same post, and even for the same post, only a different subset is held out for each viewer.
The default param of FavHoldoutFilter is EnableFavHoldout = false. The param is defined as rust_home_mixer_enable_fav_holdout in home-mixer/params/param.rs.
What we learnedB. Three ratio params that the existing inventory holdout filter uses by post type were confirmed
The filter is named InventoryHoldoutFilter. This resolves a long-standing undisclosed item.
The filter file references three params: InventoryHoldoutOriginalsPercent, InventoryHoldoutRepliesPercent and InventoryHoldoutRetweetsPercent.
Post type is determined as retweeted_tweet_id.is_some() → Retweet, in_reply_to_tweet_id.is_some() → Reply, and otherwise → Original. A quoted post is treated as Original.
Each of the three values is clamped with .min(100).
The filter is active only when EnableInventoryHoldout=trueAND at least one of the three values is >0.
The holdout target is hashed deterministically on get_original_tweet_id() (the original for a retweet, otherwise the post itself). In other words, a retweet shares the same holdout decision as the original.
What we learnedC. The click-dwell ranking term can now use the product of dwell and click probability instead of the low-fav penalty
In ranking scoring, the term that adjusted the dwell prediction by the fav rate was deleted, and a much simpler gate took its place. What was deleted is the previously introduced EnableClickDwellLowFavRatePenalty logic, a term that adjusted the dwell prediction as (fav/baseline)^alpha by multiplying four tuning parameters. When the new gate EnableCdwellOnImpr is on, the click_dwell_time prediction is multiplied by the click probability (dwell × click). Since the click_dwell head has been a binary classification probability since the 2026-09-16 commit (Gen4+22), this product is a per-impression probability, not seconds.
The EnableClickDwellLowFavRatePenalty family was deleted entirely from ranking scoring and replaced by EnableCdwellOnImprverified — home-mixer/scorers/ranking_scorer.rs (+10/−32).
Five fields held by ScoringWeights (enable_click_dwell_low_fav_rate_penalty and baseline/alpha/floor/cap) and the five corresponding params were deleted. These params had existed since the Gen4 release (2026-08-13) verified — param.rs:388-412.
A single enable_cdwell_on_impr: bool took their place.
The param.rs mirror keeps the EnableCdwellOnImpr definition as follows:
The gate observability label also changed from gate.click_dwell_low_fav_rate_penalty → gate.cdwell_on_impr.
What we learnedD. The MoE condition was dropped from the Treatment arm of the new-author boost, and the post age cap was extended from 24 hours to 48 hours
Two gates on the Treatment arm of the author cold-start scorer (the scorer that boosts posts by new authors) were relaxed together; the MoE condition has existed since the 2026-09-04 commit (Gen4+16), and the age cap since Gen4. The is_phoenix_moe(candidate) condition that the Treatment arm required was removed, so any Treatment-corpus candidate is now a cold-start target regardless of its MoE (mixture of experts, a structure in which only some expert networks are used per input) label. Also, ColdStartMaxPostAgeSecs doubled from 86400 (24 hours) to 172800 (48 hours), and this age cap, which applied only to the Treatment arm, now applies to all arms. Three regression tests were removed and one name was unified; test names also changed: treatment_viewer_cold_starts_treatment_moe_only became treatment_viewer_cold_starts_treatment_authors_only, and treatment_skips_post_older_than_max_post_age became skips_post_older_than_max_post_age.
Previously, ViewerArm::Treatment eligibility was corpus == Treatment && is_phoenix_moe(c). This change removed the is_phoenix_moe(c) condition, so only corpus == Treatment is checked.
The separate function cold_start_freshness_eligible(arm, c, max_age), which applied the age cap to the Treatment arm only, was deleted entirely.
Now apply_cold_start applies duration_since_creation_opt(c.tweet_id).is_some_and(|age| age <= params.max_post_age) as a filter for all arms.
The control_ignores_max_post_age regression test was deleted and folded into skips_post_older_than_max_post_age.
The default of ColdStartMaxPostAgeSecs changed from 86400 → 172800 (24 hours → 48 hours) verified — home-mixer/params/param.rs.
This value is used as the max post age in the filter above. In other words, the posts that get the new-author boost have now widened to candidates within 48 hours of creation.
What we learnedE. Two LLM-judge search relevance labels added five days earlier were reverted, and an action presumed to be a live-stream reply signal and a reserved slot took their place
In the serving proto recsys.proto of Phoenix (X's ranking Transformer that predicts engagement probabilities per candidate), there are two changes. One is that the two search relevance labels introduced in Gen4+20 (6bb4594, 2026-09-12) were reverted after five days; the two labels (LLM_JUDGE_*) are LLM_JUDGE_RELEVANT_TO_SEARCH=107 and LLM_JUDGE_NOT_RELEVANT_TO_SEARCH=108. Their slots were replaced by CLIENT_TWEET_LIVE_STREAM_REPLY=107 and RESERVED_108: CLIENT_TWEET_LIVE_STREAM_REPLY is presumed to be a live-stream reply signal, and RESERVED_108 is a reserved slot. The other is the 3-tier expansion of ads delayed conversion attribution.
The deleted ones are LLM_JUDGE_RELEVANT_TO_SEARCH (107) and LLM_JUDGE_NOT_RELEVANT_TO_SEARCH (108). The replacements are CLIENT_TWEET_LIVE_STREAM_REPLY (107) and RESERVED_108 (108).
The two labels were introduced five days earlier in Gen4+20 (6bb4594, 2026-09-12) and are reverted in this commit.
// before (Gen4+20)
LLM_JUDGE_RELEVANT_TO_SEARCH = 107;
LLM_JUDGE_NOT_RELEVANT_TO_SEARCH = 108;
// after (Gen4+23)
CLIENT_TWEET_LIVE_STREAM_REPLY = 107;
RESERVED_108 = 108;
What we learnedF. 20 tier 2 and tier 3 actions were added to ads delayed conversion attribution
Ads delayed conversion attribution (crediting conversions that occur late, after time has passed, to the ad) was expanded into a 3-tier structure. It extends from the previous single delay window to Tier 2 and Tier 3. In the proto, 20 slots inside the reserved 214 to 252 range were filled with real actions.
Ads delayed conversion attribution expanded to 3 tiers, and 20 action names were introduced (214-233) verified — same proto.
The existing reserved 214 to 252 narrowed to reserved 234 to 252, and tier 2 and tier 3 were attached to slots 214-233. Tier 2 is 214-223 and tier 3 is 224-233.
Tier 2 and 3 were added a month after the existing 10 *_DELAYED actions (204-213) were introduced in Gen4+6 (28e414f, 2026-08-21).
This commit does not state explicitly which part of the observation window each tier covers. It also does not state how training loss treats the tiers (whether they are multiplied together for training, or each lag gets a different loss weight).
What we learnedG. VF removed its own 3,234-line twemcache client and moved to the company-wide KV cache client
There was a large reorganization in the visibility-filtering sub-repo. Instead of maintaining its own twemcache protocol, connection, ring and discovery, the VF sub-repo moved to the company-wide KV cache abstraction. All 11 files under visibility-filtering/twemcache/ were removed. safety_label_source/twemcache.rs was rebased to depend on xai_cache::CacheClient of the new crate xai_cache (re-exporting Key, Value and KVCacheError).
The VF sub-repo's own twemcache client was removed entirely verified — 11 files under visibility-filtering/twemcache/, 3,234 lines in total, were deleted.
The removed files are accrual.rs 267 lines, client.rs 640 lines, connection.rs 762 lines, discovery.rs 121 lines, error.rs, and host_pool.rs 359 lines. The rest are host_pool_tests.rs 639 lines, key.rs 92 lines, metrics.rs 87 lines, mod.rs 13 lines, and ring.rs 239 lines.
As the replacement code, the top imports of safety_label_source/twemcache.rs changed as follows:
// before
use crate::twemcache::{Key, TwemcacheClient, TwemcacheError, Value};
// after
use xai_cache::{CacheClient, KVCacheError, Key, Value};
The fallback mapping was also reorganized. The previous mapping was TwemcacheError::Timeout → FallbackReason::Timeout, Backpressure → Backpressure, Unavailable|Io → Other. The new mapping was unified into KVCacheError::Timeout(_) → Timeout, Backpressure → Backpressure, _ → Other.
In short, VF moved the twemcache protocol client it maintained itself (including accrual, discovery, host_pool and ring; client.rs alone is 640 lines) entirely to the company-wide KV cache abstraction. The maintenance surface shrinks, and behavior is unified with the client other services use.
What we learnedH. VF gained a new RPC interface and a Thrift Action response decoder
A new RPC was added to the VF service, along with a client function that decodes its response from Thrift. The new RPC evaluate_tweets was added to the VisibilityFilteringService trait. The new file visibility-filtering-client/evaluated.rs (122 lines) provides the function decode_action, which decodes the response payload of this RPC into a Thrift Action.
The conditions decode_action imposes are as follows:
64KB payload limit (all four caps of TConfiguration at 64KiB)
Compact Thrift protocol
No trailing bytes
Action::NotEvaluated is rejected
That is, the response of evaluate_tweets must be a Thrift encoding of Evaluated(Action).
Action is a long-standing spec in xai_x_thrift::action. Its variants include MessageLink, LimitedAction and TweetVisibilityNudgeAction.
In short, VF's response interface has begun to expand toward returning a full Action object to the client, not just the enumeration of filtered reason codes.
Read together with Gen4+20, which left the xDS LDS listener migration open, VF is moving from a simple client → server filter to a richer policy-verdict service.
What we learnedI. Infrastructure was added to recompute the takedown labels of the Under-the-Hood transparency tool daily and to redact unenforced reasons
Infrastructure that recomputes the audit labels of Under-the-Hood (UTH, X's transparency tool where users look up the visibility-impact labels attached to their accounts and posts) every day and redacts unenforced labels before serving was newly added. The UTH sub-repo gained a daily job stack for reproducing account takedown labels. The new files are UthDailyPostTakedownLabelsJob.scala (283 lines), TakedownDeltas.scala (169 lines), TakedownLabels.scala (103 lines) and TakedownReplay.scala (80 lines); in addition, +164 lines were added to underTheHoodLabels.strato. On the Gizmoduck (X's user information service) side, RedactUnenforcedTakedownsFilter.scala (81 lines) and ReadFilters.scala (39 lines) were introduced, and tweet-entity-service gained TakedownReasonsTransform.scala (73 lines) and TakedownReasonsColumn.scala (25 lines).
Strato is the internal system that X code goes through to look up or store data.
This stack is a Scala/Scalding batch job that reproduces and aggregates daily takedown label deltas per user and applies them to the Strato column (underTheHoodReport.User.strato +92/-22).
UthPctdAccountTakedownEventsJob.scala was heavily revised at +392/-63. UthReferenceMonthMhPublisherJob.scala (+30/-10) and UthUserMonthMhPublisherJob.scala (+140/-34) were also modified.
This line is an audit and transparency tool pipeline, not ranking, so it is not a canon update target.
The exact semantic changes of individual functions are not covered in this scan. Most appear to be minor improvements to emb table management and gRPC utilities inferred.
Canon update Updated five sections of services/x.md.
Filters section (21 filters, line 355): Added FavHoldoutFilter (new in Gen4+23) at the end of the filter list. Deleted "the holdout ratio params need confirmation" from the InventoryHoldoutFilter entry. Replaced it with a confirmed statement of the 3-tier param (InventoryHoldoutOriginalsPercent, InventoryHoldoutRepliesPercent, InventoryHoldoutRetweetsPercent) wiring. Also added a new FavHoldoutFilter entry.
Attention scoring term (line 147): Added a sentence saying the computation of the click_dwell term was redefined in Gen4+23. The change is EnableClickDwellLowFavRatePenalty (a param present since the Gen4 release) → EnableCdwellOnImpr (dwell × click_score).
Undisclosed list (lines 1416, 2535): Deleted the item asking where LLM_JUDGE_RELEVANT_TO_SEARCH and LLM_JUDGE_NOT_RELEVANT_TO_SEARCH connect. Stated explicitly that they were vacated, introduced in Gen4+20 and reverted in Gen4+23. The undisclosed line on the holdout ratio params of InventoryHoldoutFilter was resolved and deleted.
Ads conversion action section (near line 1686): Added the Gen4+23 tier 2/3 expansion entry of 20 actions after the Gen4+9 entry.
Commit sequence section (line 2545): Added 42266f3 (2026-09-17, Gen4+23) after fad2f71 (Gen4+22), and changed "23 commits since Gen4" to "24 commits since Gen4".
Also added a new section 'Gen4+23 (2026-09-17, 42266f3)' after Gen4+22, summarizing ten findings (A–J).
Why it matters Three kinds of changes came together in this commit: the popular-post holdout, the redefinition of the click_dwell term, and the relaxed cold-start gates. A change in the Phoenix proto adds a fourth layer on top.
First, home-mixer ranking gained a new axis: a per-viewer deterministic holdout of popular posts.
Where InventoryHoldoutFilter was a 3-tier holdout by type (original, reply, retweet), FavHoldoutFilter is a 23-tier step holdout by popularity (fav count).
The two filters are each deterministic for a viewer-post pair but make independent decisions. So home-mixer can now run two kinds of counterfactual holdout experiments in parallel, at a stage still before scoring.
One is a uniform holdout by post type, and the other is a stepped holdout by popularity.
Why a popularity holdout? If 15% of posts with 10K+ fav are held out, it becomes possible to observe how those viewers respond to the next-best candidate without that post.
That is, infrastructure opens up to capture, through a measured experiment, the marginal contribution of top popular posts (the user loss when this post disappears from Home).
Paired with the three-stage response diversity observation side effect in Gen4+21, X's ranking measurement infrastructure is expanding along two axes: diversity as a set-level counterfactual and holdout as a post-level counterfactual inferred.
If the diversity statistics observation in Gen4+21 observes slate (the bundle of candidates shown at once) composition without an A/B experiment, the holdout filter runs an A/B experiment automatically inside the filter.
Second, the definition of the click_dwell ranking term changed completely.
The former EnableClickDwellLowFavRatePenalty was a heuristic penalty that lowered the dwell prediction of low-fav-rate posts by (fav/baseline)^alpha, to suppress misjudgment based only on inflated dwell.
In Gen4+22, as the click_dwell head changed to sigmoid BCE binary classification, the effective scale of the head had been reduced 60×.
This commit cleaned up by removing the fav-based penalty (four tuning params and the corresponding fields were deleted).
EnableCdwellOnImpr, which took its place, is an alternative approach. It multiplies the click_dwell prediction (dwell probability or time given a click) by the click probability to convert it into a per-impression expected value.
Since this redefinition multiplies the signals of two heads, it reduces the problem of the "dwell-raising head" and the "click-raising head" offsetting each other or double counting.
This redefinition aligns with the practice of Netflix N-tower and YouTube ranker. They have long trained the dwell head as a conditional probability (P(dwell|click)) and then multiplied it by P(click) in serving to produce an unconditional expected value.
⚠ Claim changed: Yesterday's (2026-09-17) canon described "the effective contribution of the click_dwell_time head as reduced by ~60× from before". However, if this head is redefined as a product with click_score while ContClickDwellTimeWeight is not adjusted, the effective scale changes again.
More precisely, there are two cases. If EnableCdwellOnImpr=false (default), the 60× reduction is maintained.
When it is on, the term becomes a product of probabilities (0-1 × 0-1). The previous term multiplied the click_dwell value by a fav-rate multiplier (with the gate off, the click_dwell value as is), and there was no term multiplying by click probability verified — ranking_scorer.rs diff.
If the absolute weight is maintained, the effective scale could go to a much larger reduction rather than a 60× reduction, and the ranking contribution of this head could effectively be absorbed by the remaining heads.
Whether it is actually deployed would be observed in whether the EnableCdwellOnImpr mirror is switched in the next commit and ContClickDwellTimeWeight is retuned inferred.
Third, two gates on the Treatment arm of the cold-start scorer were relaxed together.
The removal of the is_phoenix_moe requirement means cold-start targets are now activated in the Treatment experiment without an MoE label.
This gate, created in the 2026-09-04 commit (Gen4+16), was an experiment condition limiting the Phoenix MoE head to be used in training only for cold-start candidates; relaxing it presumably requires a judgment that the measured results showed the condition was unnecessary or even a loss inferred.
The expansion of max age from 24h to 48h means the new-author boost window doubled. Since this cap applies to post age, not author tenure, posts within 48 hours of creation also become cold-start targets.
In the Treatment arm, both changes increase cold-start volume. In contrast, the Control and Holdout arms, which had no cap, newly get a 48-hour cap, so their condition actually tightened verified — the author_cold_start.rs diff in D above.
As the fourth layer, the two Phoenix proto changes indicate how future ranking signals will be organized.
The vacating of the two LLM_JUDGE_* labels after five days is a signal that xAI has, for now, folded its direction of transplanting LLM judge labels as a separate axis into search relevance ranking training.
There are two possibilities. The judgment may have been routed in another way (e.g., reusing the existing CLIENT_TWEET_RELEVANT_TO_SEARCH=105 label channel as is), or the label axis itself may have been left waiting for redefinition.
The newly added CLIENT_TWEET_LIVE_STREAM_REPLY (107) appears to be a client signal for live-stream replies; we need to watch whether the next commit folds it into the training signals.
The 3-tier expansion of delayed conversion attribution is the most solid ads-infrastructure link in this commit.
If the ads conversion observation window had two kinds so far, "immediate" and "delayed", delayed is now split into tier 2 and tier 3 as well, and each observation lag is routed to a separate head.
This matches the standard approach in the literature (Chapelle 2014, Yasui & Sakata 2020 family), where delayed training applies per-lag masks to reduce lag bias inferred.
Read together with the ADS_WEB_CT_PURCHASE_VALUE Huber ratio loss formally wired in Gen4+22, X's ads training stack is moving toward value regression and per-lag delayed heads.
The VF twemcache removal and the evaluate_tweets RPC expansion are not on the ranking axis, but they are worth observing.
VF began expanding its client → server response from a sigil enum to a full Thrift Action object.
If this direction continues, home-mixer may consume richer policy verdicts where it has so far consumed VF responses only as FilteredReason.
The introduction of the UTH label reproduction job stack is infrastructure for the daily recomputation of audit labels for the Under-the-Hood transparency tool that X announced.
This line serves a user audit tool rather than ranking, so it is not a candidate for a canon update. However, it is meaningful that it sits in the same repo as the ranking code in the repository's file layout.
It leaves the intent that X keeps, in the same repository as ranking and safety, a line that lets users look up directly "why was my post treated this way."
Corrections
This commit overturned one claim from an earlier daily and partially overturned another.
⚠ Claim changed #1: the Gen4+20 observation about the two LLM-judge search relevance actions was overturned.
The subjects are LLM_JUDGE_RELEVANT_TO_SEARCH and LLM_JUDGE_NOT_RELEVANT_TO_SEARCH.
The 2026-09-13 daily (Gen4+20) said these two actions were "a natural extension of the Phoenix search ranking config xrecsys_search (introduced in Gen4+4)". It also said "somewhere there must be a pipeline in which grox or a dedicated LLM judge feeds relevant/not labels to (query, tweet) pairs".
Because they were vacated after 5 days, we conclude that the labels were rolled back without being connected in production.
We remove the undisclosed mention of these two labels from the canon.
⚠ Claim changed #2: the statement in the Gen4+22 daily that "the effective contribution of the click_dwell_time head remains reduced by ~60× compared with before" is partially overturned.
With EnableCdwellOnImpr introduced in Gen4+23, the final value of this head becomes (click_dwell_prob × click_prob).
This produces a larger reduction than 60× (probability × probability is a much smaller scale than the original unit).
The measured scale depends on the ContClickDwellTimeWeight adjustment and on whether EnableCdwellOnImpr is activated.
We fold this redefinition into the Attention head section of the canon.
Radar
LIGE-GR (Meta) goes into the radar's new-this-month items, and SARA (Kuaishou) is added to the watching thread.
LIGE-GR (Meta) is a new item that passed review on arXiv. It claims improvements of 1.14% time-spent on Instagram Reels and 0.72% time-spent on Facebook Video. The topic is a smooth leap from ranking to generative recommendation.
It is direct evidence for our tracked services (Instagram, Facebook, Threads), so it is a radar deep dive candidate.
We add it to ## New this month in radar.md (marked as needing a deep dive).
SARA (Kuaishou) covers articulated rationale for MLLM-based recommendation. It claims a Kuaishou production deployment (30+ days, 240M users).
Kuaishou is not one of our tracked services, but it is a large-scale production MLLM recommendation deployment, so we add it to the watching thread.
ANGLE is one-step sponsored search ads retrieval (2609.18296). We need to check the author affiliation of the deploying party to judge whether it is a platform company.
PCap was already added to the radar yesterday (2026-09-17). We do not process it twice.
What to check next
From the next commit on, we watch when the params, actions, and RPCs opened this time are turned on or used. LIGE-GR gets a separate deep dive.
Watch when EnableFavHoldout switches in the mirror — if this param changes from false → true, the fav-tier holdout becomes active in production. At that point, the thing to observe is under which experiment bucket the 15% holdout tier (10K+ fav) is actually activated. We also check whether the response diversity observation from Gen4+21 is logged together with this filter's activation.
Watch when EnableCdwellOnImpr switches in the mirror and whether ContClickDwellTimeWeight is adjusted alongside — if the redefinition above is actually deployed, it determines how the click_dwell contribution, reduced after Gen4+22, is readjusted. Without a weight increase, this head's contribution effectively stays reduced; with an increase, the combined dwell × click signal enters ranking at a new scale.
Check whether the Treatment-arm relaxation of the cold-start scorer led to A/B results — the removal of the MoE gate from Treatment eligibility and max_age 24h→48h arrived together, and either was triggered by measured results or prepares the next experiment setup. In the next commit, we watch how the related param mirrors are deployed.
Check whether the CLIENT_TWEET_LIVE_STREAM_REPLY (107) label is folded in as a training signal — we look at which training config's target head list this action is added to. Whether it is xrecsys_search or _home_direct_packed_base decides whether live-stream replies sit on the search relevance axis or the home ranking axis.
Watch when the ads delayed conversion tier 2/3 training config is activated — we look at which metric group and loss weight the 20 newly introduced actions are activated under. How the Gen4+6 split_head_training_by_source option handles these tiers decides the actual approach to per-lag training.
Watch the server-side implementation of the VF evaluate_tweets RPC — since only the client is open, when the server implementation starts returning which policy verdicts as Thrift Action decides whether the VF serving interface is substantively expanded.
Do a deep dive on LIGE-GR — it is a paper in which Meta claims a smooth leap from ranking to generative on Instagram Reels and Facebook Video. Since it bears directly on our tracked services, we need to read the arXiv html and decide whether to fold it into the canon.