The turn where the state did not arrive
A user asks about something established forty turns ago and gets a blank — not a refusal, not a hedge, just a reply written as though the subject had never come up. The next message is fine. Nothing is in the error log, because nothing errored. The turn was assembled, sent and returned successfully.
One of the pieces of state was not there when the input was built, and assembly does not require all of them.
Assembly is a fan-in with optional inputs
Every turn reassembles the character from several stores: the definition, the recent window, a summary, extracted facts, whatever else you carry. Each is a separate read. Any of them can be slow, empty, or return a stale record.
The failure mode is that assembly succeeds with fewer pieces. Concatenating four things and concatenating three things are the same operation, and nothing downstream can tell the difference. There is no signal in the output that says a piece is missing, because the missing piece’s absence looks exactly like that piece having nothing to say.
That is what makes partial state worse than no state. A reply built without the fact store is not obviously degraded — it is fluent, in voice, and confidently ignorant about one specific thing. Compare that with a turn that fails outright: visible, attributable, and over in one message.
The severity depends entirely on which piece went missing, and the pieces are not equally important. Lose extracted facts and the character is in voice and forgetful. Lose the summary and the middle of the conversation vanishes while the last few turns are intact — the strange case where recall of yesterday is better than recall of an hour ago. Lose the definition and the reply is a generic assistant with no character at all, which is the loudest failure available and also the easiest to catch, because it is the one users report immediately.
Required pieces and optional ones
The useful design move is to stop treating the stores as a uniform list and declare which are required.
The definition is required. A turn without it is not a degraded version of your product; it is a different product, and shipping it is worse than shipping nothing. Never assemble a turn without the definition — fail instead. Everything about the character that is authored lives there, and a persona is only ever what was deliberately re-supplied.
History is optional in the narrow sense that a turn can be produced without it. That does not make it free to drop, and it gives three postures, each with a real cost.
Fail the turn. Honest and visible. The user sees an error, retries, and usually gets a working turn because the missing read was transient. It costs a visibly broken message and it converts a quiet correctness problem into a loud availability problem, which is a trade many products would take and few make deliberately.
Degrade, and mark the gap in the input. Assemble what you have and include a plain statement that part of the record is unavailable for this turn, so the reply can decline to assert things it has no basis for instead of writing as though the record were complete. It keeps the conversation moving, and it costs a seam — the reply will be noticeably vaguer, and vagueness that arrives without explanation reads as the character losing interest. Some of that seam is unavoidable; keeping it small and local is the whole job.
Substitute a cached prior state. Serve the last known-good record. Stale but coherent, which is often better than absent, and it is the option with the nastiest edge: a cached record that predates a correction will re-assert a fact the user already fixed, and a wrong fact is more expensive than a missing one. Bound the staleness or do not do it.
There is no fourth option that produces a complete reply from incomplete state. Every route pays somewhere.
The turn
THE TURN — incomplete assembly
· Assemble from whichever stores answered
→ the turn always ships. Highest apparent
availability, no errors in any log.
· Missing pieces are indistinguishable from
empty pieces
→ a fluent, in-voice, confidently ignorant
reply. The failure is invisible to
everything except the user.
· Declare the definition required
→ removes the worst case outright. Costs a
visible error instead of a silent one.
· Mark the gap when history is short
→ the reply can avoid asserting what it has
no basis for. Costs a visible seam and
some vagueness.
· Cached prior state as a fallback
→ coherent and stale. Re-asserts
corrections that landed after the cache
was taken.
· Completeness checks and the gap marker
→ PAID EVERY TURN. Both are per-message
work and per-message input weight, on
every turn, not only the failing ones.
· None of it reconstructs the missing state
→ the turn is answered without it or not
answered. There is no third outcome.
Detection
Record a piece count and a byte count per store, per turn. This is the whole detection story in one line of instrumentation and almost nobody has it. A turn assembled from three stores where the norm is four is findable instantly, and without that count these incidents are unreproducible by construction — by the time anyone looks, the read succeeds.
Alarm on definition-absent immediately, at any rate above zero. It is the one case with no acceptable frequency, and it is also the easiest signal to get right.
Watch the register of degraded turns. Replies produced from short state are shorter, vaguer, and lower in trait density. If you tag degraded turns you can compare them as a population, which tells you how visible your seam actually is rather than how visible you assumed it was.
Correlate forgetting complaints against assembly logs by turn, not by conversation. The complaint names one message. The assembly record for that message either shows a missing store or it does not, and that single join separates this failure from drift — which is worth doing first, because drift needs entirely different work and the symptoms overlap enough to waste weeks.
What this costs and what it doesn’t fix
Completeness checking, the gap marker, and the per-turn instrumentation are all per-turn costs, paid on every successful turn to catch the rare bad one. That ratio is the honest framing: you are buying detection of a tail event with a permanent tax on the input and the assembly path.
And none of it prevents the underlying miss. State reads will occasionally not answer, and an assembly step that concatenates whatever it received cannot detect that on its own. What is available is choosing which failure the user gets, making it attributable afterwards, and refusing the one case where the character is replaced by something with the same name and no definition behind it.