← All field notes

The Memory System That Makes Claude Code Coherent Across Sessions

The biggest problem with AI assistants is amnesia. Every conversation starts from zero. Here's how I solved that with a persistent memory layer that gives Claude Code real continuity.

Every Claude Code session starts without context from earlier sessions. It does not know who I am, what I am building, which bugs I fixed yesterday, or what my priorities are. A new conversation feels like the first day of an intern's job, except the intern forgets everything overnight and arrives blank the next morning.

For a quick coding question, that is fine. For running a company across weeks and months, it is a dealbreaker. I needed Claude Code to remember.

The Architecture

The solution is a memory system built from files. Every project has a memory directory at ~/.claude/projects/[project-hash]/memory/. Inside it are individual .md files, each with YAML frontmatter.

The schema for each memory file:

---
name: "client project status"
description: "Arabia app paused, Saudi Hotels prioritized"
type: project
---

Client put Resorts Magazine on hold to prioritize Saudi Hotels app.

**Why:** Email from client, March 31st
**How to apply:** Don't propose work on Resorts until client resumes

An index file called MEMORY.md loads automatically into every Claude Code conversation. It contains one link per line, one for each memory file, so it acts as a table of contents rather than holding the content itself. The index is capped at 200 lines. When Claude needs details, it reads the specific memory file.

Four Types of Memory

Each type has a distinct purpose, and getting that taxonomy right matters more than the implementation.

User captures who I am: a developer, 18, who builds iOS apps, runs SubraLabs, prefers direct communication, and uses an iPhone 16 for testing. This calibrates how Claude talks to me. You explain things differently to a senior engineer than to someone writing their first app.

Feedback records both corrections and confirmations. This is the most important type. When I say "don't do that" or "yes, exactly like that", the system records the rule with the reason.

Example: "Always use light backgrounds, never black. Why: User prefers a magazine aesthetic with lots of light. How to apply: Use #FFFFFF or cream colors for all app backgrounds."

Confirmations matter as much as corrections. If the system only records mistakes, it becomes overly cautious and starts questioning approaches that worked. When I accept an unusual choice without pushback, that is signal too.

Project tracks state that cannot be derived from code. These are things such as: "The client paused Resorts Magazine to prioritize the new Saudi Hotels app. Why: Email from March 31st. How to apply: Don't propose Resorts work until the client resumes the conversation."

This type decays fast. A fact recorded two weeks ago might not be true anymore. The "why" field helps future sessions judge whether the memory is still relevant.

Reference stores pointers to external resources, specifically where to find information outside the project. Examples include: "Pipeline bugs are tracked in the Linear project 'INGEST'." Or: "The Grafana dashboard at grafana.internal/d/api-latency is what oncall watches."

What NOT to Save

This is as important as knowing what to save:

  • Code patterns, conventions, architecture can be read directly from the codebase
  • Git history is authoritative through git log and git blame
  • Debugging solutions live in the code itself, with context in the commit message
  • Anything already in CLAUDE.md belongs in the project's static instructions file
  • Current session state is ephemeral and not useful tomorrow

The rule is that memory is for facts that cannot be derived again. If you can read something from the repo, do not put it in memory.

How It Works in Practice

The flow on a typical day looks like this:

  1. I open a Claude Code session. MEMORY.md loads automatically into context.
  2. Claude sees the memory titles and descriptions and decides which ones are relevant to the current task.
  3. When it needs details, it reads the specific file, such as feedback_app_style.md.
  4. If I correct something during the session ("no, the client changed their mind, they want the dark theme now"), Claude creates or updates a memory file.
  5. Tomorrow's session finds that file and takes it into account.

The system is not perfect. Memories go stale. A fact recorded two weeks ago might not be true today. So there is a discipline: before acting on a memory, verify it. If a memory says "the function processPayment is in billing.swift", grep for it first. "The memory says X exists" is not the same as "X exists now."

The Bigger Picture: Five Layers of Persistence

Memory is one layer of a larger persistence stack. The full persistence stack at SubraLabs has five layers:

CLAUDE.md contains static project instructions: conventions, rules, and repo structure. Like an employee handbook, it changes rarely.

PROJECT.md is the source of truth for project state, updated daily. What's active, what's blocked, who's waiting on what. Like the project board.

status/ holds daily logs. What got done, what's next, blockers encountered. Like a written standup.

Hooks run at SessionStart, syncing email and presenting context. They provide automation without memory and run every time.

Memory is the subjective layer. Preferences, lessons learned, context that isn't in the code or the logs.

Together, these five layers create functional continuity. The result is imperfect: there are gaps, stale data, and missed context. Tomorrow's session knows who I am, what I prefer, where the projects stand, and what happened yesterday.

Right now the system has 21 memory files for the main project: 1 user profile, 9 feedback entries, 6 project states, and 5 references. The collection remains small and manageable, and it does the job. The point is not to build a sophisticated database. Claude Code only needs enough context to stop being the intern who forgot everything overnight.