Interest Is Current, Not Cumulative
The naive profile sums every act ever taken. It is wrong in a specific and permanent way: a fortnight spent reading intensively about one subject becomes a fixture, and every signal afterwards has to out-shout it. The profile stops tracking what someone cares about and starts recording what they once cared about most.
Exponential decay fixes this with one multiplier. Each contribution is scaled by one half raised to the age in half-lives, so a two-week-old save counts half, a month-old save a quarter. Nothing is deleted and no threshold is crossed — old interests fade continuously, which means a shelf recovers on its own when attention moves, with no reset button and no explaining to the reader why their history was thrown away.
Two Different Half-Lives, Doing Two Different Jobs
The profile's half-life governs how fast taste is allowed to change. It should be slow — a couple of weeks — because taste genuinely is stable and a jumpy profile chases noise.
Freshness is a separate term with a much shorter half-life, and it answers a different question: not "do you care about this subject" but "is this still news." Collapsing the two into one number is a common shortcut and it produces a shelf that either forgets your interests in two days or shows you last week's headlines. They are independent axes and deserve independent constants.
Weight the Acts by What They Cost
Opening an article is weak evidence — it costs a tap and is often regretted. Saving is strong: it is a deliberate claim of future value. Sharing is comparable, since publishing something is a public commitment. An explicit dislike is the strongest signal available and deserves a magnitude to match, because a reader who takes the trouble to press it is telling you something more precise than any amount of inferred behavior.
The ordering matters more than the exact numbers. What you must not do is weight by how easy the signal is to collect, which is how systems end up optimizing for the tap and calling it engagement.
The Afternoon a Round Number Cost
Here is a bug worth more than its size. Muting fires when a weight falls below a threshold, and the threshold was parked on a round number that matched the dislike weight exactly. It never fired.
The reason is decay. A contribution is only at its full nominal value at the instant it is recorded; a microsecond later the multiplier has shaved it. So a single dislike, weighted at exactly the negative of the threshold, produces a weight that is always fractionally above the bar, forever, no matter how obvious the intent. Moving the threshold to sit between the natural steps rather than on one fixed it. The general form: in any system with continuous decay, never place a threshold exactly on a value the system can produce — comparison against a decayed quantity is a comparison against something strictly smaller than what you wrote down.