Building Agent Memory Systems
Implement the three-layer memory architecture:
1. Observation → /diary command → writes raw session notes to disk
2. Reflection → /reflect command → analyzes N diary entries for patterns
3. Retrieval → CLAUDE.md → read into every future session
Minimal file layout:
commands/
diary.md # instructs Claude how to write a diary entry
reflect.md # instructs Claude how to analyze entries + update CLAUDE.md
hooks/
pre-compact.sh # auto-triggers diary before context is lost
~/.claude/memory/diary/YYYY-MM-DD-session-N.md # observation store
CLAUDE.md # synthesized long-term memory
processed.log # tracks which diary entries have been reflected on
Progress:
- Define the observation format (what a diary entry captures)
- Define the trigger points (manual command + automatic hook)
- Define the reflection algorithm (pattern threshold, categories, dedup)
- Define the retrieval mechanism (how memory re-enters context)
- Wire up storage location and idempotency (processed.log)
- Test end-to-end: session → diary → reflect → CLAUDE.md update → next session
1. Design the observation (diary) layer
- Trigger context-first: reflect on what's already in the conversation, not just logs. Fall back to parsing transcripts (e.g., JSONL) only when needed.
- Fixed sections force completeness: task summary, work done, design decisions, user preferences, review feedback, challenges/solutions, code patterns.
- Instruct the model explicitly to be factual and specific, capture the "why" behind decisions, document all stated preferences (commits, PRs, linting, testing), and include failures — failures are the highest-signal learning material.
- Name files deterministically:
YYYY-MM-DD-session-N.mdso ordering and dedup are trivial.
2. Design triggers
- Automatic: hook into a lifecycle event that already signals "context about to be lost" (e.g., PreCompact for long sessions). This guarantees no silent loss of learnings.
- Manual: always allow an explicit command for the user to force capture at any time.
- Prefer hooking into an existing lifecycle event over inventing a new polling mechanism.
3. Design the reflection layer
- Reflection must be incremental: check a processed-log to skip entries already analyzed. Never re-derive the same pattern twice unless explicitly asked to reprocess.
- Use an explicit occurrence threshold for what counts as a pattern vs. a strong pattern (e.g., 2+ = pattern, 3+ = strong pattern). This prevents overfitting to a single anecdote.
- Prioritize scanning for violations of existing rules above discovering new rules — reinforcing broken rules is higher value than adding new ones.
- Synthesize across fixed categories so nothing is missed: external feedback (PR review), stated preferences, design decisions that worked, anti-patterns to avoid, efficiency lessons, project-specific patterns.
- Support filters on the reflection command (date range, project path, keyword, count) so users can scope analysis instead of always running full-corpus reflection.
- Support explicit reprocess/include-all override for re-analysis, separate from the default incremental behavior.
4. Design the retrieval layer
- Output of reflection must be written directly into the memory file that's auto-loaded every session (e.g., CLAUDE.md) — retrieval should require zero user action.
- Enforce a strict output format for new rules: one-line bullets, imperative tone, no verbose explanation. Long-form reasoning belongs in diary entries, not in the always-loaded memory file.
Example 1:
Input: User corrected Claude 3 times across different sessions to "run targeted tests before full suite."
Output: Reflection detects 3+ occurrences → strong pattern → adds to CLAUDE.md:
- Run targeted tests for changed files before running the full test suite.
Example 2:
Input: /reflect last 15 entries for project /Users/username/Code/my-app related to React
Output: Reflection scopes analysis to the 15 most recent unprocessed entries, filters to the given project path, and further filters content mentions of "React" before synthesizing patterns.
Example 3: Input: A diary entry notes a design decision was reverted because it violated an existing CLAUDE.md rule. Output: Reflection flags this as a rule violation (highest priority category) and strengthens/rephrases the existing rule rather than adding a new one.
- Treat diary entries as immutable raw logs; treat CLAUDE.md as the only mutable, synthesized artifact.
- Keep the observation schema fixed/structured — free-form notes are hard to reflect over at scale.
- Make reflection idempotent via a processed-log; this is what makes periodic re-running safe and cheap.
- Bias toward capturing failures and corrections, not just successes — corrections are the densest source of preference signal.
- Keep memory updates terse; verbosity in CLAUDE.md degrades every future session's context budget.
- Design filters (date/project/keyword) into the reflection command from the start — memory systems get noisy fast across multiple projects.
- Don't let reflection re-scan already-processed entries by default — wastes tokens and risks duplicate/conflicting rules.
- Don't write essay-length rationale into CLAUDE.md — that belongs in diary entries, not the retrieval layer.
- Don't treat a single occurrence as a pattern — require repetition (2+/3+) before promoting an observation to a rule.
- Don't rely solely on manual triggers for diary creation — long sessions will lose context before the user remembers to run the command; pair with an automatic hook.
- Don't mix project-specific conventions into a global memory file without tagging — causes rule leakage across unrelated projects (a known limitation to design around).