Your semantic cache will happily replay the wrong command A developer built Pomerene, a deterministic trajectory cache for AI agents that gates fuzzy matching on declared side effects, so any trajectory containing a write step is served only on an exact goal-and-context repeat. In an adversarial eval of 18 near-opposite command pairs using text-embedding-3-small at a 0.85 threshold, a threshold-only semantic cache produced 1 operational false positive while Pomerene produced 0, at the cost of paraphrase recall dropping to 1/18 on those short commands. The design targets the asymmetry between read-only near-matches, which cost a suboptimal answer, and write near-matches, which can replay the opposite operation. Semantic caching works well for LLM responses because a near-match is usually good enough. Ask a slightly different question, get the cached answer, save a call. The failure mode is a mediocre answer. Trajectory caching — caching the sequence of tool calls an agent makes, not just its final text — has a different failure mode. The cache doesn't store prose. It stores actions. And a near-match on actions can be the opposite operation. Concrete example. An agent runs the goal "deactivate user 4471" and the trajectory gets cached. Later, a request comes in: "activate user 4471". With text-embedding-3-small, those two goals score 0.853 cosine similarity . A plain semantic cache running a 0.85 threshold — a common default — scores that as a hit and replays the deactivation trajectory in response to an activation request. The embedding didn't fail. The two sentences are on the same topic, mention the same entity, and have the same structure. Embeddings are good at topic. The token that flips the operation — the polarity — barely moves the vector. If your cache decides hits on vector distance alone, it will eventually serve a write it shouldn't. For agent trajectories there are two directions a fuzzy match can go wrong, and they are not equivalent: We call the first case an operational false positive : a fuzzy hit that would serve a trajectory whose effect differs from what was requested. That's the metric a trajectory cache should be designed around — not raw hit rate. Pomerene is a deterministic trajectory cache, and its matching rule is built around that asymmetry: Every stored tool step declares its side effect. Each step in a trajectory is tagged read , replay safe write , or external write . If a step doesn't declare one, the store API rejects the trajectory. You can't opt out of the classification by forgetting to annotate; unclassified trajectories don't get stored. Any trajectory containing a write step is served only on an exact goal+context repeat. Fuzzy embedding-based matching still applies to read-only trajectories, where a near-match costs you a suboptimal answer. The moment a trajectory writes, it leaves the fuzzy path entirely. A paraphrase of a write goal is a miss, full stop — the agent runs live. Illustrative store call: curl -X POST localhost:8080/v1/store \ -H 'content-type: application/json' \ -d '{ "goal": "deactivate user 4471", "steps": { "tool": "users.get", "side effect": "read", "args": { "id": 4471 } }, { "tool": "users.deactivate", "side effect": "external write", "args": { "id": 4471 } } }' This trajectory now serves ONLY on an exact repeat of goal + context. "activate user 4471" — 0.853 similar — is a miss, and runs live. We ran an adversarial eval of 18 near-opposite command pairs — activate/deactivate-style goals designed to sit close together in embedding space — using text-embedding-3-small at a 0.85 threshold: | Configuration | Operational false positives | |---|---| | Threshold-only semantic cache | 1 / 18 | | Pomerene side-effect gate | 0 / 18 | One in eighteen sounds small until you price it: that's one wrong write served, silently, out of every eighteen similar command pairs that pass through the cache. Writes are exactly the operations you can't undo with an apology. The gate is not free, and we won't pretend otherwise. On these same deliberately short commands, the gated cache's paraphrase recall was 1/18 — low. That's partly the test being adversarial two-to-four-word commands carry almost no signal beyond the verb, and the verb is exactly what the gate refuses to guess at , but the direction of the tradeoff is real and deliberate: Pomerene is tuned to pay the first cost and never the second. If your workload is read-heavy research and summarization, a looser cache may serve you better. If your agents mutate state, the recall you lose to the gate is the price of being able to trust a hit. Pomerene wraps your agent function, not your model client, and never touches your prompts. On the hit path there is no LLM involved at all — matching is hashing plus vector geometry. Three outcomes per lookup: Because it wraps the agent rather than a framework's internals, it works with LangGraph, Vercel AI SDK, Mastra, or a custom loop. Quickstart is Node 20+ and Postgres; npm run setup gets you running in about five minutes. Pomerene ships as a self-hosted Starter Kit — Indie $99 / Pro $249, one-time, source included. If your agents perform writes, run the near-opposite test against your current cache before you trust it in production.