DEV Community

hao li
hao li

Posted on Originally published at github.com

Everyone builds agent memory for store-and-search. I built the other half: write and delete.

The agent-memory space is crowded on the store-and-search side: embeddings, retrieval, ranking. But I kept hitting the neglected half — write and delete:

  • When should a memory fade? A fact from 6 months ago shouldn't outrank yesterday's correction.
  • When two memories disagree, who wins? Silent overwrite is how agents end up confidently wrong.
  • When you delete, can you undo it — and can you explain why it was deleted?

memgovern treats these as first-class operations. Zero dependencies, SQLite under the hood:

  • Decay: memories carry importance + TTL; queries rank by exponential-decay score, not recency alone.
  • Tombstones: deletes are tombstones, not erasures — reversible, with a reason attached, full audit trail.
  • Conflict arbitration: a contradictory write on the same key is quarantined as PENDING. You pick the winner (new / old / both / human decides) instead of silently clobbering.
from memgovern import MemoryStore, ConflictPolicy

store = MemoryStore("agent.db", conflict_policy=ConflictPolicy.MANUAL)
store.write("user.theme", "User prefers dark mode", importance=0.8)
store.write("user.theme", "User prefers light mode", importance=0.8)
# -> conflict flagged, new write quarantined as PENDING
store.resolve_conflict(conflict_id=1, winner="new")
store.delete("deploy.region", reason="migrated to eu-central-1")  # tombstone, not erasure
store.audit(key="deploy.region")  # who wrote what, who deleted what
Enter fullscreen mode Exit fullscreen mode
pip install memgovern
python demo.py   # three-act demo: forgetting → tombstones → arbitration
Enter fullscreen mode Exit fullscreen mode

The philosophy is deliberately conservative — quarantine first, arbitrate, keep receipts. Opposite of the auto-replace-everything approach.

Known gap, stated honestly: conflict detection is key-based. Semantic contradiction detection is the big next step.

v0.2: source trust scores

v0.1 made bad writes visible and reversible. v0.2 asks a question v0.1 never asked: who is writing?

"I tried to poison an AI agent's memory. It worked 216 out of 216 times." — Hacker News

Memory-implantation attacks succeed ~98% of the time in published tests because nothing in the write path scores the writer. memgovern still can't read minds — the defense stays structural, not semantic — but now every source carries a reliability ledger: writes, conflicts won/lost, tombstones, quarantines. Trust is a Bayesian-smoothed win rate, (wins + k·prior) / (decided + k) with prior 0.5, so a new source starts exactly neutral and only decided arbitrations move the needle — spamming writes can't farm trust. Idle scores decay toward the prior with a 30-day half-life.

Pass arbitration="trust" to write() and a same-key contradiction is auto-arbitrated when the trust gap between the two sources exceeds the threshold (default 0.25): higher trust wins, the loser is superseded (never silently deleted), both scores land in the audit log. Close scores fall back to quarantine as before.

Three poisoning tripwires run on every write — a hit never auto-accepts, the write is born quarantined:

  • a brand-new source contradicting a key held by a high-trust source (≥ 0.75)
  • one source writing more than 20 times in 60 seconds
  • known injection phrases (ignore previous instructions, system:, jailbreak, …)

Honest limits, stated plainly: a patient attacker can farm trust slowly, and the marker list catches exact phrases but misses paraphrases. This is a seatbelt, not a vault. 38 tests pass; old v0.1 databases migrate on open.

Repo: https://github.com/hahahahahahahahah6/memgovern

MIT licensed. If you've shipped agent memory in production — what broke for you: staleness, contradictions, or deletes you regretted?

Update (v0.3): the governed memory is now an MCP tool

The library was always the engine; v0.3 makes it callable. memgovern-mcp is a stdlib-only MCP server over stdio exposing six tools: memory_write, memory_read, memory_delete, memory_trust_report, memory_pending_conflicts, memory_release. Every write goes through the same MemoryStore.write() path — trust arbitration and the three poisoning tripwires apply exactly as in the library, nothing is reimplemented or bypassed. The server defaults to the manual conflict policy, so contradicting writes quarantine for review — which is the whole point of an agent-facing memory: the agent can write, but strangers' overwrites wait for a human.

One paste to wire it into Claude Code:

memgovern-mcp --print-config
Enter fullscreen mode Exit fullscreen mode

Honest limits: stdio only; the MCP client and your scripts must share the same DB file (~/.local/share/memgovern/memory.db by default, MEMGOVERN_DB overrides). It's a thin wrapper — all governance semantics live in the library.

61 tests pass, still zero dependencies.

Update (v0.4): write reservations for multi-session races

The last roadmap item. Two agent sessions share one memory store: A reads key K, B writes K, A writes K from stale state — last-writer-wins silently destroys B's work. memgovern's answer is optimistic concurrency:

token = store.reserve("plan", source="session-a")  # bound to current version
# ... meanwhile session-b writes "plan" ...
m = store.write("plan", "stale text", source="session-a", reservation=token)
m.status  # "conflict" — NOT applied, current value attached for retry
Enter fullscreen mode Exit fullscreen mode

Reservations expire (default 5 minutes), are consumed on a successful write (no replay), and failed CAS attempts don't pollute the trust ledger. Writes without a reservation behave exactly as before. Exposed over MCP too, as memory_reserve plus a reservation param on memory_write.

Honest limits: reservations are advisory within one DB file — a process writing SQLite directly bypasses them; expiry is wall-clock; this is not a distributed lock.

75 tests pass, still zero dependencies. The roadmap is now empty (the LLM-judged idea stays parked — it would break the zero-dependency contract).

Top comments (0)