4 min read
If your agent has ever ignored a decision you know you already made, it is usually not random.
It is your memory system.
We were mid-migration with one hard rule: after cutover, do not write to the old table. We agreed explicitly, and the next day the agent generated code that wrote to both old and new tables "just to be safe."
If you are building multi-step workflows, cutovers, runbooks, incident response, multi-day projects, this failure mode is the default unless you engineer around it.

Memory behavior in production is mostly a systems problem: write path, read path, and compaction policy.
I recently wrote about persistent memory for Claude Code:
Persistent Memory for Claude Code: Never Lose Context Setup Guide
We are now talking about multi-day horizon tasks, specialist agents, and a chief-of-staff agent that keeps the plan. But there is a hard constraint that does not care how smart your model is:
So memory becomes the difference between:
In practice, memory behaves like three subsystems:
What gets extracted and persisted, and exactly when.
How relevant state gets retrieved and injected into active context.
What gets summarized, pruned, or dropped under token pressure.
Whether you can inspect what was saved, retrieved, and ignored.
OpenClaw's default posture is usually painful for real systems:
A useful framing is to define three service-level objectives:
{
"compaction": {
"memoryFlush": {
"enabled": true,
"softThresholdTokens": 32000,
"prompt": "Write a durable session note to memory/YYYY-MM-DD.md. Capture: decisions, constraints, open questions, owners, and any state that would break the plan if forgotten. If nothing meaningful happened, write NO_FLUSH.",
"systemPrompt": "Be terse. Prefer bullet points. Do not rewrite the conversation."
}
}
}Why this works:
Trade-off: extra tokens and latency. If flush prompts are sloppy, retrieval becomes noisy.
{
"contextPruning": {
"mode": "cache-ttl",
"ttl": "4h",
"keepLastAssistants": 4
}
}TTL pruning helps interactive coherence and cost, but it is cache policy, not durable memory policy.
{
"memorySearch": {
"enabled": true,
"sources": ["memory", "sessions"],
"query": {
"hybrid": {
"enabled": true,
"vectorWeight": 0.6,
"textWeight": 0.4
}
}
}
}If your corpus contains identifiers (HTTP status codes, service names, ticket IDs), lexical weight usually needs to be higher than expected.
{
"memorySearch": {
"sources": ["memory", "sessions"]
},
"experimental": {
"sessionMemory": true
}
}Session indexing improves coverage but adds noise. That is why flush quality matters more once session indexing is enabled.
Trigger: No checkpoint write before compaction
Detection: Critical constraint appears in chat but absent in memory files
Mitigation: Enforce pre-compaction flush with typed durable notes
Trigger: Semantic-only retrieval misses exact identifiers
Detection: Agent ignores known IDs/policies despite existing notes
Mitigation: Hybrid retrieval + corpus-specific weighting
Trigger: Long sessions summarized without state guarantees
Detection: Policy drift after long runs
Mitigation: Checkpoint SLO + concise, typed memory entries
If you want memory to behave, define an explicit contract:
The Agent Foundry
The engineering library for shipping production-grade agentic systems:implementation-focused books, deployable reference repositories, and managed-service-first architectures for Next.js application backends, Supabase authentication and tenant isolation, durable background jobs, event-driven lifecycle workflows, subscription and usage-based billing, rate limiting, retries, and production observability.
€0 today, then €14.99/mo · cancel anytime · one-time purchase also available
Signs you need real memory infrastructure:
| Option | Retrieval quality(w:3) | Persistence guarantees(w:3) | Relationship reasoning(w:2) | Operational complexity(w:2) | Weighted score |
|---|---|---|---|---|---|
| QMD | 5 | 3 | 2 | 3 | 68% Best when retrieval quality is your main bottleneck. |
| Mem0 | 4 | 5 | 2 | 3 | 74% Best when missed writes and compaction loss are primary failures. |
| Cognee | 4 | 4 | 5 | 2 | 76% Best when relationship queries are first-class requirements. |
| Obsidian + curation | 3 | 4 | 3 | 4 | 70% Best when human governance and editable knowledge are priority. |
If you set memory.backend = "qmd", you replace built-in indexing with a local-first sidecar combining lexical + vectors + reranking.

npm install -g @tobilu/qmd
qmd collection add ~/notes --name notes
qmd collection add ~/Documents/meetings --name meetings
qmd context add qmd://notes "Personal notes and ideas"
qmd context add qmd://meetings "Meeting transcripts"
qmd embed
qmd query "quarterly planning process"Mem0 changes write path and compaction resilience by auto-capturing and auto-recalling memory outside context windows.

from openai import OpenAI
from mem0 import Memory
openai_client = OpenAI()
memory = Memory()
def chat_with_memories(message: str, user_id: str = "default_user") -> str:
relevant_memories = memory.search(query=message, user_id=user_id, limit=3)
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories["results"])
system_prompt = f"You are a helpful AI. Answer based on query and memories.\nUser Memories:\n{memories_str}"
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": message}]
response = openai_client.chat.completions.create(
model="gpt-4.1-nano-2025-04-14",
messages=messages,
)
assistant_response = response.choices[0].message.content
messages.append({"role": "assistant", "content": assistant_response})
memory.add(messages, user_id=user_id)
return assistant_responseTrade-offs to evaluate explicitly:
Cognee shifts representation toward graph-aware memory.

import asyncio
import cognee
async def main():
await cognee.add("Cognee turns documents into AI memory.")
await cognee.cognify()
await cognee.memify()
results = await cognee.search("What does Cognee do?")
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main())Use graph-backed memory when relationship queries matter: ownership, dependencies, hierarchy, and cross-agent responsibility.
Obsidian is useful because humans can curate what the agent considers true.

Two practical patterns:
That creates a governance loop:
Start in this order:
Then ask hard questions:
If you have already shipped an agent system, your scars likely fall into one of three buckets: missed persistence, missed retrieval, or compaction loss.
Which one was yours?