The OneRep coach remembers things between conversations. Some you tell it outright: an injury, a schedule, a food you will not eat and have your reasons. Some it writes for itself as it goes: you skip Fridays, you like higher reps on pressing, your breakfast is oats, it's always oats.
Both kinds went into the same store. One row per key, no ceiling. Reads took the forty newest.
Sit with the arithmetic of that for a moment, as we were eventually forced to. Past forty memories, what the coach remembered was whatever it had most recently written about its own observations. Your "bad shoulder, no overhead pressing" (typed by you, about your actual body) would slide out of context behind a fortnight of machine-generated notes about oats. And then the coach would program overhead press.
That is not a context-window problem. That is a product forgetting the one thing it was told that mattered, in favour of trivia it invented; a failure mode I would describe as uncomfortably human, and leave it there.
Forgetting as policy
The fix was to make eviction explicit, ordered, and written down where it can be argued with, which is the only place a policy is worth anything:
- Anything the user said themselves is never evicted. They typed it. It is not ours to discard. This rule needed no meeting.
- Safety-shaped memories are never evicted, whoever wrote them.
- Weekly episodes keep to a fixed recent count: they exist to give the coach a feel for the last few months, not a diary. Nobody needs their diary read back to them in full. Nobody survives it.
- Everything else goes oldest-first, without ceremony.
In code, two sets and a ceiling:
const PROTECTED_CATEGORIES = new Set([
"injury", "safety", "medical", "allergy", "constraint",
]);
const USER_SOURCES = new Set(["user", "manual", "onboarding"]);
export function isProtected(memory: StoredMemory) {
return (
USER_SOURCES.has(memory.source.toLowerCase()) ||
PROTECTED_CATEGORIES.has(memory.category.toLowerCase())
);
}
Sixty stored memories, fourteen weekly episodes. Roughly a season. A season is about as far back as anyone honestly remembers anyway.
Provenance is the entire design
The load-bearing field is source. Every memory records who wrote it (the user, onboarding, a manual edit, or the model), and once that field exists, the eviction rule practically writes itself, because the two kinds of memory cost wildly different things to lose.
Lose a model-authored observation and you get a marginally less personalised reply; the model will re-derive it next week, deriving things being its one reliable talent. Lose a user-authored constraint and you get a coach programming a movement somebody's shoulder cannot do. These are not the same magnitude of error, and there is no defensible reason to let one LRU pretend they are.
The category set exists for the awkward middle case: sometimes the model records the constraint, because you mentioned the shoulder in passing rather than typing it into a form. A safety fact is a safety fact regardless of whose hand held the pen. So protection is a union of the two conditions, never an intersection.
Everything outside both sets is genuinely disposable and is treated with the disposal it deserves. Oldest first. No eulogy.
What we deliberately did not build
No importance scoring. The fashionable move is to have a model rate each memory and evict the low scorers. We won't, and here is the nightmare, fully lit: a scorer decides "user has a nut allergy" reads like low-signal boilerplate (it does read like boilerplate; that's what makes it lethal), deletes it, and nobody learns this until it matters, at which point it has mattered. A rule that says this category never leaves cannot produce that outcome. It can only be too conservative, and too conservative is the one direction in which this system is allowed to fail.
No unbounded store. The tempting dodge is keep everything, retrieve selectively. That relocates the problem rather than solving it. The same "which forty" question now happens at read time, per request, non-deterministically, where no one can watch it happen. A bounded store with a written eviction policy is a thing you can print out and read to a lawyer. I've written for worse audiences.
No merging by similarity. Consolidation collapses duplicate keys, not similar meanings. "Bad left shoulder" and "shoulder impingement, left" remain two memories. Mildly wasteful. Never wrong. I'll take that trade every day of the week and twice on the days I'm behind deadline.
The line worth stealing
The system prompt has never contained a rule saying don't program around an injury the user reported, because the injury is always in the context, so the rule has nothing to do. The behaviour comes from the eviction policy, not from an instruction.
Most of what looks like model behaviour is data selection wearing a costume. If your assistant keeps forgetting the one thing that matters most about someone, the prompt is almost never where the bug is. It rarely is with people, either, but that's a different book, and I keep not writing it.
Related
The per-request version of this question (what leaves a single message's context, and in what order) is in what the coach drops first.
The AI coach ships in OneRep, on a monthly allowance. The tracking underneath is free and unlimited.