0x00 한

Research / Daily reports

  • Sources read: 1
  • Canon files updated: 1
  • Radar entries: 0
In one lineX's visibility filter now drops posts by an explicit action value instead of the filtering reason. Most filtering outcomes stay the same.

1The visibility filter drops posts by action value instead of reason — X

Source xai-org/x-algorithm@75d93d9 — public source code, 2026-09-09, 28 files, +801/−252. These notes number X commits after the Gen4 release in sequence, and this commit (2026-09-09, 75d93d9) is Gen4+18.

Background X mirrors the For You pipeline source code to xai-org/x-algorithm about twice a day (since the Gen4 release on 2026-08-13). This commit is mid-sized at 28 files, but it touched five different axes at once.

  • The visibility verdict is now decided by the action value, not the reason. visibility-filtering (VF) is the service that decides, post by post, "may this viewer see it?".
    • The VF client added an explicit Action field to TweetVisibility, and VFFilter in home-mixer (the service that assembles the For You feed) now decides drop/keep by the Action enum instead of FilteredReason.
    • The verdict axis moved from "action derived from the reason" to "the action decides, and the reason is an annotation for logging".
  • A new ad format made of a header image, body text, and one or two CTA (call-to-action) buttons appeared as a first-class format in the home timeline. It is the sixth variant, and it is not a promoted-tweet format that wraps a tweet.
    • A branch that handles ads carrying header_image_prompt_ad_info in a separate marshaller was introduced in home-mixer/util/urt/ad_marshaller.rs.
  • On the content classification side, the first frame of long videos now goes into the LLM prompt separately.
    • The media loader of grox (the Grok-based nearline classifier sub-repo) downloads and resizes a first-frame preview image for videos of 30+ seconds.
    • This image is placed before the storyboard (an image of uniformly sampled frames tiled in a grid) in the LLM prompt, with the label [Video Preview Thumbnail].
  • Live broadcast frames became formal verdict targets of two safety pipelines.
    • BroadcastLoader was newly added to grox and extracts frames of live broadcasts (X's live video, the successor to Periscope).
    • Two safety pipelines, PTOS (policy-based violation category verdict) adult-content leading-frames and the sex/nudity safemodel, accept broadcast frames as a formal payload.
  • The multimodal post embedding was dropped from the training input in both registered ranking training configs.
    • In the nano training config table of phoenix/README.md, the documentation of Phoenix (the ranking model of X For You), the line saying the multimodal post embedding is on as v5 in xrecsys_seqpack changed to "off (home_direct_packed and xrecsys_seqpack)".
  • Incidentally, phoenix NUMA pinning was fixed to exclude isolated CPUs from the sched_setaffinity target, and six feature-gated quiet-spans logs in candidate-pipeline were deleted.

What we learnedA. VF verdicts gained an explicit action value, and the filter uses it to decide what to drop

  • The VF verdict axis was refactored from the filtering reason to an explicit action value verified — visibility-filtering-client/vf_client.rs (+130/−62).
  • The verdict result the client returns is now a tuple of three values verified.
    1. The deterministic verdict Action (Allow / Interstitial / Avoid / Downrank / Drop / Tombstone / NotEvaluated)
    2. The annotation FilteredReason for logging and downstream use
    3. The label lookup result SafetyLabelMap
    • TweetVisibility now has an action: Action field as a formal member, in addition to the existing reason: Option<FilteredReason> and safety_labels.
    • A new helper, TweetVisibility::to_visibility_reason(), combines the action and the reason to derive the value of the visibility_reason field.
  • On the Strato path (Strato is the X internal system that code goes through to look up or store data), the client computes the action from the reason it receives from Strato verified. There are three rules.
    • Strato is defined in the services/x.md glossary.

    • StratoVfClient::get_result computes the action from the FilteredReason received from Strato via action_from_strato_reason.

      fn action_from_strato_reason(reason: &Option<FilteredReason>) -> Action {
          match reason {
              None => Action::Allow,
              Some(FilteredReason::SafetyResult(safety_result)) => match safety_result.action {
                  Action::NotEvaluated => Action::Allow,
                  _ => safety_result.action.clone(),
              },
              Some(_) => Action::Drop(DropReason {}),
          }
      }
      
    • If the reason Strato gave is SafetyResult, the action inside it is used as is, and every other non-SafetyResult reason (mute, block, null-cast, exclusive, etc.) becomes Drop unconditionally.

    • If Strato returns NotEvaluated, the client layer promotes it to Allow. This means Strato treats a verdict that was not run as fail-open.

  • On the gRPC path, the action in the response is used as is verified. On the new gRPC path, the standard is for the server to decide the action and send it down.
    • result_to_visibility maps result.action of the gRPC response directly to Action.
    • If there is no action field, it becomes Action::NotEvaluated.
  • The filter layer now decides what to drop by the candidate's action value verified. Allow, Interstitial, Avoid, and Downrank are kept; Drop, Tombstone, and NotEvaluated are filtered out.
    • VFFilter::filter is now a function of candidate.visibility_action. The verdict function is the following:

      pub(crate) fn should_drop_action(action: &Action) -> bool {
          match action {
              Action::Allow | Action::Interstitial | Action::Avoid | Action::Downrank => false,
              Action::Drop(_) | Action::Tombstone | Action::NotEvaluated => true,
          }
      }
      
    • The previous version matched FilteredReason variants. For SafetyResult it filtered only when action == Drop, and every other non-SafetyResult reason (mute, block, null-cast, exclusive, etc.) was an unconditional drop.

    • The new version is far more readable because the verdict axis is explicit.

  • Filtering of ancestor, quoted, and retweeted source posts also looks at the action value verified.
    • should_drop_ancillary in vf_candidate_hydrator.rs looks at visibility.action of ancestor, quoted, and retweeted posts to decide whether to drop.
    • The previous should_drop_reason(reason: &FilteredReason) was removed, and the filter layer's should_drop_action is shared.
  • An action field was added to the candidate object verified.
    • PostCandidate in candidate.rs now has visibility_action: Option<Action>, and the existing visibility_reason: Option<FilteredReason> is kept.
    • The two hydrator update functions (VFCandidateHydrator::update and VFFollowingCandidateHydrator::update) fill this field as well.
  • A new reason name for the case where the viewer blocks the author was introduced verified.
    • The new FilteredReason variant is FilteredReason::ViewerBlocksAuthor. Previously only AuthorBlockViewer (the author blocks the viewer) existed. Now the symmetric direction, "the viewer blocks the author", also has a formal reason name.
    • vf_pb::FilteredReason::ViewerBlocksAuthor and the proto to/from mappings were also added.

⚠ Claim changed (the "fail-closed" statement at services/x.md line 392): the fail-closed character is kept, but the verdict granularity is much clearer.

  • The previous canon said "VFFilter is fail-closed. Every FilteredReason variant other than SafetyResult is an unconditional drop".
  • The new verdict function has completely different syntax, but the results are mostly identical. On the Strato path, non-SafetyResult reasons are already converted to Drop at the client layer, and NotEvaluated is also dropped.
  • The previous filter also dropped a verdict that arrived as SafetyResult only when the action was Drop verified — vf_filter.rs:22-30. It kept Allow, Interstitial, Avoid, Downrank, Tombstone, and NotEvaluated.
  • What changed is that the filter layer now drops Tombstone and NotEvaluated verified — vf_filter.rs:16-21, quoted below.

"match action { Action::Allow | Action::Interstitial | Action::Avoid | Action::Downrank => false, Action::Drop(_) | Action::Tombstone | Action::NotEvaluated => true, }" — home-mixer/filters/vf_filter.rs:16-21, 2026-09-09

Why it matters (A) This refactor narrows the verdict to a unified action value at the client layer so that only the action flows downstream.

  • The type of the unified action value is the Action enum.
  • Since the Gen4 release (2026-08-13), three paths have been entangled in VF's verdict function: a path that must read SafetyResult.action, a path that must read the FilteredReason variant, and a path that must map the gRPC action.
  • In the shadow comparison that Gen4+7 (2026-08-24, d011592) introduced with reference_compare.rs, the cause of the diffs was likely mostly "reason variant matching differs slightly per pipeline" inferred, and this refactor reads as a follow-up that reduces those diffs.
  • No code in this commit actually uses Downrank yet. The door is open for Downrank to appear in the SafetyResult.action that Strato returns, but whether it is actually wired up is something to check in the next commit.

What we learnedB. The home timeline gained a sixth ad format that does not wrap a tweet

  • A new ad format was introduced in the home timeline verified — home-mixer/util/urt/ad_marshaller.rs (+258/−3).
    • The format name is header-image-prompt-ad.
  • Home timeline ads previously had five formats, and this commit added the sixth verified.
    1. Promoted tweet (promoted-tweet-<post_id>-<impression>)
    2. Preroll video ad
    3. SSP tweet/RTB Trend Takeover
    4. RTB image ad (rtb-image-ad-<impression>)
    5. Google SDK Native ad(GoogleSDKNativeAd)
    • The entry name of the sixth is header-image-prompt-ad-<impression>.
  • If the ad carries information for this format, the dedicated marshaller handles it first verified. The branch is mutually exclusive with promoted tweets.
    • This branch is at the top of the marshal_ad() function.

      if let Some(info) = &ad.header_image_prompt_ad_info {
          return marshal_header_image_prompt_ad(ad, info, sort_index, request_type, &impression_string);
      }
      
    • The criterion is whether the AdIndexInfo.header_image_prompt_ad_info: Option<HeaderImagePromptAdInfo> field is present.

  • The input (HeaderImagePromptAdInfo) is a proto message filled in by the ad index service, and its field composition can be reconstructed from the form the marshaller consumes verified.
    • Header image: image_url: String, image_width/height
    • Header and body text: header_text: String, body_text: String
    • Primary CTA: button_text: String, landing_url: String
    • Secondary CTA (not shown if empty): secondary_button_text: String, secondary_landing_url: String
    • Whether to dismiss the ad after a click: dismiss_on_click: bool
    • Impression tracking callbacks: impression_callback_endpoints: Vec<String>
    • Primary CTA click tracking callbacks: click_callback_endpoints: Vec<String>
  • This ad is carried in URT (Universal Response Tree, X's response rendering schema) as a Message-type item, not a tweet verified. It is a separate type called "message prompt".
    • The entry structure is TimelineEntryContent::Item(TimelineItem { content: TimelineItemContent::Message(MessagePrompt { content: MessageContent::HeaderImagePrompt(HeaderImagePrompt { .. }), impression_callbacks }) }).
    • MessageContent::HeaderImagePrompt is the content variant dedicated to this ad.
  • The prompt content (HeaderImagePrompt) carries the image, text, and button actions verified.
    • Image (image_variants holds the URL, width, and height): header_image: MessageImage
    • Header text: header_text: String, header_rich_text: RichText. Both forms are carried together: plain text and a RichText without entities.
    • Body (only when present): body_text: Option<String>, body_rich_text: Option<RichText>
    • Primary button (required): primary_button_action: MessageTextAction
    • Secondary button (only when secondary_button_text is not empty): secondary_button_action: Option<MessageTextAction>
  • Each button has its own click event information and callback list verified.
    • The button structure (MessageTextAction) consists of text: String and action: MessageAction { dismiss_on_click, url, client_event_info, on_click_callbacks, on_click_reactive_trigger }.
    • In each button's ClientEventInfo, the action is "primary_cta" or "secondary_cta".
  • Three tests pin down this contract verified.
    1. header_image_prompt_ad_marshals_to_message_prompt_entry: asserts the entry_id format ("header-image-prompt-ad-fa4ead01"), the impression callback, the HeaderImagePrompt content variant, the image URL/dimensions, the text, and the primary CTA URL/dismiss/callback.
    2. header_image_prompt_ad_emits_secondary_button_when_set: when secondary_button_text = "Learn more" is set, secondary_button_action exists and its action is secondary_cta.
    3. header_image_prompt_ad_info_absent_stays_a_tweet_item: when header_image_prompt_ad_info: None, it falls through to the existing promoted-tweet marshaller.

Why it matters (B) The X home timeline formally added a new ad format that does not wrap a tweet.

  • RTB image ad was already an image ad separate from tweets, but header-image-prompt-ad has a much richer component composition made of a header image, body text, and up to 2 CTA buttons.
  • This format is the first ad to use a separate content type, URT MessageContent. From the client rendering side, it is likely rendered as in-feed announcement/dialog UI rather than as a tweet inferred.
  • This is also the first time an ad format that supports a secondary CTA has entered the home timeline.
  • This commit does not show when or under what conditions the ad index system (not public) fills this ad format. It also does not show which of the five ad blenders (safe_gap, partition_organic, partition_organic_low_risk, time_gap, multi_risk) it enters.

What we learnedC. grox prepends a preview image of videos over 30 seconds before the storyboard

  • The grox media loader attaches a preview thumbnail to long videos verified — grox/core/data_loaders/media_loader.py (+100/−19).
  • A threshold length, an image field, and a label were newly introduced verified.
    • The threshold length constant is _VIDEO_PREVIEW_MIN_DURATION_MS = 30_000 (30 seconds).
    • A preview_image: bytes | None = None field was added to the Video pydantic model.
    • The label constant is PREVIEW_IMAGE_LABEL = "[Video Preview Thumbnail]".
  • The preview is attached after passing four entry conditions (a 4-way gate) when the config switch is on verified.
    • After the existing frame hydration, hydrate_video runs if grox_config.media_hydration.enable_video_preview_image: await cls.hydrate_video_preview(video, is_main_post and is_high_fav).
    • The entry conditions of hydrate_video_preview are the following four:
      1. video.convo_video is not None
      2. video.url exists
      3. video.videoInfo exists
      4. (video.videoInfo.durationMillis or 0) > _VIDEO_PREVIEW_MIN_DURATION_MS: more than 30 seconds
  • If it is a main post with high fav, the image is resized to the deluxe-only tile size verified.
    • If is_deluxe = is_main_post and is_high_fav, resize_tile(bs, tile_size) uses deluxe_image_tile_size; otherwise it uses image_tile_size.
    • If the download fails, media_loader.hydrate_video_preview_failed.count increases.
    • On success, media_loader.hydrate_video_preview.count increases and video.convo_video.preview_image = preview is set.
  • In the LLM input, the label, image, and newline go before the storyboard verified.
    • When preview_image exists, Video.interleave_storyboard adds three elements, [f"{PREVIEW_IMAGE_LABEL} ", self.preview_image, "\n"], to the front of the storyboard list. The label, the original image bytes, and a newline come in order before the storyboard composite.
    • In the _image_part-based chat marshaling path too, a text label is attached before the image via if c.preview_image: parts.append({"type": "text", "text": PREVIEW_IMAGE_LABEL}); parts.append(_image_part(c.preview_image)).

Why it matters (C) This commit added a third media representation: one representative frame of a long video, placed before the storyboard.

  • Since Gen4+12 (2026-09-01, 6384ca7), where motion-reveal frames (static/dynamic split) were added to the storyboard (from Gen4), the media representations grox passes to the LLM were these two forms.
  • A short video already has few storyboard frames and is representative enough. For a long video of 30+ seconds, however, a frame grid can blur the first impression, so the preview thumbnail first establishes "what this is".
  • Using a larger-tile preview for the Deluxe tier (main post with high fav) is also consistent with the existing design of tying LLM verdict cost to representativeness.

What we learnedD. Live broadcast frames became targets of two safety verdict pipelines

  • grox extracts frames of live broadcasts, and two PTOS safety pipelines judge these frames verified — grox/core/data_loaders/media_loader.py (broadcast part).
  • A broadcast loader was newly added to the media loader verified.
    • from broadcast_tools.broadcast import BroadcastLoader was added to media_loader.py, and MediaLoader._broadcast_loader: BroadcastLoader | None = None is the class cache.
    • _get_broadcast_loader() initializes it lazily. It uses the bearer_token=X_API_BEARER and guest_token=BROADCAST_GUEST_TOKEN environment variables.
  • Frames taken from a broadcast are normalized into a Video object verified.
    • _extract_broadcast_video(broadcast_metadata: BroadcastMetadata) -> Video extracts frames with BroadcastLoader.load_broadcast(broadcast_id, media_key=..., max_frames=config.video_max_frames, tile_size=config.video_tile_size, max_duration=broadcast_metadata.crop_seconds).
    • The extracted frames are normalized into Video(convo_video=ConvoVideo(frames=..., duration=interval, total_duration=result.video.sampled_duration)).
    • The duration is computed as (frames[-1].time_sec - frames[0].time_sec) / (len(frames) - 1).
  • The entry conditions of the adult-content leading-frames filter changed in two places verified. The target is the 6-gate condition folded into the canon at Gen4+17 (2026-09-08).
    • The conditions are in TaskSafetyPtosAdultContentLeadingFramesFilter._eligible_with_post.
  • First, the Deluxe fav gate is gone. The post now enters regardless of the fav threshold verified.
    • The old gate #2, post.get_fav_count() < grox_config.media_hydration.deluxe_fav_count_threshold: return cls._skip(post, "not_high_fav"), was deleted entirely.
  • Second, the post enters if a broadcast exists even without a long video verified.
    • The old code only looked at long_videos = [m for m in media if m.type == "video" and durationMillis >= 30s] and skipped with no_long_video if there were none.
    • The new code also computes broadcasts = [p.broadcast_metadata for p in (post, post.quoted_post) if p is not None and p.broadcast_metadata is not None and p.broadcast_metadata.broadcast_id]. The post enters if either long_videos or broadcasts is non-empty.
    • crop_seconds = 10.0 is also set for broadcasts, and a media = "both" | "broadcast" | "video" tag is attached to Metrics.counter("...eligible.count").
  • The sex/nudity safemodel verdict also receives broadcast frames as payload when the config switch is on verified.
    • A branch if grox_config.media_hydration.enable_local_broadcast_frame_extraction: was added to TaskSafetyPtosSafemodelSexNudity._collect_payloads.
    • This branch uniformly samples _MAX_FRAMES_PER_VIDEO = 8 from convo_video.frames of broadcast videos via _iter_broadcast_videos(post) and appends them as ("broadcast_frame", frame) payloads.
    • With the constant _FRAME_KINDS = {"video_frame", "broadcast_frame"}, both kinds are treated equally when computing has_video and n_video_frames.
    • _iter_broadcast_videos yields only those with broadcast_metadata.video.convo_video.frames for each of post and post.quoted_post.

Why it matters (D) With this commit, live broadcasts were promoted to a formal media payload and became verdict targets of two safety pipelines (adult-content leading-frames and safemodel sex/nudity) that were already applied to static video.

  • X's live video (broadcast, the successor of Periscope) was a blind spot of the grox safety pipelines until this commit. PTOS judged only the static media of tweets (photos, uploaded video), and live broadcasts were likely handed to other systems or left unjudged.
  • The Deluxe fav threshold, one of the 6 gates introduced in Gen4+17, was deleted a day later. The conditions of the leading-frames pipeline shrank to the following 5-gate set:
    • (a) prior PTOS result exists
    • (b) prior result is not NSFW
    • (c) video of 30+ seconds or broadcast
    • (d) prior AdultContent policy or fav ≥ 512
  • That is, the pool of candidates for re-judgment grows sharply. This decision reads as moving load control away from managing re-judgment load with the fav threshold and onto (d)'s ADULT_CONTENT_LEADING_FRAMES_UNCONDITIONAL_FAV = 512 and the prior conditions of (a)/(b) inferred.

What we learnedE. The multimodal post embedding was turned off in both registered ranking training configs

  • The multimodal post embedding was turned off in all ranking training configs verified — phoenix/README.md (+3/−3).
  • The "Multimodal post embedding" row of the nano training config table changed verified.
Before (49815da) After (75d93d9)
ranking nano v5 on xrecsys_seqpack; off on home_direct_packed off (home_direct_packed and xrecsys_seqpack)
  • The accompanying explanatory text also changed verified.

"the multimodal-embedding input is off in both, and on every registered ranking config, as the table shows" — phoenix/README.md, 2026-09-09

  • The on in the text must be read as a grammatical ellipsis of "off" ("off in both, and [off] on every registered ranking config") verified. Otherwise it contradicts the table.
  • That is, the multimodal post embedding was dropped from the training input in both registered ranking training configs (home_direct_packed flagship, xrecsys_seqpack seqpack) verified.

Why it matters (E) Gen4+17 (2026-09-08, 49815da) introduced a v8-search multimodal embedding stack dedicated to the grox search surface, opening a vector space next to ranking that is separate from v5 and v8.2. One day later, in this commit, the v5 input was turned off in the only ranking config that had v5 on.

  • That config is xrecsys_seqpack, and v5 had been on in it since the Gen4 release verified — phoenix/README.md table.
  • Combining the two events narrows the scenarios: we infer that v5 either did not deliver a meaningful improvement as a ranking input or lost out in the latency/quality tradeoff, and was temporarily removed from the training config midway through the move to v8-search inferred.
  • This commit has no code that connects v8-search to a ranking config. Whether the search surface embedding and the ranking embedding stay in separate vector spaces, or v8-search replaces v5, is something to check in the next commit.

What we learnedOther changes

  • A previous Phoenix bug that tried to schedule threads on isolated cores (isolcpus) inside a NUMA node was fixed verified.
    • The fix is in the NUMA CPU affinity handling in phoenix/xrex/utils/gpu.py. Two helpers, _parse_cpulist(text: str) -> set[int] and schedulable_cpus(*nodes: int) -> set[int], were introduced.
    • schedulable_cpus reads the CPU set of node N from /sys/devices/system/node/nodeN/cpulist, subtracts /sys/devices/system/cpu/isolated, and returns the result.
    • pin_process_to_local_numa split the existing numa.schedule.run_on_nodes(node) → numa.memory.set_membind_nodes(node) into two steps. It runs numa.memory.set_membind_nodes(node) first and os.sched_setaffinity(0, schedulable_cpus(node)) second, layering the exception handling.
    • If the node has no non-isolated CPU, it logs a warning and keeps only membind.
    • This appears to be the result of Phoenix training encountering an isolated-core configuration on real production hosts inferred.
  • Six feature-gated info logs that were emitted per component of the candidate pipeline were deleted verified.
    • These are the quiet-spans logs of candidate-pipeline. One #[cfg(feature = "quiet-spans")] tracing::info!(...) block was removed from each of filter.rs, hydrator.rs, query_hydrator.rs, scorer.rs, selector.rs, and source.rs (−40 lines in total).
    • They were quiet info-level logs emitted on each pipeline component execution. They appear to have been removed because they duplicate span records while the quiet-spans feature condition is always active inferred.
    • The string format of the error log also reverted from error!(component = self.name(), error = %err, "source_failed") to error!("{} Failed: {}", self.name(), err). The structured fields were removed and it regressed to a string log.
  • Phoenix inference code also changed slightly verified. This commit appears to have made a minor cleanup of the API surface.
    • The changed files are phoenix/xrex/configs/xrecsys.py (−1), phoenix/xrex/inference/model_runner.py (+2/−1), and phoenix/xrex/models/recsys_model.py (+5).
    • This may be work to wire up consumption of the ContentFeatures from Gen4+17 (2026-09-08), so it is something to revisit in the next commit.

Canon update services/x.md Updated the VF fail-closed statement at line 392 of the ranking and filter section. Also created one new subsection (introduced the "Gen4+18" subsection under ## Pipeline). Added the 75d93d9 sources to ## Sources.

Why it matters (overall) In this commit, the biggest code-quality improvement is the VF Action refactor, and the most visible product change is header-image-prompt-ad. Removing the multimodal embedding training config is the heaviest signal on how the input schema of X's ranking model is decided. Because the v5 input, on since the Gen4 release, was turned off one day after the v8-search stack was introduced, the follow-up on v8-search wiring needs watching.

  • If Gen4+17 was a large commit that moved five axes at once in a single day, Gen4+18 carries cleanup, follow-up, and expansion evenly across each axis.
    • VF unified the verdict axis (the largest and most fundamental refactor).
    • Ads added a new format (a new product feature).
    • grox improved LLM input with a preview thumbnail and widened safety pipeline coverage through broadcast unification.
    • The training config removed the multimodal embedding (a design-direction decision).

Corrections

  • The canon statement "VFFilter is fail-closed" was not fully negated; its wording changed.
    • It is at line 392 of services/x.md.
    • The verdict axis moved from matching on FilteredReason to an explicit Action enum.
    • The fail-closed character is retained (NotEvaluated → drop; in the Strato path, a non-SafetyResult reason is converted to Drop). However, the filter layer now newly filters out Tombstone and NotEvaluated.
    • Folded into the canon.

Radar

No items were added to the radar today. We parsed 30 arXiv cs.IR papers, and 0 passed the acceptance criteria (platform-company authors or production A/B claims). The four notable candidates are:

  • SequenceO1: End-to-End Ultra-Long (100K) Sequence Modeling in Recommendation with Low-Rank Caching
    • Already mentioned in the daily covering Gen4+17. It still has no production A/B.
  • PDMR: Passage-Driven Multi-ID Document Retrieval
    • It belongs to the Generative Retrieval family. It has no platform affiliation and no production results. Rejected.
  • Exploring Bottom-Up Clustering for Creating Semantic IDs
    • It is a clustering methodology for the Semantic ID line. It has no platform affiliation and is a theoretical increment. Rejected.
  • Q2D-Web: A Large-Scale Benchmark for Retrieval in Agentic RAG Systems
    • It is an Agentic RAG benchmark, outside the domain.

What to check next