Skip to the document
Madhuopen lab
Why AI Agents ForgetPart II — How real systems do it

Chapter 5

How does memory stay true when life changes?

How a memory system handles facts that change: a move, a new job, a broken habit. We will also see the two clocks every fact needs, and why "git for facts" is the right mental model.

3 min read753 words3 recall cards

Before you read, guess

How does the system track a memory's validity over time?

Take ten seconds and guess — even a wrong guess makes the answer stick. Tap to see where the chapter lands, or just read on.

It is a commit with two clocks: when it was true, and when we learned it.

In this blog, we will learn how a memory system handles facts that change: a move, a new job, a broken habit. We will also see the two clocks every fact needs, and why "git for facts" is the right mental model.

The problem with overwriting

Maya lived in London. Now she lives in Paris. The simplest fix is to find the London row and rewrite it.

Design"Where do I live?""Where did I use to live?"
Update in placeParisgone
Keep both, with time stampsParis (the only open row)London (closed in March)

Overwriting answers the first question and destroys the second. LongMemEval has a whole category called "knowledge update", and BEAM has "contradiction resolution" and "event ordering". Overwriting loses all three.

Two clocks

Databases solved this decades ago with the word bitemporal. Every fact carries two clocks:

  • Valid time: when the fact was true in the world. Maya lived in London from 2025-01 to 2026-03.
  • System time: when the system learned it. We were told about London on 2025-01-14 and about Paris on 2026-03-02.
"Maya lives in London."
    valid_from  2025-01      valid_to  2026-03      <- valid time
    created_at  2025-01-14   superseded_by  #7      <- system time / lineage

"Maya lives in Paris."   (#7)
    valid_from  2026-03      valid_to  NULL
    created_at  2026-03-02

Ask the store "as of today" and you get Paris. Ask "as of February 2026" and you get London. Ask "what changed?" and you follow superseded_by.

Zep's Graphiti paper names the four timestamps on every edge: t_valid, t_invalid, t_created, t_expired. HydraDB describes the same thing as a Git-style, append-only temporal graph.

Git for facts

If you have used git, you already understand this. A commit never changes. A new commit points at the old one. HEAD is the current truth, and git log is the history.

   #3 "lives in London"  <---- #7 "lives in Paris"  <---- HEAD
      valid 2025-01..2026-03      valid 2026-03..

Superseding a fact is a new commit with a parent pointer. Nothing is force-pushed. Every memory mutation is traceable, which is also what an audit wants and what "the user can see and delete everything" needs.

Who decides that a fact is superseded?

The extractor. When it writes "Maya moved from London to Paris in March 2026", it also sees the 10 most similar existing memories, including "Maya lives in London", and it returns supersedes: [id of the London row]. The store then stamps valid_to on the old row and links the two.

This is one LLM call, the same call that extracts the fact. Nobody deletes anything, and a wrong guess is reversible: reopen the old row.

Provenance: where did this come from?

HydraDB's blog puts provenance next to bitemporality: every assertion should carry the source utterance, the model that extracted it, and its confidence. We keep it small: source_ids is the list of session and turn ids the fact came from. Two reasons:

  1. Debugging. When a memory is wrong, you want to see the sentence that produced it.
  2. Safety. Facts extracted from the user's own words are trusted. Facts that came from a web page or a tool output are not. The source field tells them apart, and that is the defense against memory poisoning.

Reading the chain at answer time

When the read path returns "Maya lives in Paris", it also returns the chain behind it, and the prompt says so:

- Maya lives in Paris. (since 2026-03; replaced "Maya lives in London", 2025-01 to 2026-03)

Now the answer model can handle "where do you live", "where did you live before", and "when did you move" from one line. This is the cheapest temporal reasoning you will ever buy.

What about episodic events?

Events do not get superseded; they accumulate. "Vet checkup on March 14" does not replace "vet checkup on January 3". Both are true, both are dated. So supersedes is mostly a semantic-memory tool, and event ordering questions are answered by sorting episodic memories on event_date.

Closing

A fact is not a cell in a spreadsheet. It is a commit with two clocks: when it was true, and when we learned it. Never overwrite a commit. Add a new one, point it at the old one, and let the read path show the current truth with its history one line long.

Before you go

In one sentence, what was this chapter about?

From memory, without scrolling up. Writing it is what makes it yours; the grade is only to show you what you had.

How sure?