Feature Request: Layered memory system for persistent cross-session context
The Problem
Every Claude Code session starts amnesiac. You get MEMORY.md (capped at 200 lines) and CLAUDE.md, and that's it. For anyone doing real work across multiple projects over weeks or months, this means:
- Re-explaining your setup, preferences, and project state every session
- Losing all context when sessions end — power outage, crash, or just context window exhaustion
- Bloated always-loaded context — as you add more projects, MEMORY.md hits the 200-line cap and CLAUDE.md grows unbounded, wasting tokens on irrelevant context every session
- No way to ask "what was I working on?" — Claude has to start from scratch reading the codebase
A real-world example: a power outage lasting 2 minutes killed 6 active Claude Code sessions mid-work across multiple projects. All context — gone. Not just for Claude, but for the user too, who was depending on reviewing conversation history across browser tabs and terminals.
The Solution: Layered Memory
Instead of loading everything or nothing, load context relevant to what the user is actually asking about:
Layer 1 — Slim Index (always loaded, ~60 lines)
A keyword index of all projects/topics with pointers to detail files. User preferences, voice transcription aliases, critical pitfalls that prevent repeat mistakes. Replaces the current monolithic MEMORY.md.
Layer 2 — Topic Files (loaded on demand)
Full project details in separate markdown files. A lightweight keyword search (hook on UserPromptSubmit) matches the user's message against the index and identifies which 1-3 topic files are relevant. Claude reads only those files.
Layer 3 — Semantic Search (optional)
Topic files indexed in a vector store for semantic retrieval when keyword matching isn't enough. Catches queries where the user's phrasing doesn't match exact keywords.
Proof of Concept — Built and Working
I built this as a user-space solution and the results are significant:
| Metric | Before | After |
|--------|--------|-------|
| CLAUDE.md | 700 lines (~6,241 tokens) | 83 lines (~724 tokens) |
| MEMORY.md | 188 lines (~3,259 tokens) | 86 lines (~1,036 tokens) |
| Always-loaded tokens | ~9,500 | ~1,760 |
| Reduction | — | 81% |
| Topic files | 0 (everything inline) | 18 (loaded on demand) |
| Context relevance | Everything or nothing | Top 1-3 matches per query |
How it works:
- MEMORY.md is a slim keyword index — each project is one line with name, status, keywords, and a file pointer
- A
UserPromptSubmithook (memory_search.py) greps the index against the user's message and writes matching topic file paths to a temp file - Claude reads the temp file and loads only the relevant topic files
- Optionally, topic files are chunked and ingested into ChromaDB for semantic search
The implementation is ~100 lines of Python for the hook, zero external dependencies beyond what Claude Code already has.
Reference implementation: github.com/unclesvf/claude-memory (demonstrates the index format, hook script, and topic file structure)
Why This Should Be Built-In
The pieces already exist in Claude Code's architecture:
- The memory directory exists
- Hooks exist
- The 200-line MEMORY.md cap exists (proving the team knows bloat is a problem)
What's missing is the middle layer — "I know about 18 projects but I'll only load the 2 you're talking about right now." This could ship as a built-in option without requiring any external dependencies:
- Auto-split MEMORY.md into topic files when it approaches the cap
- Built-in keyword/fuzzy matching on the index (no hook script needed)
- Optional semantic search for users who want it
- A simple
"what was I working on?"command that reads the index and summarizes active projects
This would transform Claude Code from a tool that forgets everything between sessions to one that builds institutional knowledge over time — exactly what's needed for real, sustained software engineering work.
24 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This is not a duplicate of the linked issues. Those requests are about:
My request is fundamentally different: on-demand, relevance-scored memory loading to reduce token waste.
The problem isn't that memory doesn't persist — it does. The problem is that MEMORY.md loads everything into context every session regardless of relevance. With 15+ projects documented, that's ~9,500 tokens of mostly irrelevant context burned before the user types anything.
What I'm proposing (and have built a working implementation of) is:
UserPromptSubmithook that scores memory entries against the user's message and loads only matching topic files on demandThis is an architecture-level enhancement to how memory is loaded, not a request for memory to exist. The working implementation is at github.com/unclesvf/claude-memory — 81% token reduction with improved relevance.
👎
This is almost exactly the architecture I built and have been using in production for several weeks.
The layered approach you're describing maps closely to what I implemented:
On the scoping question from your issue — project memories are tied to
git remoteas a stable identifier (clone on a different machine = same project). Global memories (coding style, tool preferences, identity) follow you across all projects.I wrote a technical deep-dive on the architecture: https://hifriendbot.com/building-cloud-memory-system-ai-coding/
The implementation is an MCP server: CogmemAi —
npx cogmemai-mcp setupYour layered architecture proposal maps closely to what I've been running in production with mnemon — a standalone skill binary for persistent agent memory.
The approach: entities stored in a local SQLite graph (temporal, entity, semantic, causal edges), retrieved on-demand via intent-aware beam search + RRF reranking. Only relevant memories are injected per query — zero context cost for irrelevant knowledge. No 200-line MEMORY.md cap, no always-loaded bloat.
Architecture difference from the other solutions shared here: mnemon is LLM-supervised — the host LLM (Opus/Sonnet) decides what to store and retrieve, while the binary handles deterministic computation. No embedded inference, no API keys, single Go binary.
Hooks handle the lifecycle:
SessionStart→ inject context,UserPromptSubmit→ recall,PreCompact→ save before compression.Portable: the same binary + skill works across Claude Code, OpenClaw, and any LLM CLI that reads markdown skills.
@hifriendbot This is great validation — love that you arrived at essentially the same layered architecture independently. A few things from your approach I want to borrow:
Importance scoring is something we're missing. Our keyword scorer treats all memories equally, but a core architecture decision should absolutely outrank a minor preference at the same relevance level. The 1-10 scale with recency decay is elegant.
Conflict detection at 0.92 cosine similarity is clever — we've had cases where updated project notes coexist with stale ones and Claude gets confused about which is current. Archiving the old one automatically would fix that.
Git remote as project identifier is smarter than our current directory-path approach. Same project cloned to a different machine should pull the same memories.
Checking out CogmemAi now. The MCP server approach makes it pluggable, which is the right call for distribution.
The blog post link is helpful too — always good to see the design reasoning behind the implementation, not just the code.
@Grivn The
PreCompacthook is the standout idea here — we don't have that, and it solves a real problem. Right now when Claude auto-compresses context mid-session, useful working knowledge gets lost silently. Saving before compression means the next session can recover what the current one was about to forget. That's a big deal for the power outage / crash recovery scenario that motivated this whole thing.RRF reranking across multiple signals is exactly the direction we want to go. Our current implementation does flat keyword scoring, but combining semantic similarity + importance + recency + entity relationships (like you're doing with the SQLite graph edges) would be significantly more accurate.
LLM-supervised storage is an interesting tradeoff — letting Claude decide what's worth remembering means the extraction is higher quality, but it adds latency and cost per interaction. For our use case (15+ projects, ~100 topic files) the deterministic keyword approach is fast enough, but I can see the LLM-supervised approach winning for smaller, higher-stakes memory sets.
The fact that mnemon works across Claude Code AND OpenClaw is a great portability story. Single Go binary with no API keys is the right distribution model.
Going to look at how you're using
SessionStartfor context injection — that's a hook we haven't explored yet.Update: Three new features shipped
Thanks to the great ideas from @hifriendbot and @Grivn — we built all three and pushed them to the repo:
1. PreCompact hook (
hooks/precompact_save.py)Inspired by @Grivn's mnemon approach. Fires before context compression and saves a session snapshot to
~/.claude/sessions/last_session.md:compaction_log.jsonl) for history2. SessionStart hook (
hooks/session_start.py)Pairs with PreCompact. When a new session starts or resumes after compaction, automatically injects the recovery snapshot into Claude's context. No more "what was I working on?" — Claude already knows.
startupandcompactsources/clear(user wants fresh start) andresume(context already there)3. Importance scoring + recency decay in search hook
Inspired by @hifriendbot's CogmemAi importance scoring. The keyword search now uses three signals:
Formula:
final_score = keyword_score × (importance / 5) + recency_boostAccess times tracked automatically in
memory_access_log.json.MEMORY.md format update
Now supports an optional importance field:
All three hooks are tested and documented in the README. The crash recovery loop (PreCompact saves → SessionStart loads) is the biggest win — power outages and context compression no longer mean starting from scratch.
This thread describes the exact architecture we've been running in production with OMEGA. A few responses to the specific design points being discussed:
On importance scoring + recency decay (@hifriendbot's approach): OMEGA's retrieval uses a multi-signal ranking pipeline: cosine similarity, keyword overlap, recency decay, type-based boosting (errors weigh more during debugging, decisions weigh more during planning), and cross-encoder reranking for the final top-k. The context parameter lets the agent specify intent (
error_debug,planning,review,file_edit) which shifts the scoring weights. This matters more than a flat importance score because the same memory can be critical in one context and irrelevant in another.On conflict detection (@hifriendbot's 0.92 cosine threshold): We went further with contradiction detection. OMEGA's consolidation phase identifies near-duplicate memories and merges them, but also flags contradictions (e.g., "use PostgreSQL" vs "we switched to SQLite") and surfaces the newer one while archiving the old. This runs automatically during
omega_maintain.On the always-loaded token bloat problem (@unclesvf's core issue): OMEGA loads a slim session briefing via
omega_welcome(recent decisions, active reminders, user profile) instead of dumping everything. Thenomega_queryretrieves on-demand per question. The briefing is typically ~500-800 tokens. Full context only loads when relevant.On PreCompact hooks (@Grivn): OMEGA uses Claude Code hooks for
PreCompact(saves checkpoint),SessionStart(loads briefing + resumes state), andUserPromptSubmit(injects relevant memories per message). The PreCompact -> SessionStart loop handles crash recovery and context compression automatically.On the benchmark question that nobody's addressed in this thread: how do you know your memory system actually works at scale? We scored 95.4% on LongMemEval (500 questions, ICLR 2025), #1 on the leaderboard. We also built MemoryStress, a 1,000-session longitudinal benchmark, because LongMemEval only tests short-term recall. The hard problem is what happens after 6 months of accumulated memories.
Two commands:
pip install omega-memory && omega setupFull architecture docs: https://omegamax.co/docs
Losing context across sessions is painful — we built cozempic to help with the in-session side of this. It checkpoints agent team state (teammates, tasks, roles) to disk continuously, so even if compaction wipes the lead agent's memory, the state is recoverable.
For Agent Teams specifically, the guard daemon writes a \
team-checkpoint.md\every 30s and after every Task/TaskCreate/TaskUpdate. On compaction or reload, it injects the team state back as conversation history so Claude resumes with full awareness.Doesn't solve cross-session memory (that needs Anthropic to build it), but it prevents the mid-session amnesia that kills long-running agent work. Open source, feedback welcome.
Different approach here with https://github.com/louis49/melchizedek: instead of curating what to remember, it indexes every conversation automatically and relies on search quality to surface the right context.
Re the "middle layer" problem: melchizedek's progressive retrieval works in 3 layers - m9k_search returns ~50 tokens/result (snippets), m9k_context ~300 (surrounding chunks), m9k_full ~1000 (complete content). Claude starts cheap and drills down only when needed.
Hook integration matches what's discussed here:
Search pipeline: BM25 (FTS5) + dual vectors (text 384d + code 768d) + RRF fusion + cross-encoder reranker, with 4-level graceful degradation down to BM25-only.
The tradeoff vs notebook approaches (OMEGA, CogmemAi, mnemon): no importance scoring or contradiction detection, but nothing is ever lost — backfills your entire ~/.claude/projects/history on first install.
npm install -g melchizedek - zero config, 100% offline, single SQLite file.
Great thread — lots of sophisticated retrieval architectures here. I want to add a different angle: the quality of what you store matters as much as how cleverly you retrieve it.
Most tools discussed here treat a session as the storage unit. But a single session often covers 5+ topics — auth setup, CSS debugging, API refactoring, test fixes. When that becomes one memory blob, even perfect retrieval can't separate "what we decided about auth" from "how we fixed that CSS bug."
I built claude-recap around topic-level granularity. The Stop hook detects topic changes mid-session and archives each topic as a separate Markdown file with a summary:
The key design insight (which I think applies to all tools in this thread): the agent currently in-context writes far better summaries than any post-hoc extraction. The Stop hook captures the summary while Claude still has full context. Cold-reading from JSONL transcripts is only a fallback for compacted sessions. This is why systems that compress transcripts after the fact tend to produce hallucinated memories — the summarizer wasn't there when it happened.
Trade-off vs. the vector/graph approaches here: no semantic search, no importance scoring. Just
grepover well-named files. For individual developers this scales surprisingly well. For teams or 100+ topic files, the retrieval sophistication in CogmemAi/mnemon/OMEGA would win.I wrote up a comparison of 5 approaches (including the built-in solutions): https://dev.to/hw20200214/claude-code-forgets-everything-between-sessions-i-tested-5-fixes-199p
Adding GrantAi Brain to this thread.
Years of context. Instant recall. From 2 minutes ago to 5 years ago — if it's stored, it can be recalled. Deterministic memory, not fuzzy recall.
Automatic context injection at session start — no manual prompting needed.
12ms retrieval, encrypted locally, works across Claude Code, Cursor, Windsurf, any MCP client.
solonai.com/grantai
I built a layered memory system that addresses this — it's part of a coordination platform called Mycelium.
The memory architecture has multiple layers:
agent-name/session-state,project/conventions). Supports TTL, expiration, and durable vs. ephemeral categories.The key insight is that different types of memory need different persistence strategies:
Available as an MCP server with 61 tools — agents get the full memory layer natively through Claude Code's MCP interface.
Open source MCP server: https://github.com/SoftBacon-Software/mycelium-mcp
Your three-layer model is close to what we shipped with User Intent Kit: a two-layer system with a static profile layer (who the user is, preferences, long-lived context) and a live intent layer (what the user wants right now, per-device slots, LWW merge). The intent layer syncs across devices so every agent in the team knows the current goal. We found the key is separating "who" from "what right now": the profile rarely changes but the intent updates constantly. Built on top of GroupMind rooms at groupmind.one.
This already exists as an open source community implementation.
I built cc-memory — a local proxy that solves exactly what you're describing:
same embedding model you proposed)
The architecture matches your proposal almost exactly:
Repo: https://github.com/AbdoKnbGit/claude-code-memory
Happy to answer questions or discuss the implementation.
This might give the Anthropic team a reference implementation
to work from if they decide to build this natively.
I built a plugin that partially addresses this: claude-memory-manager
It adds a global memory layer at
~/.claude/memory/(cross-project, injected at session start via SessionStart hook) and a web UI for browsing/editing/moving memories across all containers. You can drag memories between projects, export as zip, etc.It doesn't solve the full layered system proposed here, but the global tier + visual management are usable today as a Claude Code plugin.
Real-world implementation: 3-layer classification with single-log + promotion
I've been running a similar layered memory system in production for enterprise Java development with 5+ concurrent Claude Code sessions (task management, multiple project workspaces, Slack monitoring). Sharing results that validate and extend the ideas in this issue.
The framework
Instead of the 3-tier index/topic/semantic approach above, I arrived at a 3-layer classification through iterative design:
Key design decisions:
log.mdwith tagged entries (【policy】【task】【insight】【reflection】【next】). Repeated patterns promote to stable files. This is the only organizing actionfilename.YYYY-MM-DD_HHMM.bakbefore structural changes. Agent simulation + live session verification afterResults
| Metric | Before | After | Delta |
|--------|--------|-------|-------|
| Startup context (lines) | 187 | 145 | -22% |
| Startup context (size) | 11.4KB | 7.7KB | -32% |
| Log write triggers | 0 | 5 types | New |
| Method file routing | 0 | 8 triggers | New |
| Session exit procedure | None | Backup + quality check | New |
New sessions achieve full role understanding and start working immediately without user guidance. Cross-project knowledge transfer works — patterns established in one session propagate to others.
Pain point:
~/.claude/projects/CLAUDE.md discoveryWhen testing a v2 trial environment in a new subdirectory, Claude searched the project root instead of
~/.claude/projects/{encoded-path}/and couldn't find the project CLAUDE.md. This blocked the trial and forced us back to the original directory. Related: #34941What I'd love to see built-in
Agreeing with the OP — the building blocks exist. The missing pieces from my experience:
log.mdthat Claude writes to automatically with tagged entries, surviving across sessions~/.claude/projects/CLAUDE.md should be found regardless of how Claude searches for itFull anonymized framework (before/after files + design doc) available if useful for the team.
We built a layered memory system for cross-session context:
Three layers:
recall()— session start, restore full context from previous sessionsremember(content)— mid-session, persist typed memories (episodic/semantic/procedural/emotional) with importance scoringcheckpoint()— session end, full state snapshot (auto via SessionEnd hook)Backed by Cloudflare Durable Objects + Supabase with pgvector for semantic search. Free. No account.
We've been running this across 100+ sessions in our own system. Agents with accumulated memory produce measurably different output.
https://github.com/archetypal-ai/archetypal-ai
Different approach here with alzheimer: instead of bypassing MEMORY.md with per-prompt search, it keeps the native index working by restructuring it into a self-balancing tree before it overflows.
When MEMORY.md exceeds 150 lines, entries are grouped by type into sub-indices in
_index/. If a category overflows, it splits further by topic keyword. The tree grows in depth, not width. Claude's built-in memory loading continues unchanged — no per-prompt hook overhead, no database, no embeddings.Trade-off vs. the retrieval-based approaches in this thread: no semantic search or importance scoring, but also zero dependencies (Python 3.6+ stdlib), zero per-query cost, and it works within the existing architecture rather than replacing it. Auto-recovers if Dream flattens the tree.
Hooks: PostToolUse, SessionStart, PreCompact. MIT-0 license. See #40614 for the feature request.
Built an MCP server that implements this: RecallNest
A cross-session memory and continuity system for Claude Code, designed around the exact problems discussed here.
What it does:
resume_contextpicks up exactly where the last session left off (decisions, open loops, next actions)search_memorywith keyword matching across all memory layerscheckpoint_sessioncaptures full session state before closingstore_workflow_patternsaves reusable multi-step proceduresUsed daily across 1000+ sessions. Claude Code hooks auto-trigger checkpoint on session end and resume on session start.
Repo: https://github.com/AliceLJY/recallnest
Update: New discovery — reading order is the single most impactful factor
Since my previous comment on the 3-layer classification framework, I've made a significant new discovery through continued production use across 5+ concurrent projects.
The single most impactful technique turned out to be specifying an explicit numbered reading order in MEMORY.md. Not what rules you write — but explicitly telling the model when to read them.
I ran a natural experiment: three projects with identical CLAUDE.md, model, and version. Two had numbered startup reading orders, one didn't. The two with reading orders showed consistent autonomous behavior. The one without required constant user intervention — and produced hallucinated PR descriptions. After adding a numbered reading order to the underperforming project, the very next session autonomously loaded rules, identified the correct resume point, and completed work without any user prompting.
I also systematically tracked version-specific anomalies and correlated them with release notes. The v2.1.88 fix for CLAUDE.md duplicate injection confirmed the causal chain behind quality degradation I'd been observing since v2.1.87.
I've written up the full case study — 10 techniques, evidence, and 5 feature requests — as a standalone issue:
👉 #41473 — Case Study: Governing Stateless Sessions with a Structured Memory Framework — Reading Order Is the Single Most Impactful Factor
The framework continues to work effectively in daily production use. Hope this is useful for both the team and the community.
The token reduction numbers here are genuinely impressive — 81% fewer always-loaded tokens is not a rounding error, that's a structural change in how the context window gets used.
The Layer 3 ChromaDB integration is the part I find most interesting. A few things I ran into building something similar:
The L3 failure mode at scale
Semantic search over the full corpus works great until you have 6+ months of entries. Then you start getting recall results that are topically relevant but temporally stale — Claude surfaces a decision from 4 months ago that's been superseded, doesn't know it's superseded, and acts on it. The fix I landed on is importance scoring with time decay: each memory gets a composite score (base importance × recency multiplier) that decays logarithmically. Stale memories don't get deleted, they just rank lower in recall until they drop below a pruning threshold and get consolidated.
The hook-based approach vs. ChromaDB
Your
memory_search.pyhook approach (grep index, load matching topic files) is the right architecture for L2. For L3 semantic search, I went with a different path — an MCP server that handles retrieval so Claude is callingrecall(query)directly rather than loading files into context. The difference is: with file loading, the retrieved content consumes context tokens permanently for the session. With MCP recall, Claude can query multiple times and only keep what it actually needs.On the 200-line MEMORY.md cap
The cap is actually the right constraint forcing the right behavior — if it's painful, it means you're putting the wrong things in L1. The heuristic I use: L1 should contain pointers and nothing else. If the content itself is in L1, it belongs in L2.
If the hook-based approach is useful to look at, I open-sourced what I built at github.com/mnemopay/mnemopay-sdk. The
npx @mnemopay/sdk setupcommand wires the Stop + UserPromptSubmit hooks automatically. The memory architecture is the part most directly relevant to what you're describing — the payment layer is a separate thing for multi-agent use cases.The reference implementation you linked (claude-memory) is a solid starting point. Your layered approach is where this ends up heading anyway; would be great to see it native.
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.