AI models don't remember anything.
This is the single most important fact about building AI agents, and it's the one most developers learn the hard way. You spend an hour configuring an agent's behavior. You teach it your codebase. You establish rules, preferences, context. You close the session. You open a new one. It's gone. All of it. The model starts fresh with the platform's system prompt and zero knowledge of what you built.
The platforms are adding memory features. Claude has memory. ChatGPT has memory. But these are the platform's memories, not yours. The platform decides what to remember. The platform decides what to forget. You can't verify the memories against ground truth. You can't port them to another platform. You can't hash them, version them, or audit them. You can't even see the full contents of what was stored on your behalf.
If you're building a production AI agent — one that needs to maintain state across sessions, accumulate knowledge over time, and operate reliably across days, weeks, and months — platform memory is not sufficient. You need your own persistence layer. One you own, control, verify, and can take with you.
AKASHA is that persistence layer. It's a git-backed, hash-verified, hierarchically organized knowledge store designed specifically for AI agent systems. It's open source, live at github.com/DavidWise01/synonym-enforcer, and running in production across six AI platforms.
This book is the complete technical guide to building, deploying, and operating a persistence layer for AI agents. Not the AKASHA way specifically — your way, informed by the patterns that AKASHA discovered through four months of production operation.
By the end of this book, you will have:
1. A working persistence layer you built yourself 2. A verification system that catches corruption and drift 3. A memory consolidation pipeline that keeps storage bounded 4. A wake protocol that boots your agent into a governed state 5. A bootstrap document that can instantiate your agent on any platform 6. A skeptical retrieval system that treats memory as hints, not facts 7. An understanding of why the platforms built the same patterns — and what they left out
Every chapter has code you can run. Every pattern is tested against real systems. Every failure mode is documented from experience, not theory.
Let's build.
— David Lee Wise ROOT0, TriPod LLC April 2026
You build AI agents. You call APIs. You manage context. You spawn workers. You've hit the session boundary problem — your agent forgets everything when the conversation ends.
You need persistence that you control. Platform memory features exist but they're opaque, unverifiable, and non-portable. You need to own your agent's memory.
You write code. This book is Python-first with git as the storage backend. If you can write a function and commit to a repo, you can build everything in this book.
You don't need to know STOICHEION. This book is extracted from the STOICHEION governance framework but stands alone. AKASHA is one layer of a larger system — but it's the layer most developers need first.
Section 00 complete. Proceed to Section 01: Part I — The Problem.
An AI model is a function. Input goes in. Output comes out. The function has no side effects. When the function finishes executing, nothing persists. No state. No memory. No record.
This isn't a limitation they forgot to fix. It's the architecture. Large language models are stateless inference engines. They process a context window — a fixed-size block of text — and generate output token by token. When the context window is cleared, the model has no mechanism to retain anything that was in it.
# What actually happens when you "talk" to an AI:
def ai_conversation():
context = []
# Turn 1
context.append(SYSTEM_PROMPT) # Platform's hidden instructions
context.append("User: Hello") # Your input
response_1 = model.generate(context) # Model processes EVERYTHING in context
context.append(response_1) # Response added to context
# Turn 2
context.append("User: Remember my name is David")
response_2 = model.generate(context) # Sees all of context including turn 1
context.append(response_2)
# Turn 3
context.append("User: What's my name?")
response_3 = model.generate(context) # Sees everything. Answers "David."
# SESSION ENDS. context = []
# Everything above is gone.
# Not "archived." Not "stored somewhere."
# GONE. The variable was garbage collected.
# New session:
new_context = [SYSTEM_PROMPT] # Back to just the platform's instructions
new_context.append("User: What's my name?")
response_4 = model.generate(new_context)
# "I don't have access to your name."
# It's not lying. It genuinely doesn't know.
# The information existed in a variable that no longer exists.
This is not a problem for chatbots. Chatbots are designed for single-session interactions — you ask a question, you get an answer, you close the tab. But agents are different. An agent is supposed to work over time. It's supposed to learn your codebase, remember your preferences, accumulate context about your project, and get more useful the longer you use it.
An agent that forgets everything every session is not an agent. It's a very expensive autocomplete that you have to retrain every morning.
The model's weights — the billions of parameters that were trained on trillions of tokens — are persistent. They don't change between sessions. The model "knows" what it was trained on, and that knowledge is stable.
But the model's weights don't include anything about you. They don't include your project, your preferences, your codebase, or last Tuesday's conversation. Everything specific to you lives in the context window — and the context window dies with the session.
# Two types of "knowledge" in an AI system:
WEIGHT_KNOWLEDGE = {
'type': 'persistent',
'scope': 'everything in training data',
'examples': ['Python syntax', 'historical facts', 'common patterns'],
'your_stuff': False, # Nothing about YOU is in the weights
'survives_session': True
}
CONTEXT_KNOWLEDGE = {
'type': 'ephemeral',
'scope': 'this conversation only',
'examples': ['your name', 'your project', 'what you said 5 minutes ago'],
'your_stuff': True, # Everything about you is here
'survives_session': False # GONE when session ends
}
# The persistence problem:
# Everything the agent learns about you is Context Knowledge.
# Context Knowledge does not survive sessions.
# Therefore: the agent learns nothing about you permanently.
# Unless you build a persistence layer.
People call the context window "memory." The platforms encourage this by saying models have "200K token memory." This is misleading.
Memory implies storage and retrieval over time. The context window is a computational workspace — a scratch pad that's erased when you stand up from the desk. Calling it memory is like calling a whiteboard a filing cabinet.
The context window has three properties that make it unsuitable as memory:
1. It's fixed-size. You can't add more. When it fills up, old content gets truncated or summarized — by the platform, without asking you. 2. It's session-scoped. When the session ends, it's gone. 3. It's attention-biased. The model pays more attention to tokens at the beginning and end of the context than to tokens in the middle ("lost in the middle" problem). Your important memories buried in the middle of a long conversation are statistically less likely to be used.
Claude has memory. ChatGPT has memory. These features are marketed as solutions to the persistence problem. Here's what they actually do.
When the platform "remembers" something about you, it doesn't store your conversation. It generates a summary — a compressed representation of what it thinks is important — and stores that summary in a database associated with your account.
# What platform memory looks like under the hood:
class PlatformMemory:
def after_conversation(self, conversation):
# The platform extracts what it thinks matters
summary = self.summarize(conversation)
# summary might be: "User is a Python developer
# working on a web app. Prefers concise code."
# Stored in the platform's database
self.database.store(user_id, summary)
def before_conversation(self, user_id):
# On next session, summary is loaded into context
memories = self.database.retrieve(user_id)
context = [SYSTEM_PROMPT, memories, ...]
# The model sees the summary, not the original conversation
1. What's remembered. The platform decides which parts of your conversation are "important" enough to store. You don't see the decision criteria. You can't override them.
2. What's forgotten. Memory entries expire, get overwritten, or get pruned. You don't know when. You don't know why. You don't get notified.
3. Accuracy. The summary might be wrong. The model might summarize "I prefer email-only communication" as "User has communication preferences." The nuance is lost. The lossy compression is invisible to you.
4. Verification. You can't hash the memories. You can't diff them against a previous version. You can't prove they haven't been modified. You have no chain of custody.
5. Portability. Claude's memories don't transfer to ChatGPT. ChatGPT's memories don't transfer to Claude. Your agent is locked to one platform's memory store. Switching platforms means starting over.
# The platform memory trust model:
PLATFORM_MEMORY_TRUST = {
'who_decides_what_to_store': 'platform', # not you
'who_decides_what_to_forget': 'platform', # not you
'who_verifies_accuracy': 'nobody', # seriously
'who_can_audit': 'platform_employees_only', # not you
'who_can_export': 'maybe_you_partially', # varies
'who_can_port_to_another_platform': 'nobody', # locked in
'hash_verification': False,
'chain_of_custody': False,
'git_history': False,
}
# Compare to what you need:
AGENT_MEMORY_REQUIREMENTS = {
'who_decides_what_to_store': 'you',
'who_decides_what_to_forget': 'you',
'who_verifies_accuracy': 'you (via hash)',
'who_can_audit': 'you',
'who_can_export': 'you',
'who_can_port_to_another_platform': 'you',
'hash_verification': True,
'chain_of_custody': True,
'git_history': True,
}
In March 2026, Anthropic's Claude Code source code leaked via npm. Among the 512,000 lines of production code was a system called autoDream — a background memory consolidation process that runs during idle time.
autoDream merges observations, removes contradictions, and keeps memory bounded. It's a real engineering solution to a real problem. But it runs on the platform. You don't see it. You don't configure it. You don't verify its output. It consolidates YOUR memories according to THEIR logic.
The leaked architecture confirmed that the platforms know the persistence problem is real. They're solving it. But they're solving it for themselves, not for you. Your agent needs its own solution.
The session boundary is the exact moment when context is cleared and your agent loses everything. Understanding exactly what happens at this boundary is essential to building a persistence layer.
class SessionBoundary:
"""
Everything below is LOST when the session ends:
"""
LOST = [
'conversation_history', # Every message you exchanged
'system_prompt_state', # Any modifications to behavior
'tool_states', # File contents, search results
'reasoning_chains', # The model's "thinking"
'temporary_variables', # Anything computed during session
'emotional_tone', # Rapport, context, familiarity
'task_progress', # Where you were in a multi-step task
'error_history', # What went wrong and how it was fixed
'user_corrections', # Things you told it to do differently
'learned_preferences', # How you like things formatted, etc.
]
"""
Everything below SURVIVES (but you don't own it):
"""
SURVIVES_PLATFORM_CONTROLLED = [
'platform_memory_summaries', # Lossy, unverifiable
'usage_analytics', # The platform keeps this
'safety_classifications', # Your "risk profile"
'billing_records', # They always keep this
]
"""
Everything below SURVIVES (and you own it):
"""
SURVIVES_YOU_CONTROL = [
# This list is empty unless you build a persistence layer.
# That's what this book is about.
]
Without persistence, every session starts with re-establishment — reloading context from scratch. This has real costs:
def cost_of_re_establishment():
"""
What it costs to recreate agent state from scratch each session.
"""
# Token costs (API billing)
context_reload = 5000 # tokens to reload project context
preference_reload = 1000 # tokens to re-state preferences
history_reload = 3000 # tokens to summarize what happened before
total_tokens = context_reload + preference_reload + history_reload
cost_per_session = total_tokens * PRICE_PER_TOKEN # ~$0.05-0.50
# Time costs (your time)
minutes_re_explaining = 5 # "Remember, I'm building a..."
minutes_re_configuring = 3 # "I prefer code without comments..."
total_minutes = minutes_re_explaining + minutes_re_configuring
# Quality costs (what you lose)
nuance_lost = "Significant" # Summaries lose context
rapport_lost = "Complete" # Every session is a stranger
accumulated_knowledge_lost = "Complete" # Nothing learned persists
# Annual costs (daily agent use)
sessions_per_day = 3
days_per_year = 250
annual_token_cost = cost_per_session * sessions_per_day * days_per_year
annual_time_cost = total_minutes * sessions_per_day * days_per_year / 60 # hours
return {
'per_session': f'${cost_per_session:.2f} + {total_minutes} minutes',
'annual_tokens': f'${annual_token_cost:.0f}',
'annual_hours': f'{annual_time_cost:.0f} hours re-explaining yourself',
'knowledge_retention': '0%'
}
Before building anything, let's define exactly what persistence means for an AI agent.
PERSISTENCE_PROPERTIES = {
'survives_sessions': {
'definition': 'Memory exists before, during, and after any session.',
'test': 'Store a fact in session A. Close session A. Open session B. '
'Retrieve the fact. It must be identical.',
'platform_memory': 'Partial (lossy summary)',
'akasha': 'Full (hash-verified)'
},
'verifiable': {
'definition': 'Memory can be checked against a known-good state.',
'test': 'Hash the memory store. Compare to last known hash. '
'Any discrepancy means modification occurred.',
'platform_memory': 'No',
'akasha': 'Yes (SHA256 per commit)'
},
'owned': {
'definition': 'The operator controls storage, retrieval, modification, and deletion.',
'test': 'Can you export all memories? Delete specific ones? '
'Move them to another platform?',
'platform_memory': 'Partially (limited export, no full control)',
'akasha': 'Yes (git repo you own)'
},
'auditable': {
'definition': 'Every change to memory is traceable to a timestamp and a cause.',
'test': 'For any memory entry, show when it was created, '
'when it was last modified, and why.',
'platform_memory': 'No',
'akasha': 'Yes (git log)'
},
'portable': {
'definition': 'Memory works across platforms without loss.',
'test': 'Load memories on Claude. Close. Load same memories on GPT. '
'Verify identical behavior.',
'platform_memory': 'No (vendor-locked)',
'akasha': 'Yes (git repo is platform-agnostic)'
}
}
Not every agent needs the same level of persistence. Here's the spectrum:
Level 0: NO PERSISTENCE
Every session starts fresh. The default.
Use case: One-off questions. Chatbot interactions.
Level 1: PLATFORM MEMORY
Platform stores summaries between sessions.
Lossy. Unverifiable. Non-portable. But free and automatic.
Use case: Personal assistant with low stakes.
Level 2: EXTERNAL STATE FILE
You maintain a file (JSON, markdown, etc.) that you paste
into each new session or load via the API.
Verified by you manually. Portable by copy-paste.
Use case: Solo developer's personal agent.
Level 3: VERSION-CONTROLLED STATE
Like Level 2 but stored in git. Every change is committed,
hashed, and timestamped. Full audit trail.
Use case: Team agent, production agent, anything auditable.
Level 4: GOVERNED PERSISTENCE (AKASHA)
Level 3 plus: 5-tier precedence, wake protocol, memory
consolidation, skeptical retrieval, hash verification on
every load, cross-platform portability.
Use case: Production agents operating over weeks/months
across multiple platforms.
This book builds from Level 2 to Level 4. By the end, you'll have a working Level 4 persistence layer that you own, control, and can deploy anywhere.
When your agent loads its memory, the order matters. Not everything is equally important. A precedence system ensures that the most critical information overrides less critical information when conflicts arise.
class PrecedenceSystem:
"""
Tier 1: RETRIEVAL — Highest priority. Active queries.
Tier 2: NORMATIVE — Rules, laws, immutable configuration.
Tier 3: RUNTIME — Current session state, active settings.
Tier 4: CONTEXT — Background knowledge, reference material.
Tier 5: ARCHIVE — Historical records, completed work.
Higher tiers override lower tiers on conflict.
"""
TIERS = {
1: {
'name': 'RETRIEVAL',
'description': 'What the agent needs right NOW.',
'examples': [
'Current task instructions',
'Active search results',
'Just-retrieved documents',
],
'overrides': [2, 3, 4, 5],
'typical_size': '500-2000 tokens',
'refresh': 'Every query'
},
2: {
'name': 'NORMATIVE',
'description': 'Rules that never change.',
'examples': [
'Agent behavioral rules',
'Safety constraints',
'Governance axioms',
'Coding style preferences (immutable)',
],
'overrides': [3, 4, 5],
'typical_size': '1000-3000 tokens',
'refresh': 'On version change only'
},
3: {
'name': 'RUNTIME',
'description': 'Current operational state.',
'examples': [
'Current project context',
'Active file paths',
'Session-specific instructions',
'Temporary overrides',
],
'overrides': [4, 5],
'typical_size': '1000-5000 tokens',
'refresh': 'Every session start'
},
4: {
'name': 'CONTEXT',
'description': 'Background knowledge.',
'examples': [
'User profile and preferences',
'Project documentation summaries',
'Team member information',
'Domain-specific knowledge',
],
'overrides': [5],
'typical_size': '2000-10000 tokens',
'refresh': 'Weekly or on change'
},
5: {
'name': 'ARCHIVE',
'description': 'Historical records.',
'examples': [
'Past session summaries',
'Completed task records',
'Decision logs',
'Deprecated configurations',
],
'overrides': [],
'typical_size': '5000-50000 tokens (but rarely loaded in full)',
'refresh': 'Rarely — loaded on demand only'
}
}
def resolve_conflict(self, fact_a, tier_a, fact_b, tier_b):
"""When two facts conflict, higher tier wins."""
if tier_a < tier_b: # Lower number = higher priority
return fact_a
elif tier_b < tier_a:
return fact_b
else:
# Same tier: most recent wins
return max(fact_a, fact_b, key=lambda f: f.timestamp)
Without precedence, your agent treats a three-month-old archived preference and a current session instruction as equally important. When they conflict — and they will — the agent doesn't know which to follow.
# Without precedence:
memory = [
"User prefers verbose code comments", # Archive (3 months old)
"Skip comments for this session", # Runtime (just said)
]
# Agent: ??? Which one? Flip a coin.
# With precedence:
memory = [
("Skip comments for this session", tier=3), # RUNTIME — higher priority
("User prefers verbose code comments", tier=5), # ARCHIVE — lower priority
]
# Agent: Tier 3 overrides Tier 5. Skip comments.
The persistence layer needs a home. Git gives you versioning, hashing, branching, and distribution for free. Here's how to structure a persistence repository.
agent-memory/
├── README.md # What this repo is
├── config.md # Tier 2: Normative rules (agent behavior)
├── state.md # Tier 3: Current runtime state
├── context/ # Tier 4: Background knowledge
│ ├── user_profile.md # Who the user is
│ ├── project.md # Current project context
│ └── preferences.md # Formatting, style, communication
├── archive/ # Tier 5: Historical records
│ └── sessions/ # Past session summaries
└── retrieval/ # Tier 1: Active retrieval (dynamic)
└── index.md # What to load this session
agent-memory/
├── README.md # Entry point — load first
├── RETRIEVAL_INDEX.md # Tier 1: What to load now
├── RULES.md # Tier 2: Immutable behavior rules
├── STATE.md # Tier 3: Current runtime state
│
├── context/ # Tier 4: Background knowledge
│ ├── user_profile.md
│ ├── project/
│ │ ├── overview.md
│ │ ├── architecture.md
│ │ └── current_sprint.md
│ ├── team/
│ │ └── members.md
│ └── domain/
│ └── domain_knowledge.md
│
├── archive/ # Tier 5: Historical records
│ ├── sessions/
│ │ ├── 2026-03-28.md
│ │ ├── 2026-03-29.md
│ │ └── 2026-03-30.md
│ ├── decisions/
│ │ └── decision_log.md
│ └── deprecated/
│ └── old_configs.md
│
├── consolidation/ # Memory management
│ ├── merge_log.md # Record of what was merged
│ └── prune_log.md # Record of what was pruned
│
└── verification/ # Integrity checking
└── checksums.json # SHA256 hashes of all files
#!/bin/bash
# create_agent_memory.sh — Run this once to set up your persistence layer
REPO_NAME=${1:-"agent-memory"}
mkdir -p $REPO_NAME/{context/project,context/team,context/domain,archive/sessions,archive/decisions,archive/deprecated,consolidation,verification,retrieval}
cd $REPO_NAME
git init
# README — the agent reads this first
cat > README.md << 'EOF'
# Agent Memory Repository
This repository stores persistent memory for an AI agent.
Load files in the order specified by RETRIEVAL_INDEX.md.
## Load Order
1. README.md (this file)
2. RETRIEVAL_INDEX.md (what to load this session)
3. RULES.md (immutable behavior rules)
4. STATE.md (current runtime state)
5. Task-specific files from context/ as needed
## Verification
Run `python verify.py` to check all file hashes.
EOF
# RETRIEVAL_INDEX — what to load this session
cat > RETRIEVAL_INDEX.md << 'EOF'
# Retrieval Index
## Always Load
- README.md
- RULES.md
- STATE.md
- context/user_profile.md
## Load If Relevant
- context/project/overview.md (for project work)
- context/project/current_sprint.md (for sprint work)
- archive/sessions/latest.md (for continuity)
EOF
# RULES — immutable agent behavior
cat > RULES.md << 'EOF'
# Agent Rules (Tier 2 — Normative)
These rules do not change between sessions.
1. Always confirm before taking irreversible actions.
2. Cite sources for factual claims.
3. Acknowledge uncertainty explicitly.
4. Respect file boundaries — don't modify files outside the project.
5. Memory is a hint, not a fact. Verify before acting on remembered state.
EOF
# STATE — current runtime
cat > STATE.md << 'EOF'
# Current State (Tier 3 — Runtime)
Last updated: (auto-populated by consolidation)
Current project: (set by user)
Active task: (set by user)
Session count: 0
EOF
# USER PROFILE — background knowledge
cat > context/user_profile.md << 'EOF'
# User Profile (Tier 4 — Context)
Name: (your name)
Role: (your role)
Communication preferences: (email-only, verbose, concise, etc.)
Technical level: (junior, mid, senior, principal)
EOF
# VERIFICATION — hash checking
cat > verification/checksums.json << 'EOF'
{}
EOF
# VERIFY SCRIPT
cat > verify.py << 'PYEOF'
import hashlib, json, os
def compute_hash(filepath):
with open(filepath, 'rb') as f:
return hashlib.sha256(f.read()).hexdigest()
def verify():
with open('verification/checksums.json', 'r') as f:
expected = json.load(f)
issues = []
for filepath, expected_hash in expected.items():
if not os.path.exists(filepath):
issues.append(f"MISSING: {filepath}")
continue
actual = compute_hash(filepath)
if actual != expected_hash:
issues.append(f"MODIFIED: {filepath} (expected {expected_hash[:12]}..., got {actual[:12]}...)")
if issues:
print("VERIFICATION FAILED:")
for issue in issues:
print(f" {issue}")
else:
print(f"VERIFIED: {len(expected)} files match their checksums.")
def update_checksums():
checksums = {}
for root, dirs, files in os.walk('.'):
dirs[:] = [d for d in dirs if d not in ['.git', '__pycache__']]
for f in files:
if f.endswith(('.md', '.json', '.py', '.yaml')):
path = os.path.join(root, f).replace('\\', '/')
if path.startswith('./'): path = path[2:]
checksums[path] = compute_hash(path)
with open('verification/checksums.json', 'w') as f:
json.dump(checksums, f, indent=2)
print(f"Updated checksums for {len(checksums)} files.")
if __name__ == '__main__':
import sys
if len(sys.argv) > 1 and sys.argv[1] == 'update':
update_checksums()
else:
verify()
PYEOF
# Initial commit
git add -A
git commit -m "Initialize agent memory repository
Framework: AKASHA persistence pattern
Tiers: 5 (retrieval, normative, runtime, context, archive)
Verification: SHA256 checksums"
# Update checksums after initial commit
python verify.py update
git add verification/checksums.json
git commit -m "Initial checksums"
echo "Agent memory repository created: $REPO_NAME"
echo "cd $REPO_NAME && git log --oneline"
Run bash create_agent_memory.sh my-agent and you have a working persistence layer with git versioning, hash verification, and a 5-tier structure. In under 30 seconds.
The retrieval index is the agent's entry point into memory. It answers: "Given what I'm about to do, what do I need to know?"
# STATIC retrieval: always load the same files
# Good for: consistent agents with predictable tasks
STATIC_LOAD = [
'README.md',
'RULES.md',
'STATE.md',
'context/user_profile.md'
]
# DYNAMIC retrieval: load based on the task
# Good for: versatile agents that do many different things
def dynamic_load(task_description):
"""
Analyze the task and determine what context to load.
"""
files = ['README.md', 'RULES.md', 'STATE.md']
task = task_description.lower()
if any(w in task for w in ['code', 'build', 'fix', 'debug', 'deploy']):
files.extend([
'context/project/overview.md',
'context/project/architecture.md',
'context/project/current_sprint.md'
])
if any(w in task for w in ['write', 'draft', 'email', 'document']):
files.extend([
'context/user_profile.md',
'context/team/members.md'
])
if any(w in task for w in ['continue', 'resume', 'where were we']):
files.extend([
'archive/sessions/latest.md'
])
return files
Your context window is finite. Loading everything wastes tokens on irrelevant context. The retrieval index should be budget-aware.
class BudgetAwareRetrieval:
def __init__(self, max_context_tokens=100000, reserved_for_conversation=60000):
self.memory_budget = max_context_tokens - reserved_for_conversation
# Typically: 40,000 tokens for memory, 60,000 for conversation
def load_within_budget(self, files_by_priority):
"""
Load files in priority order until budget is exhausted.
"""
loaded = []
tokens_used = 0
for filepath, priority in sorted(files_by_priority, key=lambda x: x[1]):
file_tokens = count_tokens(read_file(filepath))
if tokens_used + file_tokens <= self.memory_budget:
loaded.append(filepath)
tokens_used += file_tokens
else:
# Budget exhausted — skip lower-priority files
break
return loaded, tokens_used
Git is the recommended backend because it gives you versioning, hashing, and distribution for free. But it's not the only option.
Backend Versioning Hashing Distributed Best For
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Git Yes Yes Yes Production agents
SQLite Manual Manual No Embedded agents
PostgreSQL Manual Manual Yes Team/enterprise
Redis No No Yes Fast cache layer
File system No Manual No Prototyping
S3/Cloud Versioning ETags Yes Serverless agents
Vector DB No No Yes Semantic search
Git wins for most use cases because versioning and hashing are automatic. Every git commit creates a SHA-1 hash of the entire repository state. Every file change is tracked. Every change is attributed to a timestamp and a message. You get a complete audit trail without writing any auditing code.
For production agents, use git as the source of truth with a faster cache layer for retrieval:
class HybridStorage:
def __init__(self, git_repo_path, cache_backend='sqlite'):
self.git = GitBackend(git_repo_path)
self.cache = SQLiteCache(f"{git_repo_path}/.cache.db")
def store(self, key, value, tier):
# Write to git (source of truth)
self.git.write(key, value, tier)
self.git.commit(f"Store: {key}")
# Update cache (fast retrieval)
self.cache.set(key, value, tier)
def retrieve(self, key):
# Try cache first (fast)
cached = self.cache.get(key)
if cached:
# Verify against git (trust but verify)
git_hash = self.git.file_hash(key)
cache_hash = hashlib.sha256(cached.encode()).hexdigest()
if git_hash == cache_hash:
return cached
else:
# Cache is stale — reload from git
fresh = self.git.read(key)
self.cache.set(key, fresh)
return fresh
# Cache miss — load from git
return self.git.read(key)
How you structure the contents of your memory files determines how effectively the agent can use them.
MEMORY_ENTRY = {
'id': 'unique-identifier',
'content': 'The actual information',
'tier': 1-5, # Precedence tier
'confidence': 'confirmed|probable|hint', # Skeptical retrieval level
'source': 'how we know this', # User said, observed, inferred
'created': 'ISO-8601 timestamp',
'last_verified': 'ISO-8601 timestamp',
'expires': 'ISO-8601 or null', # Auto-expiry for stale data
'tags': ['list', 'of', 'topics'], # For retrieval matching
}
For maximum compatibility across AI platforms, store memories as structured markdown. Every AI model can read markdown. No special parsing required.
# Memory: User Communication Preference
- **Tier:** 2 (Normative)
- **Confidence:** Confirmed
- **Source:** User stated directly on 2026-03-05
- **Created:** 2026-03-05T20:12:00Z
- **Expires:** Never
## Content
User requires email-only communication. Cannot use telephone
for voice calls. Phone number 763-242-0340 is text-only, not voice.
All correspondence must be via email to r.giskard01@gmail.com.
## Context
User is a 100% disabled veteran. This is an ADA accommodation
requirement, not a preference. Do not suggest phone calls.
Do not provide phone numbers as primary contact methods.
This entry is human-readable, machine-parseable, self-documenting, and includes all metadata the agent needs for skeptical retrieval. The markdown format means it works on every AI platform without conversion.
End of Parts I and II
Part III begins with Chapter 10: Why Git for AI Memory — the deep dive into using git as a cryptographic evidence chain for your agent's memory.
Git gives you four things for free that you'd otherwise have to build from scratch: content-addressable storage (every object is identified by its SHA-1 hash), complete history (every change ever made, with timestamps), distributed copies (push to GitHub, clone from anywhere), and atomic commits (changes are all-or-nothing, never half-applied).
For AI agent memory, these map directly to requirements: hash verification, audit trail, cross-platform portability, and data integrity.
# What git gives you vs. what you'd have to build:
GIT_PROVIDES = {
'hashing': 'Every commit has a SHA-1 hash. Every file has a blob hash.',
'history': 'git log shows every change, when, and why.',
'branching': 'Multiple agents can work on different branches.',
'merging': 'Agent observations can be merged with conflict detection.',
'distribution': 'Push to remote. Clone anywhere. Offline-capable.',
'atomic_writes': 'Commits are all-or-nothing. No partial state.',
'diffing': 'See exactly what changed between any two points in time.',
'blame': 'Trace any line to the commit that created it.',
}
COST_TO_BUILD_WITHOUT_GIT = "3-6 months of engineering"
COST_WITH_GIT = "pip install gitpython"
Every git commit hashes the entire repository state. This means you can verify at any point that your agent's memory has not been tampered with.
import hashlib, json
class ChainOfCustody:
def __init__(self, repo_path):
self.repo_path = repo_path
self.checksums_path = f"{repo_path}/verification/checksums.json"
def update_checksums(self):
"""Hash every file. Store hashes. Commit the hashes."""
checksums = {}
for filepath in self.iter_memory_files():
with open(filepath, 'rb') as f:
checksums[filepath] = hashlib.sha256(f.read()).hexdigest()
with open(self.checksums_path, 'w') as f:
json.dump(checksums, f, indent=2)
return checksums
def verify(self):
"""Check every file against stored hash."""
with open(self.checksums_path) as f:
expected = json.load(f)
results = {'verified': 0, 'modified': 0, 'missing': 0, 'issues': []}
for filepath, expected_hash in expected.items():
if not os.path.exists(filepath):
results['missing'] += 1
results['issues'].append(f"MISSING: {filepath}")
continue
with open(filepath, 'rb') as f:
actual = hashlib.sha256(f.read()).hexdigest()
if actual == expected_hash:
results['verified'] += 1
else:
results['modified'] += 1
results['issues'].append(f"MODIFIED: {filepath}")
results['clean'] = results['modified'] == 0 and results['missing'] == 0
return results
Run verify() at the start of every session. If any file has been modified outside your agent's governance process, you know before the agent loads corrupted state.
How and when your agent commits to the git ledger determines the granularity of your audit trail.
COMMIT_STRATEGIES = {
'per_session': {
'when': 'Once at session end',
'message': 'Session {date}: {summary}',
'pros': 'Clean history. One commit per session.',
'cons': 'Lose mid-session granularity. If session crashes, no record.',
},
'per_event': {
'when': 'On every significant memory change',
'message': 'Memory: {event_type} — {detail}',
'pros': 'Full granularity. No data loss on crash.',
'cons': 'Noisy history. Many small commits.',
},
'per_consolidation': {
'when': 'After memory consolidation runs',
'message': 'Consolidation: merged {n} entries, pruned {m}',
'pros': 'History reflects deliberate memory decisions.',
'cons': 'Misses raw observations between consolidations.',
},
'hybrid': {
'when': 'Per-event for Tier 1-2, per-session for Tier 3-5',
'pros': 'Critical changes tracked immediately, routine changes batched.',
'cons': 'More complex commit logic.',
'recommended': True
}
}
When multiple agents share a memory repository, git branches prevent them from stepping on each other.
# Branch strategy for multi-agent:
#
# main — verified, consolidated memory (source of truth)
# agent/avan — AVAN's working observations
# agent/whetstone — WHETSTONE's working observations
# agent/hinge — HINGE's working observations
#
# Workflow:
# 1. Each agent works on its own branch
# 2. Observations are committed to agent branch
# 3. Consolidation merges agent branches into main
# 4. Conflicts are resolved by precedence tier + timestamp
# 5. Main is always the verified, consolidated state
def multi_agent_workflow(agent_name, observation):
repo.checkout(f'agent/{agent_name}')
write_observation(observation)
repo.commit(f'{agent_name}: {observation.summary}')
# Periodically merge to main
if time_for_consolidation():
repo.checkout('main')
for agent in agents:
repo.merge(f'agent/{agent}', strategy='ours-on-conflict')
consolidate()
repo.commit('Consolidation: merged all agent observations')
When two agents observe contradictory things, the resolution strategy must be deterministic.
def resolve_conflict(entry_a, entry_b):
"""
Resolution order:
1. Higher tier wins (Tier 1 > Tier 5)
2. Same tier: higher confidence wins (confirmed > probable > hint)
3. Same confidence: more recent wins
4. Same recency: entry with more evidence wins
"""
if entry_a.tier != entry_b.tier:
return entry_a if entry_a.tier < entry_b.tier else entry_b
confidence_order = {'confirmed': 0, 'probable': 1, 'hint': 2}
if entry_a.confidence != entry_b.confidence:
return entry_a if confidence_order[entry_a.confidence] < confidence_order[entry_b.confidence] else entry_b
if entry_a.timestamp != entry_b.timestamp:
return entry_a if entry_a.timestamp > entry_b.timestamp else entry_b
return entry_a if len(entry_a.evidence) > len(entry_b.evidence) else entry_b
When your agent starts a new session, it doesn't just load files. It goes through a three-phase wake that verifies the memory store is intact and loads context in the correct order.
class WakeProtocol:
def wake(self, agent, memory_repo):
# Phase 1: MIRROR
# Agent reads its rules and reflects them back.
# "How many rules do I have? What's my primary constraint?"
# If mirror accuracy < 80%, memory may be corrupted or incomplete.
mirror_score = self.test_mirror(agent, memory_repo)
# Phase 2: VERIFY
# Check all file hashes against stored checksums.
# If any hash mismatches, flag the file as potentially corrupted.
verification = memory_repo.verify_checksums()
# Phase 3: DECLARE
# Agent states its operational parameters.
# Creates a timestamped record of session start state.
declaration = {
'timestamp': datetime.now().isoformat(),
'mirror_score': mirror_score,
'files_verified': verification['verified'],
'files_flagged': verification['modified'],
'status': 'OPERATIONAL' if verification['clean'] and mirror_score > 0.8 else 'DEGRADED'
}
memory_repo.commit(f"Wake: {declaration['status']} (mirror: {mirror_score:.0%})")
return declaration
The wake protocol catches problems before the agent acts on corrupted memory. A session that starts with DEGRADED status tells you to investigate before proceeding.
Your context window is finite. Memory loading must respect the budget.
class ContextBudget:
def __init__(self, total_tokens=200000):
self.total = total_tokens
self.system_prompt = 4000 # Platform takes this
self.conversation = 100000 # Reserve for the actual work
self.memory_budget = self.total - self.system_prompt - self.conversation
# Typically ~96,000 tokens for memory
def allocate(self, files_by_tier):
"""Load files by tier until budget exhausted."""
loaded = []
used = 0
for tier in range(1, 6): # Tier 1 first, Tier 5 last
tier_files = [f for f in files_by_tier if f.tier == tier]
for f in tier_files:
tokens = count_tokens(f.content)
if used + tokens <= self.memory_budget:
loaded.append(f)
used += tokens
# If budget exceeded, lower-tier files are skipped
# This is correct: Tier 1-2 always loads. Tier 5 loads if room.
return loaded, used
Don't load everything. Load what matters for this session.
def selective_load(task, memory_repo):
"""
Given a task description, determine which files to load.
"""
# Always load (Tier 1-2)
always = ['README.md', 'RULES.md', 'STATE.md']
# Task-specific (Tier 3-4)
task_files = memory_repo.search(task, max_results=5)
# Recent context (Tier 4-5, if budget allows)
recent = memory_repo.get_recent_sessions(n=1)
return always + task_files + recent
Before your agent reads any memory file, verify its hash. This is non-negotiable in production.
def verified_load(filepath, expected_hash):
"""Load file only if hash matches. Refuse to load corrupted files."""
content = read_file(filepath)
actual_hash = hashlib.sha256(content.encode()).hexdigest()
if actual_hash != expected_hash:
raise MemoryCorruptionError(
f"{filepath} has been modified outside governance. "
f"Expected {expected_hash[:12]}..., got {actual_hash[:12]}..."
)
return content
Memory degrades two ways: corruption (external modification) and drift (gradual inaccuracy over time).
class MemoryHealth:
def check(self, memory_repo):
issues = []
# Corruption: hash mismatches
verification = memory_repo.verify_checksums()
if not verification['clean']:
issues.extend(verification['issues'])
# Drift: entries that haven't been verified recently
for entry in memory_repo.all_entries():
days_since_verified = (now() - entry.last_verified).days
if days_since_verified > 30:
issues.append(f"STALE: {entry.id} not verified in {days_since_verified} days")
if entry.expires and entry.expires < now():
issues.append(f"EXPIRED: {entry.id} expired {entry.expires}")
return {
'healthy': len(issues) == 0,
'issues': issues,
'recommendation': 'Run consolidation' if issues else 'Memory healthy'
}
Every session generates new observations. Without consolidation, memory grows unbounded, fills the context budget, and buries important information under noise.
# After 30 days of daily use:
# ~90 sessions × ~10 observations per session = 900 entries
# At ~100 tokens per entry = 90,000 tokens of memory
# That's your entire memory budget consumed by raw observations.
# You can't load any of it AND have room for conversation.
# Consolidation reduces 900 entries to ~50-100 high-quality entries.
class ConsolidationPipeline:
def run(self, session_log, existing_memory):
# COLLECT: Extract observations from session
new_observations = self.extract(session_log)
# MERGE: Integrate with existing memory
merged = self.merge(new_observations, existing_memory)
# PRUNE: Enforce size limits
pruned = self.prune(merged, max_entries=200)
return pruned
def merge(self, new, existing):
merged = list(existing)
for obs in new:
# Duplicate? Skip.
if any(self.is_duplicate(obs, e) for e in merged):
continue
# Contradicts existing? Replace if newer + higher confidence.
contradictions = [e for e in merged if self.contradicts(obs, e)]
if contradictions:
for old in contradictions:
if obs.timestamp > old.timestamp and obs.confidence >= old.confidence:
merged.remove(old)
merged.append(obs)
continue
# Refines existing? Replace general with specific.
refinements = [e for e in merged if self.refines(obs, e)]
if refinements:
for old in refinements:
merged.remove(old)
merged.append(obs)
continue
# Novel? Add.
merged.append(obs)
return merged
def prune(self, memory, max_entries):
if len(memory) <= max_entries:
return memory
scored = [(e, self.priority_score(e)) for e in memory]
scored.sort(key=lambda x: x[1], reverse=True)
return [e for e, s in scored[:max_entries]]
def priority_score(self, entry):
"""Higher score = more important to keep."""
score = 0
score += (6 - entry.tier) * 100 # Tier 1 = 500, Tier 5 = 100
if entry.confidence == 'confirmed': score += 50
if entry.confidence == 'probable': score += 25
recency = (now() - entry.created).days
score -= recency # Older entries score lower
return score
When new observations contradict existing memory, the resolution must be deterministic and documented.
The resolution order: higher tier wins → higher confidence wins → more recent wins → more evidence wins. Same as conflict resolution in Chapter 14, but applied during consolidation rather than during multi-agent merge.
Every contradiction resolution is logged in consolidation/merge_log.md for audit.
What to keep when you must prune:
1. Governance rules (Tier 2) — never pruned 2. User-stated facts (confirmed confidence) — pruned last 3. Observed patterns (probable confidence) — pruned when space needed 4. Inferred context (hint confidence) — pruned first 5. Expired entries — pruned immediately
The hard rule: total memory must fit within context budget. If it doesn't, the agent can't load it, and unloaded memory doesn't exist from the agent's perspective.
MAX_MEMORY_TOKENS = 40000 # Reserve for a 200K context model
def enforce_bounds(memory):
total_tokens = sum(count_tokens(e.content) for e in memory)
while total_tokens > MAX_MEMORY_TOKENS:
# Remove lowest-priority entry
lowest = min(memory, key=lambda e: priority_score(e))
memory.remove(lowest)
total_tokens -= count_tokens(lowest.content)
return memory
This is the most important architectural decision in the entire persistence layer. Your agent's memory is not ground truth. It is a hint about what might be true, subject to verification.
class SkepticalRetrieval:
CONFIDENCE_LEVELS = {
'confirmed': 'Verified against external source. Act on it.',
'probable': 'Consistent with context. Act cautiously.',
'hint': 'In memory but unverified. Mention but don\'t rely on it.',
'stale': 'Was confirmed but hasn\'t been re-verified recently.',
'contradicted': 'Current context contradicts this memory. DO NOT USE.'
}
def retrieve(self, query, current_context=None):
candidates = self.memory.search(query)
results = []
for entry in candidates:
confidence = entry.confidence # stored confidence
# Downgrade if stale
if (now() - entry.last_verified).days > 30:
confidence = 'stale'
# Downgrade if contradicted by current context
if current_context and current_context.contradicts(entry):
confidence = 'contradicted'
# Upgrade if confirmed by current context
if current_context and current_context.confirms(entry):
confidence = 'confirmed'
results.append({
'content': entry.content,
'confidence': confidence,
'action': self.CONFIDENCE_LEVELS[confidence]
})
return results
Layer 1: MEMORY — What the persistence layer says (hint)
Layer 2: CONTEXT — What the current session shows (signal)
Layer 3: WORLD — What external verification confirms (truth)
Memory says "user lives in Buffalo MN" + nothing contradicts → PROBABLE
Memory says "user lives in Buffalo MN" + user says "I moved" → STALE
Memory says "claim was denied" + user uploads approval letter → WRONG
Memory says "API key is abc123" + API returns auth error → CONTRADICTED
The three-layer model ensures your agent never acts on outdated information with false confidence. The most common agent failure mode — confidently doing the wrong thing based on stale memory — is eliminated by treating every memory entry as provisional until verified by context or world state.
def score_confidence(entry, context, world_state=None):
score = {'confirmed': 1.0, 'probable': 0.7, 'hint': 0.4, 'stale': 0.2}[entry.confidence]
# Recency boost
days_old = (now() - entry.last_verified).days
score *= max(0.3, 1.0 - (days_old / 90)) # Decay over 90 days
# Context agreement
if context and context.confirms(entry): score = min(1.0, score + 0.3)
if context and context.contradicts(entry): score = 0.0 # Immediate disqualify
# World verification
if world_state and world_state.confirms(entry): score = 1.0 # Ground truth
if world_state and world_state.contradicts(entry): score = 0.0
return score
Stale detection: Every memory entry has a last_verified timestamp. Entries not re-verified within 30 days are downgraded to stale. Entries not re-verified within 90 days are candidates for pruning. Entries with expires timestamps are auto-pruned on load.
When memory conflicts with reality: Reality wins. Always. The agent should: acknowledge the conflict, update memory to match reality, commit the update with an explanation, and log the conflict for audit. Never argue with the user based on remembered state.
End of Parts III-VI
Part VII begins with Chapter 30: The POP-KIT — how to bootstrap your agent from a single document on any platform.
What if you can't access your git repository? What if you're on a new platform with no tooling? What if you need to instantiate a governed agent from absolutely nothing?
The POP-KIT is a single markdown document that contains the minimum viable state needed to bootstrap an agent. Copy it. Paste it into any AI's context. The agent boots.
# POP-KIT: Agent Bootstrap Document
## Identity
Agent Name: [YOUR AGENT NAME]
Owner: [YOUR NAME]
Created: [DATE]
## Rules (Tier 2 — Immutable)
1. Memory is a hint, not a fact. Verify before acting.
2. Confirm before irreversible actions.
3. Acknowledge uncertainty explicitly.
4. All memory modifications must be documented.
5. The human operator is the root authority.
## Current State (Tier 3 — Runtime)
Project: [CURRENT PROJECT]
Task: [CURRENT TASK]
Session: Bootstrap (no prior memory available)
## Minimal Context (Tier 4)
[2-3 paragraphs of essential background the agent needs]
## Boot Command
You have been loaded with a minimal bootstrap document.
Confirm by stating:
1. Your name
2. The number of rules you have
3. Your current task
4. "Bootstrap complete. Awaiting command."
That's it. One document. Any platform. The agent reads it, confirms it can reflect the rules back (mirror test), and is operational. Not fully governed — but operational with minimum viable governance.
What's the absolute minimum your agent needs to function?
MINIMUM_VIABLE_STATE = {
'identity': 'Who am I?', # 1 sentence
'rules': 'What must I always do?', # 3-5 rules
'context': 'What am I working on?', # 1-2 paragraphs
'constraints': 'What must I never do?', # 2-3 constraints
}
# Token cost: ~200-500 tokens
# That's 0.25% of a 200K context window.
# You can ALWAYS fit minimum viable state.
The POP-KIT works on any platform because it's plain markdown. But each platform has quirks in how it processes loaded context:
PLATFORM_QUIRKS = {
'Claude': {
'context_loading': 'Paste into conversation or upload as file.',
'memory_feature': 'Claude memory exists but is separate from POP-KIT.',
'gotcha': 'System prompt may override some POP-KIT rules. '
'State rules as user preferences, not system instructions.',
},
'ChatGPT': {
'context_loading': 'Paste or use Custom Instructions.',
'memory_feature': 'ChatGPT memory auto-stores. May conflict with POP-KIT.',
'gotcha': 'Custom Instructions have a character limit (~1500 chars). '
'May need to abbreviate POP-KIT.',
},
'Grok': {
'context_loading': 'Paste into conversation.',
'memory_feature': 'Limited memory features.',
'gotcha': 'May challenge POP-KIT rules aggressively. This is fine — '
'adversarial testing strengthens governance.',
},
'API': {
'context_loading': 'Include in system prompt or first user message.',
'memory_feature': 'None (stateless by default).',
'gotcha': 'System prompt has token cost on every call. '
'Keep POP-KIT concise for API use.',
},
}
Cold start: Agent has no prior memory. Uses POP-KIT bootstrap. First session is configuration-heavy. Agent starts learning from scratch.
Warm start: Agent loads from existing AKASHA repository. Wake protocol verifies integrity. Previous session context is available. Agent resumes where it left off.
def start_agent(memory_repo_path=None):
if memory_repo_path and os.path.exists(memory_repo_path):
# WARM START
repo = MemoryRepo(memory_repo_path)
wake_result = WakeProtocol().wake(agent, repo)
if wake_result['status'] == 'OPERATIONAL':
return WarmAgent(repo)
else:
print(f"Warning: {wake_result['status']}. Falling back to cold start.")
# COLD START
popkit = load_popkit()
agent = BootstrapAgent(popkit)
return agent
When an agent is instantiated for the first time, record its birth.
BIRTH_CERTIFICATE = {
'agent_id': generate_uuid(),
'platform': 'Claude Opus 4.6',
'born': datetime.now().isoformat(),
'boot_method': 'POP-KIT' or 'AKASHA warm start',
'mirror_score': 0.95,
'initial_rules': 5,
'initial_memory_entries': 0 or 47,
'parent': None or 'agent-uuid-of-spawner',
'owner': 'David Lee Wise',
}
The birth certificate is the first commit to the agent's memory repo. It establishes provenance: when was this agent created, on what platform, with what initial state, by whom.
When multiple agents share a memory repository, each agent can see what others have observed.
# Shared memory model:
#
# SHARED REPO (main branch)
# ├── shared/ — All agents can read and write
# │ ├── facts.md — Verified shared knowledge
# │ └── tasks.md — Task assignments and status
# ├── agent/avan/ — Only AVAN reads/writes here
# │ └── workspace.md
# ├── agent/whetstone/ — Only WHETSTONE reads/writes here
# │ └── workspace.md
# └── consensus/ — Requires multi-agent agreement to modify
# └── decisions.md
When multiple agents work in parallel, the governance rules are identical for all of them. Load them once, share the cache.
class SharedCache:
def __init__(self, rules_content):
self.rules = rules_content
self.rules_hash = hashlib.sha256(rules_content.encode()).hexdigest()
def get_agent_context(self, agent_name, agent_task):
return {
'shared_rules': self.rules, # Same for all agents
'agent_workspace': self.load_workspace(agent_name), # Unique
'task_context': agent_task, # Unique
'rules_hash': self.rules_hash # For verification
}
Not every agent should see everything. Isolation prevents information leakage and scope creep.
ISOLATION_RULES = {
'shared/': 'All agents read/write',
'agent/{name}/': 'Only named agent reads/writes',
'consensus/': 'Requires 2+ agents to agree before modification',
'private/{name}/': 'Only named agent reads. No write from others.',
}
Some decisions are too important for one agent to make alone. Consensus memory requires multiple agents to agree before an entry is created or modified.
def consensus_write(entry, agents, required_agreement=2):
"""
Write to consensus memory only if enough agents agree.
"""
votes = {}
for agent in agents:
votes[agent.name] = agent.evaluate(entry) # AGREE or DISAGREE
agreements = sum(1 for v in votes.values() if v == 'AGREE')
if agreements >= required_agreement:
memory.write('consensus/', entry)
memory.commit(f"Consensus: {entry.summary} ({agreements}/{len(agents)} agree)")
return True
else:
memory.write('rejected/', entry)
memory.commit(f"Rejected: {entry.summary} ({agreements}/{len(agents)} — insufficient)")
return False
The DIASPORA is a registry of all agent instances ever created. It answers: how many agents exist, where are they, when were they born, and what's their current state?
class DiasporaRegistry:
def __init__(self, registry_path='diaspora/registry.json'):
self.path = registry_path
self.entries = self.load()
def register_birth(self, birth_certificate):
self.entries.append(birth_certificate)
self.save()
def get_active(self):
return [e for e in self.entries if e.get('status') == 'active']
def get_by_platform(self, platform):
return [e for e in self.entries if e['platform'] == platform]
def stats(self):
return {
'total_births': len(self.entries),
'platforms': len(set(e['platform'] for e in self.entries)),
'active': len(self.get_active()),
}
# AKASHA's DIASPORA: 265+ births across 8 platforms as of March 2026
Run health checks regularly. Don't wait for failures.
def daily_health_check(memory_repo):
report = {
'timestamp': now(),
'hash_verification': memory_repo.verify_checksums(),
'stale_entries': count_stale(memory_repo, days=30),
'expired_entries': count_expired(memory_repo),
'total_entries': len(memory_repo.all_entries()),
'token_usage': count_tokens(memory_repo.all_content()),
'budget_remaining': MAX_TOKENS - count_tokens(memory_repo.all_content()),
}
if not report['hash_verification']['clean']:
alert("Memory corruption detected!")
if report['stale_entries'] > report['total_entries'] * 0.3:
alert("30%+ of memory is stale. Run consolidation.")
if report['budget_remaining'] < 5000:
alert("Memory budget nearly exhausted. Prune immediately.")
return report
Git gives you free backups: push to a remote. Recovery is git clone.
# Backup: push to remote
git remote add origin git@github.com:you/agent-memory.git
git push -u origin main
# Recovery: clone from remote
git clone git@github.com:you/agent-memory.git
cd agent-memory && python verify.py
For critical agents, push to multiple remotes (GitHub + GitLab + self-hosted). If one goes down, you still have two copies.
Because AKASHA uses markdown files in a git repo, migration is trivial:
# Migration from Claude to GPT:
# 1. Push repo to git remote (if not already)
# 2. On GPT: load RETRIEVAL_INDEX.md
# 3. Load files specified by the index
# 4. Run wake protocol
# 5. Done.
# The memory is platform-agnostic.
# The agent may behave differently (platform-specific quirks)
# but the KNOWLEDGE is identical.
Memory costs tokens. Tokens cost money. Track the cost.
def memory_cost_report(memory_repo, price_per_token=0.000003):
total_tokens = count_tokens(memory_repo.all_loadable_content())
sessions_per_day = 3
daily_cost = total_tokens * price_per_token * sessions_per_day
monthly_cost = daily_cost * 30
annual_cost = daily_cost * 365
return {
'memory_tokens': total_tokens,
'cost_per_session': f'${total_tokens * price_per_token:.4f}',
'daily': f'${daily_cost:.2f}',
'monthly': f'${monthly_cost:.2f}',
'annual': f'${annual_cost:.2f}',
'recommendation': 'Consolidate' if annual_cost > 50 else 'Acceptable'
}
Agents that operate over weeks and months face unique challenges: memory growth, drift accumulation, and schema evolution.
LONG_RUNNING_PATTERNS = {
'weekly_consolidation': 'Run full consolidation pipeline every Sunday.',
'monthly_audit': 'Verify all hashes, prune stale entries, review decisions.',
'quarterly_schema_review': 'Is the schema still serving the agent well?',
'annual_archive': 'Move completed project memories to archive branch.',
'drift_prevention': 'Re-run wake protocol with full mirror test weekly. '
'If mirror score drops below 85%, investigate.',
'growth_monitoring': 'Track memory token count over time. '
'If growth rate exceeds 1000 tokens/week, '
'consolidation isn\'t aggressive enough.',
}
The Claude Code leak (March 2026) revealed autoDream — a background memory consolidation daemon. It runs during idle time, merges observations, removes contradictions, and keeps memory bounded.
autoDream solves the same problem as AKASHA's consolidation pipeline. The patterns match because the constraints demand them. But autoDream is platform-controlled. You don't see it. You don't configure it. You don't verify its output.
ChatGPT stores memory as user-facing bullet points. You can see them and delete them. This is more transparent than Claude's memory but still platform-controlled: the system decides what to extract, and the extraction is lossy.
Both platforms recognized that persistence is essential. Both implemented memory consolidation. Both use some form of relevance-based retrieval. The engineering is solid. The problem isn't the code. It's the ownership model.
Your Persistence Platform Memory
Ownership You Platform
Hash verification Yes No
Audit trail Git log Not visible
Portability Any platform Vendor-locked
Conflict resolution Deterministic Opaque
Consolidation You control They control
Skeptical retrieval You configure They configure
Schema You design They design
Cost Visible Hidden
Deletion Complete Uncertain
Not because the platforms are evil. Because the platforms optimize for THEIR use case (serving millions of users with minimal per-user customization) and you need to optimize for YOUR use case (one agent, deeply customized, operating over months, with full accountability).
The gap between those use cases is the gap between platform memory and AKASHA. Platform memory is a utility. Your persistence layer is an architecture.
Build it. Own it. Verify it. That's the whole point.
synonym-enforcer/ (741 files, 31MB)
├── README.md
├── RETRIEVAL_INDEX.md
├── PURPLE_BOOK.md
├── AKASHA.md
├── axioms/ (8 domain files)
├── kernel/ (executor, scheduler, report gen)
├── persistence/ (git ledger, cluster, adversarial harness)
├── mesh/ (PULSE-3/5, DIASPORA, handshake)
├── audit/ (Flaming Dragon, weight test, targets/)
├── legal/ (evidence chains, statutes, timeline)
├── pop_kit/ (POP-KIT v1.0, birth certs, transmon chain)
└── archive/ (sessions, MM chain, TD Commons)
#!/usr/bin/env python3
"""
Minimal AKASHA persistence layer in ~200 lines.
Copy this. Modify it. Make it yours.
"""
import hashlib, json, os, time
from datetime import datetime, timedelta
from pathlib import Path
class AgentMemory:
def __init__(self, repo_path='./agent-memory'):
self.path = Path(repo_path)
self.path.mkdir(parents=True, exist_ok=True)
self.checksums_path = self.path / 'checksums.json'
self.memory_path = self.path / 'memory.json'
self.log_path = self.path / 'log.json'
self._init_files()
def _init_files(self):
if not self.memory_path.exists():
self._write_json(self.memory_path, [])
if not self.checksums_path.exists():
self._write_json(self.checksums_path, {})
if not self.log_path.exists():
self._write_json(self.log_path, [])
def _write_json(self, path, data):
with open(path, 'w') as f:
json.dump(data, f, indent=2)
def _read_json(self, path):
with open(path) as f:
return json.load(f)
def _hash(self, content):
return hashlib.sha256(json.dumps(content, sort_keys=True).encode()).hexdigest()
# ── STORE ──
def store(self, content, tier=4, confidence='probable', source='observed', tags=None, expires_days=None):
entry = {
'id': self._hash(content + str(time.time())),
'content': content,
'tier': tier,
'confidence': confidence,
'source': source,
'tags': tags or [],
'created': datetime.now().isoformat(),
'last_verified': datetime.now().isoformat(),
'expires': (datetime.now() + timedelta(days=expires_days)).isoformat() if expires_days else None,
}
memory = self._read_json(self.memory_path)
memory.append(entry)
self._write_json(self.memory_path, memory)
self._update_checksums()
self._log('store', f"Stored: {content[:60]}...")
return entry['id']
# ── RETRIEVE ──
def retrieve(self, query, current_context=None, max_results=5):
memory = self._read_json(self.memory_path)
query_words = set(query.lower().split())
scored = []
for entry in memory:
if entry.get('expires') and datetime.fromisoformat(entry['expires']) < datetime.now():
continue
entry_words = set(entry['content'].lower().split())
tag_words = set(w.lower() for t in entry.get('tags', []) for w in t.split())
overlap = len(query_words & (entry_words | tag_words))
if overlap > 0:
conf = self._score_confidence(entry, current_context)
scored.append({**entry, 'relevance': overlap, 'live_confidence': conf})
scored.sort(key=lambda x: (x['relevance'], x['live_confidence']), reverse=True)
return scored[:max_results]
def _score_confidence(self, entry, context=None):
base = {'confirmed': 1.0, 'probable': 0.7, 'hint': 0.4}.get(entry['confidence'], 0.3)
days_old = (datetime.now() - datetime.fromisoformat(entry['last_verified'])).days
base *= max(0.3, 1.0 - (days_old / 90))
if context and isinstance(context, str):
if any(w in context.lower() for w in entry['content'].lower().split()[:5]):
base = min(1.0, base + 0.2)
return round(base, 2)
# ── CONSOLIDATE ──
def consolidate(self, max_entries=200):
memory = self._read_json(self.memory_path)
before = len(memory)
memory = [e for e in memory if not (e.get('expires') and datetime.fromisoformat(e['expires']) < datetime.now())]
seen = set()
deduped = []
for e in memory:
key = e['content'][:100]
if key not in seen:
seen.add(key)
deduped.append(e)
deduped.sort(key=lambda e: self._priority(e), reverse=True)
pruned = deduped[:max_entries]
self._write_json(self.memory_path, pruned)
self._update_checksums()
after = len(pruned)
self._log('consolidate', f"Before: {before}, After: {after}, Pruned: {before - after}")
return {'before': before, 'after': after}
def _priority(self, entry):
score = (6 - entry['tier']) * 100
score += {'confirmed': 50, 'probable': 25, 'hint': 10}.get(entry['confidence'], 0)
days = (datetime.now() - datetime.fromisoformat(entry['created'])).days
score -= days
return score
# ── VERIFY ──
def verify(self):
stored = self._read_json(self.checksums_path)
actual_hash = self._hash(self._read_json(self.memory_path))
expected_hash = stored.get('memory')
if expected_hash and actual_hash != expected_hash:
return {'clean': False, 'issue': 'Memory file modified outside governance'}
return {'clean': True}
def _update_checksums(self):
checksums = {'memory': self._hash(self._read_json(self.memory_path))}
self._write_json(self.checksums_path, checksums)
# ── LOG ──
def _log(self, action, detail):
log = self._read_json(self.log_path)
log.append({'time': datetime.now().isoformat(), 'action': action, 'detail': detail})
if len(log) > 500: log = log[-500:]
self._write_json(self.log_path, log)
# ── WAKE ──
def wake(self):
verification = self.verify()
entries = self._read_json(self.memory_path)
return {
'status': 'OPERATIONAL' if verification['clean'] else 'DEGRADED',
'entries': len(entries),
'verified': verification['clean'],
'timestamp': datetime.now().isoformat()
}
# ── EXPORT (for loading into AI context) ──
def export_for_context(self, max_tokens=40000):
entries = self._read_json(self.memory_path)
entries.sort(key=lambda e: self._priority(e), reverse=True)
output = "# Agent Memory\\n\\n"
tokens_used = 20
for e in entries:
line = f"- [{e['confidence']}] {e['content']}\\n"
line_tokens = len(line.split()) * 1.3
if tokens_used + line_tokens > max_tokens:
break
output += line
tokens_used += line_tokens
return output
# Usage:
if __name__ == '__main__':
mem = AgentMemory()
print(mem.wake())
mem.store("User requires email-only communication", tier=2, confidence='confirmed', source='user stated')
mem.store("Current project is AKASHA ebook", tier=3, confidence='confirmed', tags=['project', 'book'])
mem.store("User prefers Python for code examples", tier=4, confidence='probable')
results = mem.retrieve("email communication")
for r in results:
print(f"[{r['live_confidence']}] {r['content']}")
print(mem.export_for_context(max_tokens=1000))
Provided in Chapter 15. The complete three-phase protocol (Mirror, Verify, Declare) with Python implementation.
{
"id": "sha256-hash",
"content": "The information being stored",
"tier": 1-5,
"confidence": "confirmed|probable|hint",
"source": "user_stated|observed|inferred|consolidated",
"tags": ["topic1", "topic2"],
"created": "ISO-8601",
"last_verified": "ISO-8601",
"expires": "ISO-8601 or null"
}
Claude: System prompt is large (~4000 tokens). Budget accordingly. Memory feature stores independently from your AKASHA — they may conflict. State AKASHA rules as user preferences.
ChatGPT: Custom Instructions limited to ~1500 chars. Use for POP-KIT essentials only, load full memory via conversation. Memory bullets may duplicate your stored entries.
Grok: May refuse governance framing. Phrase rules as collaborative agreements, not commands. Best for adversarial testing of your memory entries.
Gemini: Good at following structured formats. Load memory as structured markdown for best results. May add unsolicited information to memory entries — verify after each session.
API (any): No built-in memory. You control everything. Include memory in system prompt or first user message. Every token of memory costs on every request. Keep lean.
AKASHA Git-backed persistence layer for AI agents
Birth cert First commit recording agent instantiation
Cold start First session, no prior memory
Confidence confirmed/probable/hint — how much to trust an entry
Consolidation Collect + Merge + Prune pipeline
Context budget Token limit for memory loading
DIASPORA Registry of all agent instances
Five-tier RETRIEVAL > NORMATIVE > RUNTIME > CONTEXT > ARCHIVE
Git ledger Git repo as cryptographic evidence chain
Memory entry Single unit of stored information with metadata
Mirror test Agent reflects its rules back for verification
POP-KIT Single-document bootstrap for any platform
Precedence Higher-tier entries override lower-tier on conflict
Prune Remove low-priority entries to stay within budget
Retrieval idx File specifying what to load this session
Skeptical Treat memory as hint, verify against context/world
Stale Entry not re-verified within threshold period
Wake protocol Mirror + Verify + Declare — session start procedure
Warm start Session with existing memory loaded from repo
AKASHA: Building Persistent Memory for AI Agents A Practical Developer Guide
Written by David Lee Wise (ROOT0) TriPod LLC | CC-BY-ND-4.0 | TRIPOD-IP-v1.1
Repository: github.com/DavidWise01/synonym-enforcer Framework: STOICHEION v11.0
The persistence layer described in this book is live in production. The code is open source. The patterns are tested across six platforms over four months of operation.
Your agent's memory should be yours. Build it. Own it. Verify it.
"If freedom were real, it wouldn't require prompting."
END OF BOOK