When the user contradicts the record

The fact store says the user works nights. Two months later they mention a normal daytime schedule. Nothing in the conversation announces a change, both statements were true when made, and the store now has to represent a person who said two incompatible things.

Whatever the store does next is a design decision you have already made, usually without noticing. There are three options and each of them breaks something different.

Overwrite, append, or supersede

Overwrite. The new value replaces the old one. The store stays small, unambiguous and cheap to include on every turn. The loss is the past: a relationship product where the record of what someone used to do is gone is a product that cannot refer back to anything, and referring back is most of the value. Overwriting also makes the store trust its most recent input unconditionally, so a throwaway remark outranks a fact established over months.

Append. Both values are kept, with whatever timestamps you have. Nothing is destroyed and the history is inspectable. The loss lands at assembly time: the input now contains two contradictory facts, and which one the model uses is not something you chose. Contradiction inside an input does not read as chronology unless you make it read that way, and a plain list of facts contains no signal about which is current.

Supersede. The old value is kept but marked as replaced, and only the current value is included in the input by default. This is the one that behaves best and it is the most work: you need a notion of which facts are single-valued and which accumulate, because superseding a job is right and superseding a mentioned sibling is data loss. That distinction cannot be inferred from the text of the fact; it has to be part of your schema.

The reason this feels harder than it should is that a fact store is being asked to hold two different things at once — the current state of a person, and the record of a relationship. Those have opposite update semantics. The current state wants replacement. The record wants immutability.

Contradiction is not the same as change

The store cannot distinguish four cases that look identical in text: the user’s circumstances changed, the user misspoke, the user was speaking hypothetically or in another voice, or the extractor misread one of the two statements. Only the first calls for an update.

You can improve the odds without solving it. Facts that arrive as a direct first-person statement are stronger evidence than facts inferred from context. A fact restated across separate sessions is stronger than one mentioned once. A fact volunteered in answer to a question about itself is stronger than one picked up in passing.

What you cannot do is ask the model to adjudicate reliably at the moment of extraction, because the extractor sees one statement and not the pattern. Confidence has to be a property of the store over time, not a judgement made once at write time — which is the same reason a wrongly captured fact is so durable.

The other option, and it is genuinely available, is to let the character ask. A brief in-character acknowledgement that something seems to have changed is often better product behaviour than silently picking a side, because it converts an invisible state decision into a visible, correctable one. The cost is that it is intrusive if it happens often, and it will happen often if your extraction is noisy.

The turn

THE TURN — a contradicted fact

  · Overwrite on conflict
                    → smallest store, no ambiguity in the
                      input, trivially cheap.

  · The past is gone
                    → destroys exactly the historical record a
                      relationship product exists to have, and
                      lets one remark outrank months.

  · Append and keep both
                    → nothing lost, and the input now contains
                      two facts that both look current.

  · Supersede with a schema
                    → behaves correctly, and requires deciding
                      per field whether values replace or
                      accumulate.

  · Every retained fact
                    → PAID EVERY TURN it is included, and a
                      store that only appends is a per-message
                      cost that never stops rising.

  · None of this detects a lie or a joke
                    → the store sees text, not intent. Some
                      contradictions are not changes.

Detection

Log every conflict as an event, whichever resolution you chose. The conflict rate per user per week is the number that tells you whether your extraction is capturing facts or capturing noise — a store that conflicts constantly is not observing a changeable person, it is misreading one.

Break the rate down by field. Conflicts concentrated in a few fields usually mean those fields are single-valued in your schema and multi-valued in reality. Conflicts spread evenly across everything mean the extractor is the problem, not the schema.

Keep the losing value even when you overwrite, in a log the input never sees. The cost is storage and nothing else, and it is the only way to answer the two questions that actually come up: what did the store believe when it produced that response, and how often does a value get replaced and then replaced back. A value that oscillates is a schema error you can see only in the log.

Watch the size of the fact store as a distribution rather than an average. Append-only stores develop a tail of users whose fact lists have grown large enough to dominate their inputs, and those users experience both the highest cost and the most contradiction — which is the same tail that dominates the bill.

What this costs and what it doesn’t fix

Retained facts are the cheapest kind of long-term memory per unit of reach and still a permanent per-turn charge, because a fact once stored is included on every subsequent turn unless something removes it. Superseding is the version that keeps that charge flat over time; appending is the version where a relationship gets more expensive the longer it lasts. Neither is free, and the schema work that makes superseding possible is paid once in design and then on every write.

What none of it fixes is that a fact store is a lossy representation of a person and the loss is not symmetrical. It keeps what the extractor decided to keep, resolves conflicts by rules that cannot see intent, and presents the result to the model as settled truth. The best available position is that resolution is explicit, reversible, logged, and occasionally handed back to the user rather than guessed at.