---
title: "Memory with a timeline: what was true then, what was known then, and corrections that keep their history"
slug: knowledge-layer-3-time-and-history
date: 2026-10-21
author: Semurg
section: how-to
tags: [knowledge-graph, time, history, corrections, python, how-to]
summary: A correction should not erase what came before it. Store a value, correct it, and ask one memory what is current, what was known before the correction, and what was true at an earlier time, from Python.
status: published
covers: [sdk:remember, sdk:recall]
constants: ["0.1.1", 8601]
---

Agents get things wrong and then learn better. A useful memory keeps both: the corrected value becomes current, and the earlier value
stays readable as history, so you can always ask what your agents believed, and when. Semurg keeps both clocks on every value, the model
researchers call bitemporal (valid time and transaction time): you can ask what is true now, what was true at an earlier time
(`valid_at`), and what was known at an earlier moment (`as_of`), alone or together.

> Diagram two-times: Each value carries two clocks: when it was true in the world (the source's date) and when the memory learned it (when it was stored). A read can ask about either, or both.

## Two clocks on every value

When you store something you can say when it was true, with `date` (the session's date, or the time of the event). The memory also
records when it learned it. Those are two different clocks, and the difference matters: an alert sent at 09:30 says what was believed at
09:30; a review call at 10:20 may correct it, and a report written a week later may add something that was true all along.

## An incident, corrected

An on-call agent records an outage from the first alert. Fifty minutes later, the review call corrects the severity and the cause.
The program below ran as written against a fresh node of release r11.121.20261021.3619d35, with semurg 0.1.1 from PyPI. Save it as `timeline.py` (with a node and `SEMURG_API_TOKEN` set, as in [the PyPI quickstart](/blog/pip-install-semurg)):

```python
import os, time
from datetime import datetime, timezone
from semurg import Client

with Client("http://127.0.0.1:4000", token=os.environ["SEMURG_API_TOKEN"]) as mem:
    mem.remember("ops", date="2026-10-01T09:30:00Z", source="alert-7731", facts={"entities": [
        {"name": "INC-2041", "type": "incident",
         "attributes": {"severity": "SEV2", "root_cause": "expired TLS certificate"}}]})
    time.sleep(2)
    before_correction = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    time.sleep(2)
    mem.remember("ops", date="2026-10-01T10:20:00Z", source="review-call", facts={"entities": [
        {"name": "INC-2041", "type": "incident",
         "attributes": {"severity": "SEV1", "root_cause": "connection-pool exhaustion"}}]})

    def severity(**when):
        answer = mem.recall("ops", subject="INC-2041", compact=True, **when)
        return next(i for i in answer["items"] if i["kind"] == "entity")["attributes"]["severity"]

    print("now:                                 ", severity())
    print("true at 10:00:                       ", severity(valid_at="2026-10-01T10:00:00Z"))
    print("true at 10:30, as known now:         ", severity(valid_at="2026-10-01T10:30:00Z"))
    print("true at 10:30, as known before the fix:", severity(valid_at="2026-10-01T10:30:00Z", as_of=before_correction))
```

It prints:

```text
now:                                  SEV1
true at 10:00:                        SEV2
true at 10:30, as known now:          SEV1
true at 10:30, as known before the fix: SEV2
```

Four answers from one memory, and the last two are the point:

- **Now**, the review call's value is current: SEV1. The newest value of an attribute wins.
- **True at 10:00**, before the review at 10:20, the alert's SEV2 was the value on record. This is the question a post-incident review
  asks: what did we have at 10:00?
- **True at 10:30, as known now**: SEV1, because the review call's value holds from 10:20.
- **True at 10:30, as known before the correction was stored**: SEV2. The same moment in the world, a different moment of knowledge, a
  different answer. This is how you audit what an agent knew when it acted, not only what is true today.

The corrected value was not overwritten. Each value is kept with its source and its date, and a read chooses by both clocks.

## Conflicting reports are kept, side by side

Not every disagreement is a correction. Support may report the first customer complaints at 09:05 while monitoring fired at 09:14. Store
them as two attributes, `first_customer_report_at` and `started_at`, each from its own source, and both stay current: the memory keeps
the evidence and lets the reader see both, cited to where each came from.

## Things to know

- `as_of` and `valid_at` take an ISO 8601 date or date and time; a bare date means the end of that day, in UTC. Both can be given at
  once: what was known at `as_of` about what was true at `valid_at`.
- A value sent without `date` counts from the moment it was stored.
- `recall` and `context` both take `as_of` and `valid_at`, on every interface: Python, plain HTTP, the WebSocket, and MCP.

Next in this series: [Graph RAG that says when it does not know](/blog/knowledge-layer-4-graph-rag). Earlier:
[the automated ontology](/blog/knowledge-layer-2-the-automated-ontology).
