# Tastebud Memory

> A chef's palate for AI agent memory; Un-mix any day's work into its exact projects, and detect workstreams nobody has named yet. Hyperdimensional fingerprints, zero dependencies.

- **Type:** MCP server
- **Install:** `agentstack add mcp-mikhail-za-tastebud-memory`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Mikhail-Za](https://agentstack.voostack.com/s/mikhail-za)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Mikhail-Za](https://github.com/Mikhail-Za)
- **Source:** https://github.com/Mikhail-Za/tastebud-memory

## Install

```sh
agentstack add mcp-mikhail-za-tastebud-memory
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 👅 tastebud-memory

**Compositional project fingerprints for AI agent memory.** Give every project a deterministic
high-dimensional identity vector. Every day's work becomes a weighted blend of those vectors.
One "taste" (a dot product) decomposes any day back into its exact ingredients, enumerates
every day a project ever touched, and detects ingredients **nobody has named yet**.

Zero dependencies. Two JSON files. ~600 lines of Node.

## What it looks like

```console
$ node tastebud.mjs decode 2026-01-12        # un-mix one day from its 4096-dim vector alone
2026-01-12 - Dosing pump driver; recipe-site deploy; soil sensors moved to bed 2
recovered from vector alone (vs actual):
  aquarium-controller          est=0.502  actual=0.500
  recipe-site                  est=0.296  actual=0.300
  garden-sensors               est=0.191  actual=0.200

$ node tastebud.mjs gaps                     # what's been worked on but never documented?
workstreams in logs with NO project file:
  aquarium-controller            4 day(s)  mass=2.150  first=2026-01-10
  home-lab                       3 day(s)  mass=1.550  first=2026-01-05
  sourdough-lab                  2 day(s)  mass=0.900  first=2026-01-15  [NOT EVEN IN CODEBOOK]

$ node tastebud.mjs tasteslike sourdough-lab # the unknown ingredient: what is it close to?
sourdough-lab  [UNKNOWN INGREDIENT - not in codebook]
keeps company with (rarity-weighted co-occurrence):
  recipe-site                  0.463
  aquarium-controller          0.201

$ node tastebud.mjs unknowns                 # triage: which unknowns are RIPE to decide on?
unknowns: 1 total  (decide 1, maturing 0)
  sourdough-lab                open 155d   2 day(s)  mass=0.900  [OVERDUE  ]  -> alias->recipe-site
```

Those last two are the headline feature. `sourdough-lab` doesn't exist anywhere as a project:
the nightly tagger *invented* the slug because nothing in the codebook fit, and the system
flagged it and placed it next to its nearest relative. Your agent notices new workstreams
forming **before you've named them** and then helps you decide what to do about each one. The
`-> alias->recipe-site` is a *recommendation*, not a decision: `sourdough-lab` keeps company
almost entirely with `recipe-site`, so the system reads it as the same work and suggests folding
it in. You stay in charge: you could mint it as its own project instead, dismiss it, or wait.

## The pipeline

```
daily log (markdown)
     │  nightly LLM tagger (rules + your codebook; invents slugs when nothing fits)
     ▼
composition row   {"date":"2026-01-12","major":[{"slug":"aquarium-controller","w":0.5},...]}
     │  deterministic: slug string → seeded ±1 hypervector, day = weighted sum
     ▼
fingerprint (4096-dim)
     │
     ├─ decode / where / first / cooccur / window / diff / gaps   (exact membership queries)
     ├─ similar / drift / tasteslike / backtest                   (vector-layer extras)
     ├─ unknowns / mint / alias / dismiss / watch / autofile      (ripen-then-decide triage loop)
     └─ MCP server → your agent tastes before it reads
```

## The idea (and where it came from)

This started as a question about hexadecimal colors: *what if every project had a unique color,
and a day's work blended them into a new color you could un-mix?* The problem is that color
can't do that. Three channels can't carry the membership of 50 projects, which is why mixed
paint can't be un-mixed.

The metaphor that actually works is a **chef's palate**. A trained chef tastes an unfamiliar
dish and names every ingredient in it, estimates the proportions, and (the key move) notices
when there's *something in the dish he doesn't recognize*. That's what this does, with
~30-year-old math: [hyperdimensional computing](https://link.springer.com/article/10.1007/s12559-009-9009-8)
(Kanerva) / vector symbolic architectures.

- Each project slug deterministically seeds a **4,096-dimension ±1 vector**. Random
  high-dimensional vectors are nearly orthogonal, so they don't interfere.
- A day's fingerprint is the **weighted sum** of its projects' vectors.
- **Decoding** is a dot product against the codebook: projects in the blend score close to
  their weight, and absent projects score close to zero. Mixing is reversible, the thing color
  couldn't do.
- **The residual**, the mass the codebook can't explain, is the unknown ingredient detector.
  When your agent starts working on something that has no name yet, this notices *before you do*.

## Why bother? (what semantic search can't do)

We baselined an agent's real memory (92 days, ~400 files) with embedding search before building
this. Semantic search was good at date lookups and well-named topics, and structurally
incapable of:

- **Enumeration**: "list ALL days that touched project X" (search returns representatives, never
  the complete set)
- **Origin under aliasing**: "when did X start, including under its old name?" (recency and
  volume bury the origin; this was a total miss in our baseline)
- **Temporal set-difference**: "active in March, dead by June"
- **Absence**: "which workstreams never got documentation?" (you can't embed a negation)

One concrete failure from that baseline, because it's instructive. A business project had been
renamed early in its life, and we asked search *"when did this start, including under its
earlier name?"* with three increasingly charitable query phrasings. Every one returned the
later, high-volume era of the project; the actual origin date and rename event never surfaced,
because similarity ranking structurally favors where the *bulk* of the writing is, and origins
are by definition thin. `tastebud.mjs first ` answers it exactly, because the codebook
records the alias and the composition table records the date. Absence queries fail even worse:
asked "which workstreams never got a project file," embedding search matched the literal phrase
"project file" to documentation *about the memory system itself*. It cannot reason about what
isn't there.

Fingerprints answer all four exactly, in milliseconds, because composition is *recorded*, not
inferred. Tastebud **complements** semantic search (meaning); it doesn't replace it (membership).

## The unknown-ingredient loop (detect, ripen, decide, reverse)

Detecting an unnamed workstream is only half the job. The other half is deciding what it *is*,
without being nagged about every fleeting one-off and without making a single irreversible
mistake. Tastebud closes that loop with a full triage + reversible-decision system. The chef
keeps tasting; you only get asked once a dish is worth a verdict.

**Detection is continuous but silent.** Every night the sweeper notices new unknown slugs and
logs them. No pings. A brand-new slug seen once is not a reason to interrupt you.

**Mature before asking.** An unknown is *not* surfaced for a decision until it ripens: it has to
recur across days, accumulate real major weight, or age past about a week. Thin one-offs sit
quietly in a **Maturing** bucket and never nag. A workstream that shows up for one afternoon and
vanishes was noise; a workstream that keeps coming back has earned a question. All the thresholds
are env-tunable (`TASTEBUD_RIPE_DAYS`, `TASTEBUD_RIPE_MASS`, `TASTEBUD_RIPE_AGE_DAYS`, and more).

**Auto-file the obvious.** If an unknown already has a project file on disk (config `projectsDir`),
the nightly sweeper files it into the codebook automatically. A file means a human already judged
it a real project, so there is nothing to ask. Logged, not pinged.

**Weekly digest decides only ripe items.** Once a week a review surfaces *only* the ripe unknowns,
each with a recommendation (**mint** / **alias** / **dismiss** / **watch**) plus the context behind
it, pushed through the config `notifyCommand`. Everything still maturing stays out of your way.

**Every decision is reversible.** Triage state lives in a persistent ledger
(`/unknowns-ledger.json`). Nothing you decide is permanent:

- **dismiss** means "not now," not "never." A dismissed one-off *revives* if it starts growing
  again, and comes back recommended as something to watch.
- **mint** writes the slug into the codebook, and is fully undoable with `mint --undo`.
- **watch** parks it on a watchlist: out of the decide queue, still in view.
- **alias** folds a name into an existing project and resolves it. The engine treats codebook
  aliases as known, so the unknown leaves the list for good (until you remove the alias).

```console
$ node tastebud.mjs unknowns                 # what is ripe to decide on right now?
unknowns: 1 total  (decide 1, maturing 0)
  sourdough-lab                open 155d   2 day(s)  mass=0.900  [OVERDUE  ]  -> alias->recipe-site

$ node tastebud.mjs alias sourdough-lab recipe-site   # fold it in; it now resolves
$ node tastebud.mjs watch sourdough-lab "keep an eye on it"   # or defer the call
$ node tastebud.mjs mint sourdough-lab --class product        # or make it its own project
$ node tastebud.mjs mint sourdough-lab --undo                 # changed your mind: undo it
```

The recommendation is a hint, never a decision. Here `sourdough-lab` co-occurs almost entirely
with `recipe-site`, so the system suggests aliasing it; a human who knows it is a distinct effort
can mint it instead. The point of the loop is that you make the call, late enough to be sure, and
can always take it back.

## Quickstart (60 seconds)

```bash
git clone https://github.com/Mikhail-Za/tastebud-memory && cd tastebud-memory
node tastebud.mjs check                      # validate the sample data
node tastebud.mjs decode 2026-01-12          # un-mix one day from its vector alone
node tastebud.mjs where aquarium-controller  # every day that project ever touched
node tastebud.mjs gaps                       # workstreams with no documentation
node tastebud.mjs tasteslike sourdough-lab   # an UNKNOWN ingredient: what is it close to?
node tastebud.mjs backtest aquarium-controller  # would the detector have caught this project emerging?
node tastebud.mjs unknowns                   # the triage queue: which unknowns are ripe to decide?
node tastebud.mjs color 2026-01-12           # the original metaphor, as garnish
```

The unknown-ingredient loop adds its own small command set (all reversible, all snapshot before
they write):

```bash
node tastebud.mjs unknowns --write           # write unknowns-report.md (Decide / Watching / Maturing)
node tastebud.mjs alias sourdough-lab recipe-site   # fold a name into a project; it now resolves
node tastebud.mjs mint sourdough-lab --class product  # promote an unknown to its own codebook slug
node tastebud.mjs mint sourdough-lab --undo  # reverse a mint
node tastebud.mjs dismiss sourdough-lab "one-off"   # "not now"; revives if it grows again
node tastebud.mjs watch sourdough-lab        # park on the watchlist, out of the decide queue
node tastebud.mjs decisions                  # the persistent decision ledger, grouped by status
node tastebud.mjs digest                     # the daily report: ripe decide queue + recent decisions (push via notifyCommand)
node tastebud.mjs autofile                   # dry-run filing unknowns that already have a project file
```

The sample data is a fictional fortnight that includes an emerging project and an unknown
ingredient, so every command above demonstrates something real.

## Using it on your own memory

1. **Codebook**: list your projects in `codebook.json` (slug + aliases + `has_file`). Slugs are
   permanent (they seed the vectors); add aliases instead of renaming.
2. **Backfill**: have a strong LLM read each daily log and emit `{major:[{slug,w}], minor:[], new:[]}`
   per day (rules in `examples/nightly-prompt.md`). Verify a sample before trusting it. See
   `docs/methodology.md` for the gate/backtest protocol we used.
3. **Nightly**: schedule your platform's LLM to tag yesterday into `inbox/.json`
   (prompt template provided), and run `node tagger.mjs nightly --write` an hour later as the
   deterministic sweeper. If the primary fails, a local model takes over and you get an alert
   with the reason (`notifyCommand` in config can point at Slack, ntfy, a webhook, anything).
   The same nightly pass logs new unknown slugs silently and auto-files any that already have a
   project file (config `projectsDir`); it does not interrupt you for either.
4. **Weekly decision loop**: once a week run `node tastebud.mjs unknowns --write` and review only
   the ripe unknowns, each with a recommendation. Decide with `mint` / `alias` / `dismiss` /
   `watch`; every call is reversible and snapshots before it writes. See
   [`docs/production-pattern.md`](docs/production-pattern.md) for the wiring.
5. **Agent integration**: register `mcp-server/server.mjs` (standard MCP, stdio) so your agent
   can *taste before reading*: decode a day in milliseconds, then fetch only the logs that matter.

## What this is not

- **Not a summarizer.** A fingerprint is an index, not the content. The chef recovers the
  ingredient list, not the recipe steps. Taste, identify, then fetch.
- **Not magic capacity.** A bundle reliably holds a few dozen constituents at D=4096. Plenty
  for a day, wrong for a year; aggregate windows instead.
- **Not a replacement for the codebook.** Decoding requires reference vectors, like a palate
  requires training. Unknown ingredients are detected as *unexplained mass*, then triaged.

## Honest engineering notes

The full methodology, including the kill-gates this project had to pass before going live,
is in [`docs/methodology.md`](docs/methodology.md). Highlights: an adversarial verification pass
re-derived 31 of 92 days blind and found 2 real tagging errors (93.5% faithful); backtests
flagged emerging projects within 0-2 days; and our honest finding that **the composition table
does most of the query work**. The vector layer earns its keep on decode, drift, similarity, and
fixed-size encoding, not on basic lookups. Production wiring (primary LLM cron + deterministic
sweeper + alerted local fallback) is in [`docs/production-pattern.md`](docs/production-pattern.md).

## License

MIT. Built by Mikhail Zaidi with Claude (Anthropic), June 2026.

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Mikhail-Za](https://github.com/Mikhail-Za)
- **Source:** [Mikhail-Za/tastebud-memory](https://github.com/Mikhail-Za/tastebud-memory)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-mikhail-za-tastebud-memory
- Seller: https://agentstack.voostack.com/s/mikhail-za
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
