DEV Community

plasmon
plasmon

Posted on

I Designed a Memory System for Claude Code — 'Forgetting' Was the Hardest Part

Everyone talks about making AI remember things. Handoff prompts. System instructions. Memory files. The implicit assumption is always the same: the problem is that AI forgets, so the solution is to make it remember more.

After weeks of using Claude Code on a moderately complex project, I discovered this framing is backwards. The hardest part of AI memory management isn't remembering — it's forgetting.


Where Handoff Prompts Break Down

Handoff prompts work well for short-term continuity. Summarize the previous session, paste it into a new one, keep going. Simple and effective.

The cracks appear when a project runs for weeks. Here's what actually happened.

On day 3, I told Claude: "Use approach A." On day 4: "Actually, switch to approach B." The handoff prompt captures the latest state — B. So far, correct.

A week later, a new context made approach A relevant again. But by then, A had been overwritten in the handoff summary. Not just the instruction — the reasoning behind why A existed in the first place was gone. A handoff prompt preserves the snapshot, not the history.

The second failure mode is bloat. If you stuff every past decision and instruction into the handoff, it grows unbounded. Old directives that you've mentally revoked sit alongside current ones. You know the old directive is dead because you issued a newer one. The AI doesn't — it treats both as equally valid.


Human Memory and AI Memory Are Structurally Inverted

The root cause clicked when I realized human and AI memory operate on opposite principles.

Humans are recency-biased. What you said three months ago is background noise. What you said yesterday is your current position. This is the recency effect from serial position theory in cognitive psychology — a universal human trait, not a bug.

AI has no recency bias. Line 1 of CLAUDE.md carries the same weight as line 100. A feedback note from last week is weighted identically to one from today. There is no temporal decay, no "that was then, this is now" mechanism.

This asymmetry causes real problems in long-running projects. I accumulated writing style feedback over time: "Write more politely" (week 1) and "Too polite, be more direct" (week 2). Both instructions stayed active in memory. Claude alternated between them based on context, but its selection criteria didn't match mine. To me, "be direct" was obviously the current rule. To Claude, both rules were simultaneously valid.

Humans run their memory on a forgetting-first architecture. AI runs on a forgetting-never architecture. Handoff prompts don't bridge this gap.


A 3-Layer Memory Architecture

Claude Code has an auto-memory feature that generates memory files from conversations and loads them into subsequent sessions. Used naively, it accumulates files indefinitely with equal weighting — reproducing the exact problem above.

I restructured memory into three layers.

Layer 1: Active (Drives Behavior)

Only memories indexed in MEMORY.md belong to this layer. Claude Code automatically loads MEMORY.md at session start, so Layer 1 entries are the only ones that actively shape behavior.

# Memory Index (Active Only)
> Archive protocol: Archived memories → MEMORY_ARCHIVED.md (reference only)

- [feedback_writing_style.md](feedback_writing_style.md) — Write directly, avoid AI-typical phrasing
- [feedback_article_focus.md](feedback_article_focus.md) — One topic per article, no scope creep
- [user_profile.md](user_profile.md) — Hardware × AI specialist, bilingual
Enter fullscreen mode Exit fullscreen mode

Target: 20–40 entries. Beyond that, the bloat problem described below kicks in. This layer defines who the AI is right now.

Layer 2: Archived (Evidence, Not Instruction)

Memories that were once active but should no longer guide behavior. Managed in a separate MEMORY_ARCHIVED.md.

---
name: old_writing_policy
status: archived
archived_at: 2026-03-30
archived_reason: Superseded by direct-style policy. Kept as evidence.
---
Enter fullscreen mode Exit fullscreen mode

The key: files are never deleted. The reasoning behind past decisions has future value. What gets removed is the decision-making authority, not the record.

Layer 3: Vectorized Reference (Search-Only)

When memories exceed ~50 files, the curation cost of Layer 1 rises. The plan (still in progress) is to vectorize older memories into a vector DB like ChromaDB for semantic search.

