x-algo-scoring
SKILL.md
X Algorithm Scoring
The X algorithm calculates a weighted engagement score for each post by combining predicted probabilities of 18 user actions. This score determines feed ranking.
Weighted Score Formula
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 Weights by Category
Positive Weights (Increase 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 |
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 |
Negative Weights (Decrease Score)
| 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 |
VQV Video Eligibility
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 // No VQV contribution for short videos or non-videos
}
}
Score Offset Logic
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::NEGATIVE_WEIGHTS_SUM) / p::WEIGHTS_SUM * p::NEGATIVE_SCORES_OFFSET
} else {
// Positive scores just add offset
combined_score + p::NEGATIVE_SCORES_OFFSET
}
}
Score Normalization
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);
Additional Scoring Stages
1. Author Diversity Scoring
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) + self.floor
}
Parameters: AUTHOR_DIVERSITY_DECAY, AUTHOR_DIVERSITY_FLOOR
2. Out-of-Network Scoring
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
});
Example Score Calculation
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
PostCandidate Score Fields
pub struct PostCandidate {
pub weighted_score: Option<f64>, // After WeightedScorer
pub score: Option<f64>, // Final score after all scorers
// ...
}
Related Skills
/x-algo-engagement- Reference for all 18 action types/x-algo-pipeline- Where scoring fits in the full pipeline
Weekly Installs
2
Repository
cloudai-x/x-algo-skillsInstalled on
windsurf2
opencode2
cursor2
codex2
claude-code2
antigravity2