Your Agent's Memory Can't Tell You When It's Wrong
I have 1,900 memories stored across 43 projects. My agent reads them every session and believes every one of them equally — the note I wrote this morning and the note I wrote eight months ago about a service that no longer exists.
I built memgraph so my coding agents would stop relearning the same things. It works. Nineteen hundred memories later, it works a little too well: it hands an agent a fact with exactly the same confidence whether that fact is current or nine months dead.
Here is what that looks like in practice. One of my memories said a product was live at a particular domain. In July I retired it — the service was merged into another one, the repo archived, the domain 301’d elsewhere. So I did what everyone does: I opened the memory and edited the past. I bolted an annotation onto the sentence: RETIRED 2026-07-29 → became product #11.
That works exactly once. At nineteen hundred memories it does not scale, and worse, it destroys the thing that made the memory worth keeping. After that edit I could no longer answer a question I actually care about: what did I believe in July, and when did I find out I was wrong?
Two commands
memgraph verify reads a memory, pulls out every assertion it can actually check — a filesystem path, a URL, a repository — and resolves each one.
$ memgraph verify --project system
stale 1788507903 — Memory 1788507903
path ~/ai/sick-memory (missing, but ~/ai exists locally)
checked 380 — 13 stale, 221 unknown, 9 fresh, 137 without claims
memgraph supersede replaces a belief without destroying it. The old memory keeps its content, gains a forward pointer, and quietly drops out of recall.
$ memgraph supersede 1788507903 \
--text "stampd retired; became comptoir product sdlt" \
--reason "merged into comptoir 2026-07-29"
Memory 1788507903 superseded by 1788507904
The old memory is still on disk, still readable with --include-superseded, and the change is recorded in an append-only ledger. Nothing was erased. The July belief and the September correction both exist, in order, with a reason attached.
The hard part was deciding what counts as evidence
Writing the checkers took an afternoon. Making them trustworthy took the rest of the day, and it is the only part of this worth writing about.
My first version had an obvious-seeming rule: a file is stale if it is missing but its parent directory exists. If ~/ai/foo/ is there and ~/ai/foo/bar.md is not, the memory is out of date. Clean. Defensible. I ran it against my real store.
75 stale memories out of 380. Nearly every one was a lie.
STALE /etc/systemd/system/traefik.service | missing, but /etc/systemd/system exists locally
STALE /usr/local/bin/hotify-cli | missing, but /usr/local/bin exists locally
STALE /var/log/fail2ban.log | missing, but /var/log exists locally
Read those again. Every one of those memories is about a different machine — a VPS, a Proxmox container, a Raspberry Pi on my desk. And /etc/systemd/system exists on every Linux box in the world, including this laptop. My rule was not detecting deleted files. It was detecting that Linux is Linux.
That is the whole problem with staleness checking in one screenshot. A checker that cries wolf 75 times gets ignored on the 76th, and the 76th is the one that mattered. So I inverted the design around a single rule:
Staleness requires positive evidence. Everything ambiguous is unknown, never stale.
- A missing file counts only if its parent exists under my own home directory.
/etc,/usr,/vardescribe servers, not this machine — their contents prove nothing. - A URL answering 401 or 403 is alive. An auth wall is proof of life. Only a definitive 404 or 410 is evidence something is gone.
- A timeout or an unreachable host is
unknown. A network blip must never demote a true memory. - If a memory mentions
ssh,docker exec,user@hostor an IP address, its path claims are discounted entirely. That memory is describing a filesystem this process cannot see.
Same store, same command, after those four rules: 13 stale out of 380. And the survivors are real — a directory I renamed months ago, three git worktrees I cleaned up, a script that no longer exists. Every one of them a memory my agent would still have been quoting at me.
Two smaller false-positive classes fell out of the same calibration run. Sentence punctuation was being eaten into paths, so the cache is at /var/cache/apt. reported a missing file called apt. — the period is legal in a filename, so the regex happily swallowed it. And template fragments like chromium-<version> were being cut at the placeholder and reported as a missing chromium-. Neither would have shown up in a unit test I wrote myself. Both were obvious the moment I pointed the thing at nineteen hundred real memories.
Report by default, offline by default
verify writes nothing unless you pass --mark, and touches the network only if you pass --net. Both defaults are deliberate.
This command runs over 1,900 memories in 43 scopes. If the network hiccups while it is marking files, the blast radius is my entire knowledge base flagged as wrong. A tool that can quietly corrupt its own store to be helpful is not helpful. So the destructive capability is opt-in, and the exit code carries the signal instead: 90 means stale memories were found, which makes it a one-line cron gate.
memgraph verify --json || test $? -eq 90
Never destroy the record
Every supersede and delete now appends to a ledger.jsonl carrying a snapshot of the memory — so a deleted memory is still readable after the memory is gone.
$ memgraph ledger --since 30d
2026-09-04T07:45:03Z supersede 1788507903 -> 1788507904 (javi)
reason: merged into comptoir
And both operations refuse to proceed if the ledger write fails. That felt pedantic when I wrote it and correct ten minutes later: an action whose record was lost must not appear to have happened. A delete that succeeds while its audit entry silently vanishes is worse than a delete that fails loudly.
This is the part I actually took from the enterprise knowledge-graph world — bitemporality and an append-only decision ledger, minus the Postgres, the Docker compose file, and the ontology workbench. Two timestamps and a JSONL file get you most of the value. The rest is a platform I did not want to run.
What the dogfooding found
Pointing a new tool at your own data always costs you something. Two things fell out:
A latent bug in the memory writer: reading a memory and writing it back was not idempotent. The formatter re-emitted a blank line the parser kept, so every rewrite grew the file by a newline at each end. Harmless when you edit a memory once a week. Unbounded the instant you put verify --mark on a cron across 1,900 files. There is a byte-stability test guarding it now.
And the release before this one shipped with no binary attached, which meant the one-line install in my own README had been returning 404 to anyone who tried it. I found that while cutting this release. It is fixed, and it is a reminder that the install path is a feature like any other — it needs a test, not a good intention.
Try it
memgraph is open source and installs as a single static binary with no runtime dependencies:
curl -L https://github.com/javimosch/memgraph/releases/latest/download/memgraph \
-o ~/.local/bin/memgraph && chmod +x ~/.local/bin/memgraph
memgraph verify --project myproject
memgraph guide
It speaks MCP, so verify and supersede are available to your agent directly — and the tool descriptions tell it to reach for supersede instead of edit-or-delete whenever a fact changed rather than was wrong from the start. That distinction is the whole point. Editing a memory says the past was mistaken. Superseding it says the world moved.
If you are curious how memgraph decides which memories to surface in the first place, I wrote about the skill graph earlier. The rest of what I build lives at intrane.fr.