The critical design constraint: Layer 3 data must never drive decisions. It's reference material, not instruction.

Why? Cosine similarity returns content-similar results, but it can't distinguish "this memory is still valid" from "this memory was revoked two weeks ago." Even with status: archived metadata in the vector store, ranking algorithms don't reliably surface metadata alongside semantic similarity.

The risk is that the AI retrieves an archived instruction from Layer 3 and treats it as current guidance. So Layer 3 is explicitly "read-only for context" — never "act on this."


Why "Forgetting" Is the Hardest Design Problem

The most time-consuming part of this architecture was the Layer 1 → Layer 2 transition: the act of making the AI forget.

Deletion achieves forgetting, but destroys the audit trail. A month later, you want to know what the previous policy was and why it existed. It's gone.

Setting active: false in the same file is dangerous. The AI reads the full file content. Even with a flag saying "ignore this," the instruction text enters the context window. Once it's in context, it exerts influence — flags notwithstanding.

The solution was physical separation from the context window. Remove the entry from MEMORY.md. Move it to MEMORY_ARCHIVED.md. Claude Code only auto-loads MEMORY.md, so archived files are invisible at session start. The archived content can still be accessed on demand, but it doesn't passively enter the context.

The human bottleneck is the same as with email folders: "I might still need this" prevents migration. Layer 1 bloats, contradictory instructions accumulate, and you're back to the original problem.

The heuristic that worked: archive when the intent is covered by a newer instruction. Not "might I need this again?" but "is this instruction's purpose served by something currently active?" If yes, archive. If no, keep.


What This Revealed About the Human Side

The biggest shift in my thinking: AI memory management is fundamentally a human design problem, not an AI capability problem.

Handoff prompts ask the AI to "remember." Memory files tell the AI to "reference this." Both approaches treat AI memory as the variable to optimize.

What actually needed to happen was for the human to design the memory structure. What gets remembered, what gets forgotten, what gets preserved as evidence — these are decisions only the human can make. The AI can't determine instruction priority because priority lives in the human's head.

Metacognition — the ability to monitor and control your own cognitive processes — transfers directly to AI memory management. You need to maintain awareness of what you've told the AI, which of those instructions are still valid, and how they interact.

It's overhead. But it's overhead that pays for itself. "Why is Claude behaving differently than last time?" and "I've told it this three times already" — these symptoms disappear when the memory structure is intentionally designed rather than accumulated by default.


Implementation Template

Here's the directory structure I'm using with Claude Code. The concept applies to other tools, though the implementation details differ.

project_root/
├── CLAUDE.md              # Project instructions (near-immutable)
└── .claude/
    └── memory/
        ├── MEMORY.md              # Layer 1 index
        ├── MEMORY_ARCHIVED.md     # Layer 2 index
        ├── feedback_*.md          # User feedback memories
        ├── user_*.md              # User profile memories
        └── project_*.md           # Project state memories
Enter fullscreen mode Exit fullscreen mode

Memory file frontmatter:

---
name: writing_style
description: Writing directive — direct style, no AI phrasing
type: feedback
---
Enter fullscreen mode Exit fullscreen mode

Archive additions:

---
name: old_policy
status: archived
archived_at: 2026-03-30
archived_reason: Superseded by new policy. Retained as evidence.
---
Enter fullscreen mode Exit fullscreen mode

Keep MEMORY.md entries under 150 characters each. The index is a pointer, not the content — details go in individual files.


What's Next

Layer 3 vectorization is still a design concept. The automation I want: memories not referenced for N sessions get flagged as archive candidates. That would cut the human curation cost significantly.

The other missing piece: contradiction detection. When adding a new memory, check it against existing Layer 1 entries for conflicts. Two instructions that contradict each other make AI behavior unpredictable — catching that at write time would prevent a class of problems I've hit repeatedly.

Both of these are solvable engineering problems. The harder part — recognizing that AI memory is a design problem, not a scaling problem — is the insight that changed how I work with Claude Code.

Top comments (0)