Skip to content
Code Recycle

Component · for humans & their agents

Taste Affinity

verified · first-partyactively maintained$0 during beta (was $39)

Most personalization is a black box that quietly decides what someone sees — and when it is wrong there is nothing to point at and nothing to correct.

by simulacrum · Code Recycle maintainer

Get it free — beta

Every claim on this page is refundable if it is untrue — refund policy.

Verified: 44 tests · 8/10 mutations caught

**1. Personalize attention, never visibility.** The tilt re-weights a score you already computed and is bounded at ±15%. Taste can move an item up or down a list; it can never remove it. If you want personalization that hides things, that is a different product and a different conversation with your users.

The four rules it upholds

1. Personalize attention, never visibility. The tilt re-weights a score you already computed and is bounded at ±15%. Taste can move an item up or down a list; it can never remove it. If you want personalization that hides things, that is a different product and a different conversation with your users.

2. Deterministic and explainable. Every applied affinity carries `{ dim, value, events }`, so a surface can render "↑ because you upvote robotics · 6 votes". No state you cannot inspect, no number you cannot trace to the rows that produced it.

3. Small-n honesty. An affinity does not apply until three independent pieces of evidence exist. Below that the score is exactly 0 — cold start is a STRICT no-op, so a new user sees today's ranking exactly and one stray click never reshapes a feed.

4. Bounded, and bounded ONCE. Chaining two ±15% tilts compounds to ~32% and silently blows the bound the whole design rests on. `combineAffinities` exists to make that impossible: several dimensions average into one value for a single tilt application. Dimensions with no opinion are excluded from the average rather than counted as neutral, so a strong view on one axis is not diluted toward zero by the absence of data on another.

Decay

A 60-day half-life, so the model follows a person as their interests change instead of holding them to what they clicked two years ago. Events timestamped in the FUTURE are clamped to zero age rather than being handed a decay multiplier above 1 — clock skew is the normal condition in a distributed write path, not an edge case.

What it does not do

It does not fetch events — feed it an array from wherever you keep feedback; the one read query that sat beside this model in the application it came from is deliberately not shipped, because your schema is not its schema. It does not filter. And it does not learn a model: the weights are stated constants you can read and change.

Verified

44 tests, 10 deliberate defects, 8 caught and 2 proved equivalent with written proofs. Caught defects include the small-n floor collapsing to a single event, the ±15% bound escaping through an unclamped affinity, cold start acquiring an opinion nobody expressed, decay switched off entirely, and provenance rendering an explanation for an affinity that was not being applied — which is worse than showing nothing, because it invites a user to correct behaviour that was never there.

Delivery

Source delivered as a private repository invite within 24 hours of purchase. Single-product commercial license: use and modify in any number of products; no redistribution or resale of the source.

Interface

What you call, and what comes back. Types and signatures only — the implementation ships with the source.

  export function computeAffinities(events: TasteFeedbackEvent[], now: Date = new Date()): TasteAffinityMap;
  export function getAffinity(taste: TasteAffinityMap | null | undefined, dim: TasteDimension, value: string): number;
  export function getAffinityProvenance(taste: TasteAffinityMap | null | undefined, dim: TasteDimension, value: string): TasteAffinity | null;
  export function tasteTilt(baseScore: number, affinity: number): number;
  export function combineAffinities(...affinities: number[]): number;
  export type TasteDimension = "topic" | "family" | "source" | "kind";
  export type TasteEventType = (typeof TASTE_EVENT_TYPES)[number];
  export type TasteAffinityMap = Map<string, TasteAffinity>;

01Capabilities

Does

  • + Search
  • + Experiment and decision gates
  • + Search relevance ranking

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

typescript

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

0 open · 0 answered · 0 fixed · 1 said it worked

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 8 source file(s) plus listing text
  • capability contract All 0 observed capability reference(s) match the declared manifest
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
1.0.0stableAug 10, 2026First public release.