Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/feedthejoe / src/core/signal/signals.js |
| Date | Not specified in source |
| Repository | Janady13/feedthejoe |
| Revision | 0e608a2b515a959bf35d2783428766624400ca3c |
| Branch | main |
| Path or archive member | src/core/signal/signals.js |
| Original SHA-256 | 152447a9373ca31c8ed0ce1fc496600c5dccbbbe5da33aa490631fe112a13a2d |
| Git blob SHA | 6c02db6b1c58e6cb54d78cbc94770116e94f6aba |
| Reading-copy SHA-256 | 152447a9373ca31c8ed0ce1fc496600c5dccbbbe5da33aa490631fe112a13a2d |
Recovered source transcript
Download complete source text ↓116 lines · 4.2 KB
/**
* The signal model.
*
* TikTok's ranking is not driven by likes. The public record on its recommender
* — the Monolith paper and the reporting on the internal "For You" documents —
* is consistent on one point: *completion rate* dominates, and explicit
* approval (the like) is a weak, late, and biased signal. Watch time relative
* to item length is the honest measure of whether attention was actually held.
*
* This module is the translation of that hierarchy to a reading surface. The
* analogue of "watched it to the end" is "dwelled on it for about as long as it
* takes to read", which is why COMPLETION is computed as a ratio against an
* estimated read time rather than as raw seconds. Raw seconds would simply rank
* long items above short ones.
*
* These weights are the tuning surface. They are constants here, not magic
* numbers scattered through the ranker.
*/
/**
* Signal kinds, ordered by the strength of the evidence they carry.
* @readonly
* @enum {string}
*/
export const Signal = Object.freeze({
/** Dwell time reached a full estimated read. The dominant positive signal. */
COMPLETION: 'completion',
/** Returned to an item already seen. Strong: costly to fake, rarely accidental. */
REWATCH: 'rewatch',
/** Copied the link or used the share affordance. Intent to show someone else. */
SHARE: 'share',
/** Opened the full piece from the feed card. */
EXPAND: 'expand',
/** Explicit save. Weak — deliberate approval is rarer and noisier than attention. */
SAVE: 'save',
/** Reached the contact surface from this item. The conversion signal. */
CONTACT: 'contact',
/** Scrolled past faster than reading speed permits. The only negative. */
SKIP: 'skip',
});
/**
* Relative weights. COMPLETION is the unit; everything else is expressed
* against it. CONTACT exceeds 1.0 because it is the outcome the whole surface
* exists to produce, so an item that precedes it should be strongly reinforced.
*
* SKIP is negative and deliberately large in magnitude relative to SAVE: on a
* feed, disinterest is expressed by scrolling, and a model that can only learn
* from positive signals will drift toward whatever it happened to show first.
*
* @readonly
* @type {Readonly<Record<string, number>>}
*/
export const SIGNAL_WEIGHT = Object.freeze({
[Signal.COMPLETION]: 1.0,
[Signal.REWATCH]: 0.85,
[Signal.SHARE]: 0.7,
[Signal.EXPAND]: 0.55,
[Signal.SAVE]: 0.35,
[Signal.CONTACT]: 1.2,
[Signal.SKIP]: -0.6,
});
/** Words per minute used to estimate a read. Deliberately conservative. */
export const READ_WPM = 240;
/**
* Estimated seconds to read a piece of the given length.
* Floors at 3s so that a one-line item cannot be "completed" by a glance.
*
* @param {number} wordCount
* @returns {number} Seconds.
*/
export function estimatedReadSeconds(wordCount) {
if (!Number.isFinite(wordCount) || wordCount <= 0) return 3;
return Math.max(3, (wordCount / READ_WPM) * 60);
}
/**
* Convert observed dwell into a completion ratio in [0, 1].
*
* Capped at 1: lingering for five times the read length is not five times the
* evidence, it usually means the tab was left open. That cap is what stops
* "walked away from the laptop" from becoming the strongest signal in the
* system.
*
* @param {number} dwellSeconds
* @param {number} wordCount
* @returns {number}
*/
export function completionRatio(dwellSeconds, wordCount) {
if (!Number.isFinite(dwellSeconds) || dwellSeconds <= 0) return 0;
return Math.min(1, dwellSeconds / estimatedReadSeconds(wordCount));
}
/**
* Resolve an observed event into a scalar learning weight.
*
* COMPLETION is scaled by how much of the item was actually consumed; every
* other signal is discrete and carries its full weight. An unknown kind
* contributes nothing rather than throwing — the ranker must never be taken
* down by an unrecognised event from the DOM layer.
*
* @param {{kind: string, completion?: number}} event
* @returns {number}
*/
export function weightOf(event) {
const base = SIGNAL_WEIGHT[event.kind];
if (base === undefined) return 0;
if (event.kind === Signal.COMPLETION) {
const ratio = Number.isFinite(event.completion) ? Math.min(1, Math.max(0, event.completion)) : 0;
return base * ratio;
}
return base;
}