Ranking system¶
iFixedX ranks the same corpus two ways so you can compare X-style engagement ordering with a text + recency + anti-spam ranker. Scoring runs on the API server (server/search.ts); the client displays score and optional breakdown per tweet.
Ranker vs mixer (boundary)¶
Stage |
Where |
What it does |
|---|---|---|
Ranker |
|
Assigns |
Corpus cap |
|
Slices tweets before |
Mixer |
|
Merges live batch + Pipetrix canonical, unseen-first, tab filters — does not re-score |
Home Top shows one ranked list (platform or refined) after mixer merge and time-window filter. Changing sliders requires a new API fetch (or re-rank from stored corpus on Explore).
Modes¶
UI label |
API |
Character |
|---|---|---|
Default (engagement-style) |
|
Stronger weight on likes/retweets (log-scaled) |
iFixedX rank |
|
Query overlap + phrase match + recency − spam; weaker engagement |
Home Top tab uses one of these lists (after mixer merge and time filter). Explore always computes both for comparison.
Scoring components¶
Each tweet gets a numeric score and breakdown object:
Key |
Meaning |
|---|---|
|
Token overlap between query and post text |
|
Bonus when full query appears as substring |
|
Exponential decay, 14-day half-life, × |
|
|
|
Negative — hashtags, promo words, tag-based bait (refined only) |
Final score: overlap + phrase + recency + engagement - spam (spam stored in breakdown as negative spam).
Recency formula (code)¶
ageHours = max(1, hours since createdAt)
decay = exp(-ln(2) * ageHours / (24 * 14))
recency = decay * 3.2 * recencyWeight
recencyWeight is clamped to [0, 1] on the API.
Refined-only rules¶
With a non-empty query, if overlap + phrase < 0.8, score is multiplied by 0.35 (weak textual match sinks).
Strict match enables hashtag/promo penalties and extra penalty when tags look engagement-bait shaped.
Home / empty query¶
On For You, the query string is often empty. Overlap/phrase are ~0; recency and engagement (platform mode) still sort the feed. This is intentional — search ranking rules do not demote the whole timeline.
Ranking lab sliders¶
Controlled in UI, sent to /api/compare and /api/for-you:
Parameter |
Default (server) |
Effect |
|---|---|---|
|
|
0 = ignore age; higher = prefer newer posts |
|
|
Anti-spam / hashtag penalties |
|
Compare: 30; For You: 35; xAI home: 12 (max 25) |
Corpus size before |
Explore also supports omitRetweets (Recent Search query -is:retweet).
SearchParams in search.ts only includes q, mode, recencyWeight, strictMatch — maxResults is enforced in route handlers, not inside scoreTweet.
Slider values are snapshotted into Pipetrix mixerMetaJson.rankingLab on ingest — see Ranking lab snapshot.
API flow¶
corpus[] → rankTweets() → platform_style[] + refined[]
Explore:
GET /api/compare?q=…&source=mock|liveHome:
GET /api/for-you(live / mock / xai paths)
Fairness direction¶
Product goal: better candidate quality and seasoning, not reserved “boost slots.” See Fair ranking philosophy.
Future work (exp-sharing, Grok semantic scores) should extend scoreTweet / mixerMetaJson, not a parallel ranker DB. Raw Grok batches for replay live in Grok ingests.