Calculate and explain X algorithm engagement scores. Use when analyzing post ranking, understanding score weights, engagement potential, or why one post ranks higher than another.
Use the skills CLI to install this skill with one command. Auto-detects all installed AI assistants.
Method 1 - skills CLI
npx skills i CloudAI-X/x-algo-skills/x-algo-scoringMethod 2 - openskills (supports sync & update)
npx openskills install CloudAI-X/x-algo-skillsAuto-detects Claude Code, Cursor, Codex CLI, Gemini CLI, and more. One install, works everywhere.
Installation Path
Download and extract to one of the following locations:
No setup needed. Let our cloud agents run this skill for you.
Select Provider
Select Model
Best for coding tasks
No setup required
The X algorithm calculates a weighted engagement score for each post by combining predicted probabilities of 18 user actions. This score determines feed ranking.
Score = Σ(weight × P(action)) for all 18 actions + offset
From home-mixer/scorers/weighted_scorer.rs:
fn compute_weighted_score(candidate: &PostCandidate) -> f64 {
let s: &PhoenixScores = &candidate.phoenix_scores;
let vqv_weight = Self::vqv_weight_eligibility(candidate);
let combined_score = Self::apply(s.favorite_score, p::FAVORITE_WEIGHT)
+ Self::apply(s.reply_score, p::REPLY_WEIGHT)
+ Self::apply(s.retweet_score, p::RETWEET_WEIGHT)
+ Self::apply(s.photo_expand_score, p::PHOTO_EXPAND_WEIGHT)
+ Self::apply(s.click_score, p::CLICK_WEIGHT)
+ Self::apply(s.profile_click_score, p::PROFILE_CLICK_WEIGHT)
+ Self::apply(s.vqv_score, vqv_weight)
+ Self::apply(s.share_score, p::SHARE_WEIGHT)
+ Self::apply(s.share_via_dm_score, p::SHARE_VIA_DM_WEIGHT)
+ Self::apply(s.share_via_copy_link_score, p::SHARE_VIA_COPY_LINK_WEIGHT)
+ Self::apply(s.dwell_score, p::DWELL_WEIGHT)
+ Self::apply(s.quote_score, p::QUOTE_WEIGHT)
+ Self::apply(s.quoted_click_score, p::QUOTED_CLICK_WEIGHT)
+ Self::apply(s.dwell_time, p::CONT_DWELL_TIME_WEIGHT)
+ Self::apply(s.follow_author_score, p::FOLLOW_AUTHOR_WEIGHT)
+ Self::apply(s.not_interested_score, p::NOT_INTERESTED_WEIGHT)
+ Self::apply(s.block_author_score, p::BLOCK_AUTHOR_WEIGHT)
+ Self::apply(s.mute_author_score, p::MUTE_AUTHOR_WEIGHT)
+ Self::apply(s.report_score, p::REPORT_WEIGHT);
Self::offset_score(combined_score)
}| Action | Weight Constant | Signal Type |
|---|---|---|
| Favorite | FAVORITE_WEIGHT | High value engagement |
| Reply | REPLY_WEIGHT | High value engagement |
| Retweet | RETWEET_WEIGHT | High value engagement |
| Quote | QUOTE_WEIGHT | High value engagement |
| Follow Author | FOLLOW_AUTHOR_WEIGHT |
| Action | Weight Constant | Signal Type |
|---|---|---|
| Not Interested | NOT_INTERESTED_WEIGHT | Negative signal |
| Block Author | BLOCK_AUTHOR_WEIGHT | Strong negative |
| Mute Author | MUTE_AUTHOR_WEIGHT | Strong negative |
| Report | REPORT_WEIGHT | Strongest negative |
Video Quality View (VQV) weight only applies if video meets minimum duration:
fn vqv_weight_eligibility(candidate: &PostCandidate) -> f64 {
if candidate
.video_duration_ms
.is_some_and(|ms| ms > p::MIN_VIDEO_DURATION_MS)
{
p::VQV_WEIGHT
} else {
0.0
Handles negative combined scores to ensure proper ranking:
fn offset_score(combined_score: f64) -> f64 {
if p::WEIGHTS_SUM == 0.0 {
combined_score.max(0.0)
} else if combined_score < 0.0 {
// Negative scores get scaled offset
(combined_score + p
After weighted scoring, scores are normalized (implementation in util/score_normalizer.rs, excluded from open source):
let weighted_score = Self::compute_weighted_score(c);
let normalized_weighted_score = normalize_score(c, weighted_score);Penalizes multiple posts from the same author to promote variety:
// From home-mixer/scorers/author_diversity_scorer.rs
fn multiplier(&self, position: usize) -> f64 {
// First post from author: full score
// Second post: score × decay_factor
// Third post: score × decay_factor²
(1.0 - self.floor) * self.decay_factor.powf(position as f64) +
Parameters: AUTHOR_DIVERSITY_DECAY, AUTHOR_DIVERSITY_FLOOR
Adjusts scores for posts from accounts user doesn't follow:
// From home-mixer/scorers/oon_scorer.rs
let updated_score = c.score.map(|base_score| match c.in_network {
Some(false) => base_score * p::OON_WEIGHT_FACTOR, // Reduced weight
_ => base_score, // Full weight for in-network
});For a post with these predicted probabilities:
favorite_score: 0.12 (12% chance of like)reply_score: 0.03 (3% chance of reply)retweet_score: 0.05 (5% chance of retweet)not_interested_score: 0.02 (2% chance of negative signal)Weighted Score =
0.12 × FAVORITE_WEIGHT +
0.03 × REPLY_WEIGHT +
0.05 × RETWEET_WEIGHT +
0.02 × NOT_INTERESTED_WEIGHT (negative) +
... + offset
pub struct PostCandidate {
pub weighted_score: Option<f64>, // After WeightedScorer
pub score: Option<f64>, // Final score after all scorers
// ...
}/x-algo-engagement - Reference for all 18 action types/x-algo-pipeline - Where scoring fits in the full pipeline| Very high value |
| Share | SHARE_WEIGHT | Distribution signal |
| Share via DM | SHARE_VIA_DM_WEIGHT | Distribution signal |
| Share via Copy Link | SHARE_VIA_COPY_LINK_WEIGHT | Distribution signal |
| Photo Expand | PHOTO_EXPAND_WEIGHT | Interest signal |
| Click | CLICK_WEIGHT | Interest signal |
| Profile Click | PROFILE_CLICK_WEIGHT | Interest signal |
| VQV | VQV_WEIGHT | Video engagement (conditional) |
| Dwell | DWELL_WEIGHT | Attention signal |
| Quoted Click | QUOTED_CLICK_WEIGHT | Interest signal |
| Dwell Time | CONT_DWELL_TIME_WEIGHT | Continuous attention |