Every message you send the OneRep coach arrives with a workspace: one object holding your profile, goals, today's plan, recent workouts, food entries, measurements, recovery signals, presets, recipes, remembered facts. It is JSON.stringify'd straight into the user turn. No formatter, no prose summary, no retrieval pipeline with a name and a logo. A dictionary, thrown at a model, which is less dignified than the industry likes to describe it and works better than most of what the industry describes.
This is lovely right up until somebody has forty presets, thirty recipes and a year of logs. Then the object is enormous, the request is expensive, and (the part that actually matters) it risks being truncated somewhere neither we nor the model controls, which is the worst place for anything to be decided.
So, a budget:
/** Roughly 15k tokens, leaves room for the system prompt, history, and reply. */
export const MAX_WORKSPACE_CHARS = 60_000;
Most of the restraint happens upstream: each source projects down to what it needs at query time. The budget is the backstop. But a backstop that fires must choose what to sacrifice, and choosing what to sacrifice is this entire article. It's most articles, if you read them at the right hour.
An ordered list, not a scoring function
The trimmer is a list of steps, applied in order, stopping the moment the object fits:
for (const step of TRIM_STEPS) {
if (step.apply(working)) truncated.push(step.field);
if (JSON.stringify(working).length <= maxChars) break;
}
No relevance model, no embeddings, no cleverness of any kind. A fixed order, lowest value first: recipe ingredients get capped at four before food entries get capped at twenty, which happens before body measurements, which happens long before recent workouts are touched.
We were tempted by clever ranking, the way one is tempted by anything expensive at midnight, and we declined. A ranked trimmer is non-deterministic in exactly the place you can least afford surprises: two identical-looking conversations get different context, the coach contradicts itself between them, and you can reproduce neither. A fixed order means that when the coach says something odd, you read one list, top to bottom, and know precisely what it did and did not see. Auditable beats clever. Write that on something.
Some fields are structural, not informational
The comment above the list is the part I'd keep if I had to burn the rest:
today,routine,profile,goals, and every preset/recipe id and name are deliberately absent: operations reference those by ID, so trimming them would break the model's ability to act rather than merely narrow its context.
Here is the distinction the whole design balances on. Cutting foodEntries from sixty to twenty makes the coach less informed. Cutting a preset's ID makes the coach unable to log that preset, and a language model does not report a missing capability, because reporting missing capabilities is not what it was raised to do. It will invent an ID, the operation will fail validation, and your user meets a coach that has confidently claimed to log breakfast and has logged nothing. The most dangerous sentence a model produces is the one it has no way of knowing is false.
So identity is exempt at any size; only detail is on the menu. A trimmed preset loses its item list, never its name and ID; the coach can still act on it, it just can't recite what's inside.
Two steps cut by meaning rather than count, and I'm fond of both. The programming block keeps its deload verdict and weekly volume and sheds the accessory lifts off the tail: the lifts are ordered most-trained-first, and the two summary lines carry the whole analysis anyway. The recovery block drops the raw HRV, sleep, steps and resting-heart-rate objects and keeps the notes: four small numbers versus the sentences a coach would actually say out loud. When forced to choose between the data and the meaning, keep the meaning. This applies broadly. Broadly, nobody does it.
The truncated field, and the bug we grew in it
The trimmer returns a list of what it cut, and that list goes to the model so it can hedge honestly: I'm only seeing your last twenty meals here. Cheap, truthful, humane.
The first version reported every step it attempted rather than every step that removed something. A field that was empty all along still got named in the confession.
The result was a coach apologising for sins it had not committed, telling a user with no body measurements whatsoever that it was working from a partial measurement history. And it sounded exactly like a real limitation, which is the worst kind of bug in a product where the model's account of its own state is the only account the user will ever get. A false confession is still a lie. It's just a lie wearing humility.
Every step now returns whether it actually changed anything, and the type carries a comment explaining why, because this is precisely the safeguard a future maintainer will simplify away some innocent Tuesday:
A step that found nothing to cut must not be reported:
truncatedis what tells the model to hedge its claims, and naming a field that was empty all along makes it apologise for missing history the user never had.
The general shape
Context budgets get discussed as token accounting. They are not token accounting, or not only. The moment you must choose what leaves the window, you are writing product behaviour: deciding what your assistant is permitted to forget about a person, in what order, and whether it will own up to it afterward.
Ours is a hardcoded list in one file with a comment on every position. Nobody will give a conference talk about it. But at 1 AM, with a confused user in the inbox, auditable is worth more than sophisticated ever was. And 1 AM is when these things are read. Trust me on the hour.
Related
The rules for what the coach forgets permanently, as opposed to per-request, are in never evict what the user typed.
The AI coach is part of OneRep. The tracking underneath it is free and unlimited.