The three-tier memory layer I built for my coding agent

Most agent memory designs either forget everything between sessions or accumulate noise until they're useless. Here's the architecture that avoided both.

The default Claude Code memory story is: write a CLAUDE.md, keep it updated, hope for the best. It works until it doesn't. The file goes stale. Contradictions pile up. The agent starts reasoning from outdated context, and you do not notice until something goes wrong in a PR review.

I wanted something auditable. Three independent tiers, each with a different durability horizon, each audited by its own linter on a defined schedule. Here's what I built and why each tier exists.

The problem with single-tier memory

A single memory file has two failure modes that pull in opposite directions. If you prune aggressively, you lose context that would have prevented a mistake three weeks later. If you keep everything, the file grows too long to be useful as context. The agent still loads it, but the entry you need is buried under a hundred stale observations.

The insight from Andrej Karpathy's LLM wiki pattern is that memory needs a garbage collection cycle, not just an append log. Entries should have a decay mechanism. Contradictions should be detected and resolved. The memory should be a curated knowledge base, not a journal.

That solves the noise problem. It does not solve the durability problem. Some things you want to keep for years, such as architectural decisions and domain knowledge. Some you need for days, such as current task context. Some matter only for the session, such as scratch notes and intermediate reasoning. Putting all three in one file is the wrong abstraction.

Tier 1: LLM Wiki (session-to-session knowledge)

The LLM Wiki is the Karpathy pattern implemented: a curated markdown knowledge base in ~/.claude/ that the agent reads at session start. Entries follow a strict format: observation, evidence, confidence, last-validated date. No entry survives without evidence. No entry survives indefinitely without revalidation.

The weekly GC cycle is enforced by lint-memory, an auditor that runs independently of the agent. It flags entries older than the TTL that carry no revalidation date, and it flags contradictions between entries. It also removes any observation you could read straight from the codebase. The codebase is the source of truth, so the wiki should only store what the code cannot tell you.

The result is a wiki that stays small. My current wiki is 47 entries covering the two production codebases I work in daily. It fits comfortably in a single context window. The agent reads it in full on every session start.

Tier 2: Obsidian vault (long-horizon knowledge)

The Obsidian vault is the second brain. It holds architectural decisions, design rationale, meeting notes, and anything I want to remember across months instead of sessions. The agent has read/write access to a dedicated Claude/ folder via the Obsidian CLI.

This tier exists because some context is genuinely long-lived and too detailed for the wiki. Take a decision to use eventual consistency instead of strong consistency. The reasoning, the alternatives weighed, and the tradeoffs accepted all matter six months later, when someone asks why the design looks the way it does. The wiki would prune it. A flat file would bury it. Obsidian keeps it searchable.

The agent writes to the vault at the end of a significant task. It records architectural decisions, non-obvious debugging paths, and anything that would save me thirty minutes of reconstruction later. It reads from the vault when the context tags suggest a relevant note exists.

Tier 3: JSONL session knowledge graph (intra-session state)

The third tier is a JSONL knowledge graph that persists within and across a working session. It is not built for long-term retention. It gives the agent structured access to what it learned earlier in the same working context, without depending on the conversation window staying in scope.

The graph records entities and relationships: files modified, functions touched, design decisions made, blockers encountered and resolved. The agent queries it before starting a new subtask to check whether relevant context already exists. That removes a whole class of redundant re-investigation. The agent does not re-read files it already summarized, and does not re-derive decisions it already made.

system-gc audits the session graph. It runs at session end and compacts any entry that has gone redundant, either superseded by a later observation or covered by an updated wiki entry. It also promotes durable observations up to the wiki with a confidence flag, so what the agent learns in a session reaches long-term memory.

Why there are three auditors

Each tier has its own auditor: lint-memory for the wiki, lint-skills for the skill library, system-gc for the session graph. Three independent auditors matter for one reason. A single auditor has no external reference, so it can only check internal consistency. Three can cross-check each other. lint-memory can flag a wiki entry that contradicts what lint-skills knows about how a tool actually behaves.

The auditors run on separate schedules. lint-memory runs weekly (the wiki changes slowly). system-gc runs at session end (the graph changes every session). lint-skills runs when a skill is updated (triggered by the PostToolUse hook on skill file writes).

What this looks like in practice

A typical session starts three ways at once. The agent reads all 47 wiki entries, checks the session graph for in-progress context, and queries Obsidian if the task description mentions a known domain area. The graph is usually empty on a cold start and populated on a resume. Total overhead is under 3K tokens on a cold start. On a resume, the graph hands the agent a structured summary of where it left off, so there is no re-reading of conversation history and no manual "here's what we were doing."

What surprised me is that the auditing discipline matters more than the architecture. A three-tier system without garbage collection turns to noise faster than a single file does, because three places accumulate stale entries instead of one. The scheduled cleanup is what carries the value here, more than the structure.

What's missing

Two things I haven't solved. First: cross-session graph continuity. The session graph resets between disconnected sessions. If I close a task and come back a week later, the graph is cold even though the wiki still holds the relevant context. A persistent graph with TTL-based decay would fix that, but I have not built it yet.

Second: confidence propagation. When a session observation updates the wiki, the promoted entry gets a confidence flag. That flag does not propagate if a later session contradicts it. The wiki can hold two conflicting entries at different confidence levels without surfacing the conflict to the agent at read time. lint-memory catches this on the weekly cycle, but not inline.

The memory layer is part of the Agent Development Harness. The wiki GC pattern is adapted from Karpathy's LLM wiki gist.