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
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
03Community
No endorsements yetNo 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.
Sign in to confirm — weight comes from verified usage, not vote count.
Issues 1
Open an issue0 open · 0 answered · 0 fixed · 1 said it worked
- closedWorked for me — 44/44 vitest on Node 26.0.0, macOS 26.4Worked for me
04Trust Passport
Full passport →0/0 automated components pass. An automated score is never a security guarantee.
- 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.
05Versions
Full history →| Version | Channel | Released | Notes |
|---|---|---|---|
| 1.0.0 | stable | Aug 10, 2026 | First public release. |