Provenance: every memory traces to an origin
The reference implementation keeps an append-only ledger that maps each memory id to the observation that created it, and records any later consolidations. No silent edits, no fabricated sources.
This page walks the actual code in /code/agentmemory/ledger.py and /code/examples/provenance.py. All examples are fictional and clearly marked.
Origin observation → entry → derived summary
The flow is simple and explicit. When an agent observes a fact, it creates an Entry and records the origin in the ledger. Later, consolidation may produce a derived memory, but the original observation is never deleted.
1. Record the origin
from agentmemory import MemoryStore, Entry, ProvenanceLedger, Retrieval
store = MemoryStore()
ledger = ProvenanceLedger()
def add(text, source, tags):
e = store.add(Entry(text=text, source=source, tags=tags))
ledger.record_origin(e.id, source)
return e
primary = add("on-call number routes to the primary engineer", "runbooks/oncall.md", ["oncall"])
secondary = add("after-hours escalation uses the on-call rotation", "runbooks/escalation.md", ["oncall"])
The ledger stores memory_id, source, observed_at (a Unix timestamp), and optional extra metadata.
2. Retrieve and trace
r = Retrieval()
for hit in r.search(store, "who do I call off hours", k=2):
origin = ledger.origin(hit.entry.id)
print(f"memory {hit.entry.id}:")
print(f" text : {hit.entry.text}")
print(f" origin: {origin['source']}")
Every hit carries its entry.id; the ledger returns the original source. No hidden rewriting.
3. Derived summaries keep the chain
When consolidation creates a new memory, record_consolidation stores the derived_from ids, a reason, and a timestamp. The lineage method walks back to the earliest origins.
ledger.record_consolidation(
derived_id="mem-42",
from_ids=[primary.id, secondary.id],
reason="merged on-call notes"
)
lineage = ledger.lineage("mem-42")
# lineage["origin"] is None if mem-42 is derived;
# lineage["derived_from"] lists parent origins.
What the ledger does not guarantee
The ledger is append-only in the sense that it never deletes an original entry, but it does not claim immutability, tamper-proofing, or cryptographic security. It is a plain Python dictionary in memory. The source field is whatever the caller provides; there is no built-in trust or permission verification. The observed_at timestamp records when the observation was logged, not a freshness guarantee about the underlying fact.
Developer checklist
- Run python examples/provenance.py and confirm it prints the origin source for each hit.
- Inspect ledger.entries() to see all recorded origins.
- Call ledger.lineage(id) on a derived memory and verify the parent chain.
- Check that record_origin is called immediately after store.add; never reorder them.
- Remember: the ledger is in-memory only. For persistence, serialize it yourself.
Fictional example uses the reserved phone number +1-555-0100; no real PII is used. The ledger code is MIT licensed — see /code/LICENSE.