Module 3: CLAUDE.md and the memory system

Auto Memory and Settings: What Claude Learns and How You Configure It

Auto Memory and Settings: What Claude Learns and How You Configure It

Overview

So far we've covered explicit memory — what you write in CLAUDE.md. But Claude Code has another form of memory: auto memory, where Claude learns automatically from your corrections and preferences without you writing anything in a file.

On top of that, Claude Code has a settings system with 3 scopes (global, project, local) that controls permissions, allowed tools, and agent behavior. Settings aren't contextual memory like CLAUDE.md — they're technical configuration that defines what Claude Code can and can't do in your environment.

This capsule covers both systems: how auto memory works, where it's stored, how to manage it, and when it beats CLAUDE.md. Then the settings system: what to configure in each scope, how to do it, and the best practices for teams.


Auto memory: automatic learning

What auto memory is

Auto memory is Claude Code's ability to remember corrections and preferences across sessions without you writing them into CLAUDE.md. When you correct Claude, that correction can be saved as a "memory" that applies in future sessions.

How it works

Session 1:
You: "Create a Button component"
Claude: [creates it with a function declaration]

You: "Don't use function declarations. Use arrow functions
     with const. It's the project convention."

Claude: [corrects it, uses an arrow function]
Claude: [internally] → Saves: "This user prefers
        arrow functions with const for components"

═══════════════════════════════════════════════

Session 2 (days later):
You: "Create a Card component"

Claude: [uses an arrow function with const automatically]
→ Applies the learned correction without you repeating it

The kinds of things it learns

Auto memory captures correction patterns:

What you sayWhat Claude learns
"Don't use var, use const"Preference: const over var
"Imports go through the @/ alias"Convention: import aliases
"Always add a return type"Rule: explicit return types
"Tests go in tests/, not tests/"Structure: test location
"Answer in Spanish"Preference: Spanish language
"Don't generate obvious comments"Style: minimal comments

Version requirement

Auto memory requires Claude Code v2.1.59 or higher. Check with:

claude --version

If your version is older, update with claude update.

Turning auto memory on/off

Auto memory is on by default. To turn it off:

Option 1 — Interactive toggle:

> /memory

Then use the auto memory toggle in the interface.

Option 2 — Settings:

{
  "autoMemoryEnabled": false
}

Option 3 — Environment variable:

export CLAUDE_CODE_DISABLE_AUTO_MEMORY=1

Where auto memories are stored

Auto memories live in a local directory inside ~/.claude/, organized by project:

~/.claude/projects/<project>/memory/
├── MEMORY.md              → Concise index (the first 200 lines or 25KB, whichever comes first, load in every session)
├── debugging.md           → Detailed notes on debugging patterns
├── api-conventions.md     → API design decisions
└── ...                    → Other topic files Claude creates as it needs them

MEMORY.md acts as the directory's index. Claude reads and writes files in this directory during your session. The topic files load on demand, not at startup.

Auto memory is local to your machine. All worktrees and subdirectories inside the same git repository share one auto memory directory.

How to see your auto memories

Use the /memory command inside a session:

> /memory

/memory lists every CLAUDE.md and rules file loaded in your current session, lets you turn auto memory on and off, and opens a link to the auto memory folder. Select any file to open it in your editor.

When you ask Claude to "remember" something (e.g., "always use pnpm, not npm"), Claude saves it in auto memory. To add instructions to CLAUDE.md instead, ask for it explicitly: "add this to CLAUDE.md."

How to delete or edit auto memories

If Claude learned something incorrect or out of date:

# From the interactive interface
> /memory

# Claude lists the memories
# You can ask it to delete a specific one:
> "Delete the memory about using tabs instead of spaces.
   We changed that convention."

You can also edit the files in .claude/ directly if you prefer manual control.


When auto memory helps vs when CLAUDE.md is better

Auto memory is better when:

1. Personal preferences you discover while working:

You: "I don't like long answers. Be more concise."

→ Auto memory: perfect. It's a personal preference Claude
  learns and applies in future sessions.
→ CLAUDE.md: doesn't fit. It's your preference, not the project's.

2. Minor corrections that don't deserve a formal rule:

You: "I prefer if/else over ternaries for complex conditions"

→ Auto memory: captures the preference with no bureaucracy.
→ CLAUDE.md: too granular for a formal rule.

3. Habits that emerge with use:

After 5 corrections, Claude notices:
- This user always wants tests after implementing
- This user prefers async/await over .then()
- This user wants detailed logs in development

→ These patterns get internalized as auto memory.

CLAUDE.md is better when:

1. Project rules the whole team must follow:

"Don't use any in TypeScript" → CLAUDE.md
Because it applies to the whole team, not just you.
Auto memory is personal, CLAUDE.md is shared.

2. Factual information about the project:

"Stack: FastAPI, PostgreSQL, Alembic" → CLAUDE.md
Because it's a fact, not a preference.
Auto memory is for corrections, not for facts.

3. Conventions you need to document:

"Files in kebab-case, classes in PascalCase" → CLAUDE.md
Because you need it explicit and visible to the team.
Auto memory is implicit — nobody else sees it.

Comparison table

AspectAuto MemoryCLAUDE.md
Who creates itClaude, automaticallyYou, manually
VisibilityOnly youThe whole team (if committed)
PersistenceAcross sessionsPermanent
ScopePersonalProject or directory
MaintenanceAutomatic (but it can pile up)Manual (you update it)
PrecisionVariable (Claude interprets)Exact (you write it)
Ideal forPersonal preferencesProject rules

Best practices for auto memory

Let Claude learn naturally

Don't try to "program" auto memory. Just work with Claude Code, correct it when needed, and let the corrections accumulate.

# GOOD — a natural correction:
You: "Use const instead of let here, the variable isn't reassigned."

# BAD — forcing a "memory":
You: "Remember permanently: always use const over let
     when the variable isn't reassigned. Save it."

Review it periodically

Every few weeks, review the auto memories that have accumulated:

> /memory

Delete the ones that no longer apply (maybe you changed a convention) and check that there are no contradictions.

If something matters, put it in CLAUDE.md

If a correction you made really matters to the project, don't rely on auto memory alone — add it to CLAUDE.md:

You: "Never use console.log in production. Use the logger."

→ Good: Claude remembers it as auto memory.
→ Better: Add "Don't use console.log — use loguru" to CLAUDE.md
→ Best: Both. Auto memory for you, CLAUDE.md for the team.

Watch out for contradictory memories

If you said "use tabs" in January and "use spaces" in February, both corrections can coexist as auto memories. Claude has to resolve the contradiction — and it may pick wrong.

Fix: review and clean up memories whenever you change a convention.


The settings system

What settings are

Claude Code's settings are JSON files that configure the agent's technical behavior: which tools it can use, which permissions it has, and how it operates. They aren't context about your project — they're configuration for the agent itself.

The 3 scopes

┌────────────────────────────────────────────────┐
│  GLOBAL (~/.claude/settings.json)              │
│  Applies to ALL your projects                  │
│  Personal, not shared                          │
│                                                │
│  ┌────────────────────────────────────────┐    │
│  │  PROJECT (.claude/settings.json)       │    │
│  │  Applies only to THIS project          │    │
│  │  Committed to the repo (shared)        │    │
│  │                                        │    │
│  │  ┌────────────────────────────────┐    │    │
│  │  │  LOCAL                         │    │    │
│  │  │  (.claude/settings.local.json) │    │    │
│  │  │  Your personal overrides       │    │    │
│  │  │  NOT committed                 │    │    │
│  │  └────────────────────────────────┘    │    │
│  └────────────────────────────────────────┘    │
└────────────────────────────────────────────────┘

Precedence: Local > Project > Global

Scope 1: Global settings

Location

~/.claude/settings.json

What to configure here

Preferences that apply to all your projects, on all your machines:

{
  "permissions": {
    "allow": [
      "Read",
      "Glob",
      "Grep",
      "LS"
    ],
    "deny": []
  },
  "preferences": {
    "verbose": false,
    "theme": "dark"
  }
}

When to use global settings

  • Tools you always want to allow (read, grep, ls)
  • UI preferences that apply everywhere
  • Behavior defaults you like

What NOT to put here

  • Tools specific to one project (like a particular linter)
  • Permissions that only apply to certain projects
  • Configuration you don't want on all your machines

Scope 2: Project settings

Location

my-project/.claude/settings.json

What to configure here

Configuration shared with the team. It gets committed to git:

{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Glob",
      "Grep",
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(npx prisma migrate dev)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(npm publish)"
    ]
  }
}

allowedTools: pre-approving tools

The most important setting in project settings. It defines which commands Claude can run without asking you for confirmation:

{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(npm run format)",
      "Bash(npx prisma generate)"
    ]
  }
}

With this configuration, when Claude wants to run npm test, it just runs it without asking "Allow? [y/n]". This speeds up the workflow significantly.

denyTools: blocking dangerous tools

{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Bash(npm publish)",
      "Bash(docker rm)"
    ]
  }
}

These commands will never run, not even if Claude or you ask for them. It's a safety net.

When to use project settings

  • Tools the whole team should pre-approve (test, lint)
  • Tools nobody should be able to run by accident (rm -rf, force push)
  • Configuration that must be consistent across every developer on the team

Committing it to the repo

Project settings get committed so the whole team shares them:

git add .claude/settings.json
git commit -m "chore: configure claude code project settings"

Scope 3: Local settings

Location

my-project/.claude/settings.local.json

What to configure here

Your personal overrides, the ones you do NOT share with the team:

{
  "permissions": {
    "allow": [
      "Bash(docker compose up -d)",
      "Bash(psql)"
    ]
  },
  "preferences": {
    "verbose": true
  }
}

When to use local settings

  • Tools only you use (maybe you have Docker and your teammate doesn't)
  • Extra permissions for your local environment
  • Overrides for personal preferences

.gitignore

Local settings should not be committed:

echo ".claude/settings.local.json" >> .gitignore

Comparisons and decisions

Auto memory vs CLAUDE.md vs Settings

┌─────────────────────────────────────────────────────────┐
│                                                         │
│  Auto Memory       CLAUDE.md         Settings           │
│  ┌────────────┐    ┌────────────┐    ┌────────────┐     │
│  │ LEARNS     │    │ CONTEXT    │    │ CONFIGURES │     │
│  │            │    │            │    │            │     │
│  │ "Don't use │    │ "Stack:    │    │ "Allow     │     │
│  │  var"      │    │  FastAPI"  │    │  npm test" │     │
│  │            │    │            │    │            │     │
│  │ Implicit   │    │ Explicit   │    │ Technical  │     │
│  │ Personal   │    │ Project    │    │ Permissions│     │
│  └────────────┘    └────────────┘    └────────────┘     │
│                                                         │
│  "What             "What             "What              │
│   Claude            you define        Claude CAN        │
│   learns from       about the         or CANNOT         │
│   corrections"      project"          do"               │
│                                                         │
└─────────────────────────────────────────────────────────┘
AspectAuto MemoryCLAUDE.mdSettings
PurposeLearn preferencesProvide contextConfigure permissions
Who creates itClaudeYouYou
FormatInternalMarkdownJSON
SharedNo (personal)Yes (git)Depends on the scope
Kind of infoCorrections, preferencesStack, conventions, rulesTools, permissions
Example"Prefers const""Stack: FastAPI""allow: npm test"

When to use each one

You need to...Use
Have Claude remember a correctionAuto memory (it happens on its own)
Define the project rulesCLAUDE.md
Pre-approve toolsSettings (project)
Set your personal style preferenceAuto memory or CLAUDE.local.md
Block dangerous commandsSettings (project deny)
Share context with the teamCLAUDE.md + project settings
Keep your private configurationLocal settings + CLAUDE.local.md

Common patterns

Pattern 1: "Standardized team settings"

Every project on the team shares consistent settings:

{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Glob",
      "Grep",
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(npm run format)",
      "Bash(npm run build)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Bash(npm publish)"
    ]
  }
}

Pattern 2: "Auto memory + CLAUDE.md = intentional redundancy"

The corrections that matter go in both places:

Session: "Don't use console.log, use the logger"
→ Claude saves it as auto memory
→ You add it to CLAUDE.md

Redundant? Yes. Bad? No.
- Auto memory: covers you if you forget to update CLAUDE.md
- CLAUDE.md: covers the team, who don't have your auto memory

Pattern 3: "Local settings for development"

Your local settings add permissions for your development environment:

{
  "permissions": {
    "allow": [
      "Bash(docker compose up -d)",
      "Bash(docker compose down)",
      "Bash(psql -h localhost)",
      "Bash(redis-cli)"
    ]
  }
}

Your teammate who uses SQLite doesn't need Docker permissions.

Pattern 4: "Periodic auto memory cleanup"

Every month, review and clean up:

> /memory

Claude: "Current memories:
1. Prefers arrow functions for components
2. Uses tabs with 2 spaces [NOTE: you switched to 4 two weeks ago]
3. Don't use moment.js, use date-fns
4. Tests with the describe/it pattern
5. Answers in Spanish"

You: "Delete #2, I use 4 spaces now. And update #1:
     I don't use arrow functions for components anymore, I use function
     declarations (we changed the convention)."

Pattern 5: "Progressive settings"

Start permissive, restrict as you go:

// Week 1 — just the basics
{
  "permissions": {
    "allow": ["Read", "Write", "Glob", "Grep"]
  }
}

// Week 2 — you add safe commands
{
  "permissions": {
    "allow": [
      "Read", "Write", "Glob", "Grep",
      "Bash(npm test)",
      "Bash(npm run lint)"
    ]
  }
}

// Week 3 — you block the dangerous ones
{
  "permissions": {
    "allow": [...],
    "deny": ["Bash(rm -rf *)", "Bash(git push --force)"]
  }
}

Pitfalls and edge cases

Pitfall 1: Relying on auto memory alone for critical rules

# Dangerous:
You tell Claude "never modify the payments table directly"
Claude saves it as auto memory
Your teammate uses Claude Code → doesn't have that memory → breaks payments

# Fix: critical rules go in CLAUDE.md (shared)

Pitfall 2: Contradictory auto memories piling up

January: "Use Express for APIs"
March:   "Use Fastify instead of Express"
June:    "Let's try Hono"

→ Three auto memories about frameworks that contradict each other
→ Claude can get confused about which one to follow

Fix: clean up memories when you change stack or convention

Pitfall 3: Settings that are too permissive

{
  "permissions": {
    "allow": ["Bash(*)"]
  }
}

This lets Claude run ANY command without asking for confirmation. Including rm -rf /, git push --force, npm publish. Don't do this.

Pitfall 4: Forgetting the .gitignore for settings.local.json

# If you commit settings.local.json:
# - Your teammate inherits your personal permissions
# - It can cause conflicts if you have different environments
# - It defeats the whole point of "local"

# Fix:
echo ".claude/settings.local.json" >> .gitignore

Pitfall 5: Not using settings when you should

# If Claude asks you every single time:
"Claude wants to run: npm test. Allow? [y/n]"
"Claude wants to run: npm test. Allow? [y/n]"
"Claude wants to run: npm test. Allow? [y/n]"

# ...and you always say "y", you should add it to settings:
{
  "permissions": {
    "allow": ["Bash(npm test)"]
  }
}

# Now npm test runs without confirmation

Edge case: auto memory vs CLAUDE.md in conflict

CLAUDE.md: "Use tabs for indentation"
Auto memory: "This user prefers spaces"

Which one wins?

It depends on the implementation, but generally:
- CLAUDE.md carries more weight as an explicit source
- But if the correction was recent, it can prevail

Fix: when you spot a conflict, resolve one of them:
- Update CLAUDE.md so it matches your preference
- Or delete the auto memory that contradicts CLAUDE.md

Complete worked example

Scenario: configuring a full project with all 3 systems

A FastAPI project with a team of 3 developers. The complete setup:

1. CLAUDE.md (project context):

# Invoice API

Electronic invoicing API. Python 3.12, FastAPI, PostgreSQL.

## Stack
- FastAPI 0.109, SQLAlchemy 2.0, Alembic
- Pytest + httpx, Pydantic v2
- Redis for cache, Celery for background tasks

## Conventions
- snake_case, type hints required
- Schemas: InvoiceCreate, InvoiceUpdate, InvoiceResponse
- Don't use print(), use loguru

## Commands
- Dev: `uvicorn src.main:app --reload`
- Test: `pytest -v`
- Lint: `ruff check src/`
- Migrate: `alembic upgrade head`

## Rules
- Do NOT modify alembic/versions/ by hand
- Do NOT write raw SQL queries (use SQLAlchemy)
- Run the tests after changes in services/

2. Project settings (.claude/settings.json):

{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Glob",
      "Grep",
      "Bash(pytest -v)",
      "Bash(pytest)",
      "Bash(ruff check src/)",
      "Bash(ruff format src/)",
      "Bash(alembic upgrade head)",
      "Bash(uvicorn src.main:app --reload)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Bash(pip install * --break-system-packages)",
      "Bash(alembic downgrade *)"
    ]
  }
}

3. Local settings (.claude/settings.local.json) — just for you:

{
  "permissions": {
    "allow": [
      "Bash(docker compose up -d)",
      "Bash(docker compose down)",
      "Bash(pgcli)",
      "Bash(redis-cli)"
    ]
  }
}

4. Auto memory (accumulates with use):

After 2 weeks of use, Claude has learned:

  • You prefer to see verbose test output
  • You don't want long explanations, just the code
  • You always want the Pydantic schema before the endpoint
  • You prefer async generators over lists for large responses

Result: A complete system where:

  • CLAUDE.md provides the project context (shared)
  • Project settings define the team's permissions (shared)
  • Local settings add your tools (personal)
  • Auto memory refines the experience over time (personal)

Practice exercises

Exercise 1: Generate auto memories on purpose

Open Claude Code and work on a task. During the session, make 3 explicit corrections:

  1. Correct a naming convention
  2. Correct a style preference (e.g., "don't use complex ternaries")
  3. Correct an output preference (e.g., "be more concise")

In a later session, check whether Claude applies the corrections automatically.

What to watch for

If auto memory is working correctly:

  • Claude should apply all 3 corrections without you repeating them
  • You can confirm with /memory that they were saved
  • If one didn't get saved, the correction may not have been clear enough

Tip: the clearest, most direct corrections get saved best:

  • ✅ "Don't use var, always const or let"
  • ❌ "It might be better if you used something other than var"

Exercise 2: Configure project settings

Create .claude/settings.json in your project with:

  • 4-5 pre-approved commands (test, lint, build)
  • 2-3 blocked commands (rm -rf, force push)

Check that it works: ask Claude to run npm test and see whether it asks for confirmation.

Template
{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Glob",
      "Grep",
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(npm run build)",
      "Bash(npx prisma generate)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Bash(npm publish)"
    ]
  }
}

After creating the file:

  1. Start a new Claude Code session
  2. Ask: "Run the tests"
  3. If it doesn't ask for confirmation → the settings work
  4. If it asks for confirmation → check the file is at the right path

Exercise 3: Compare auto memory vs CLAUDE.md

  1. Start a new session
  2. Make a correction to Claude (e.g., "don't use semicolons")
  3. In the next session, check whether it remembers (auto memory)
  4. Add the same rule to CLAUDE.md
  5. Delete the auto memory
  6. Verify the rule still applies (now from CLAUDE.md)

This shows that both sources produce the same result, but CLAUDE.md is visible and shareable.

What to watch for
  • Auto memory: it works, but it's invisible to the team
  • CLAUDE.md: it works, and any developer can see it
  • Best practice: both, for the rules that matter
  • Auto memory alone: for minor personal preferences

If the rule matters to the project, CLAUDE.md is the right source. If it's a preference of yours, auto memory is enough.

Exercise 4: Configure all 3 settings scopes

Create the 3 settings files:

  1. Global (~/.claude/settings.json): Basic permissions you want in every project
  2. Project (.claude/settings.json): Permissions specific to this project
  3. Local (.claude/settings.local.json): Your personal overrides

Verify the local override works: add a permission in local that isn't in project.

Step-by-step guide
# 1. Global settings
mkdir -p ~/.claude
cat > ~/.claude/settings.json << 'EOF'
{
  "permissions": {
    "allow": ["Read", "Glob", "Grep"]
  }
}
EOF

# 2. Project settings
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)"
    ]
  }
}
EOF

# 3. Local settings
cat > .claude/settings.local.json << 'EOF'
{
  "permissions": {
    "allow": [
      "Bash(docker compose up -d)"
    ]
  }
}
EOF

# 4. Add local to .gitignore
echo ".claude/settings.local.json" >> .gitignore

Verify:

  • npm test runs without confirmation (project)
  • docker compose up runs without confirmation (local)
  • rm -rf something asks for confirmation (it's not in allow)

Exercise 5: Auto memory cleanup

Review your current auto memories and clean house:

> /memory
  1. Identify memories that no longer apply
  2. Identify memories that contradict CLAUDE.md
  3. Delete the obsolete ones
  4. Migrate the important ones to CLAUDE.md if they aren't there yet
Cleanup checklist

For each auto memory, ask yourself:

  • Is it still valid? (Did you change the convention?)
  • Does it contradict something in CLAUDE.md? (If so, one of the two has to change)
  • Is it important enough that it should live in CLAUDE.md? (If so, add it)
  • Is it a minor personal preference? (If so, leave it as auto memory)
  • Does it only apply to an old project? (If so, delete it)

Summary

  • Auto memory is implicit learning: Claude remembers corrections and preferences across sessions.
  • It kicks in naturally when you correct Claude. You don't have to force it.
  • Auto memory is personal, it isn't shared. For team rules, use CLAUDE.md.
  • Review it periodically with /memory and clean out obsolete or contradictory memories.
  • Settings have 3 scopes: Global (all your machines), Project (shared with the team), Local (just you).
  • allowedTools pre-approves commands to speed up the workflow.
  • denyTools blocks dangerous commands as a safety net.
  • Project settings get committed to git. Local settings go in .gitignore.
  • Auto memory + CLAUDE.md + Settings = a complete memory and configuration system.
  • When something matters: put it in CLAUDE.md (explicit, shared). When it's personal: let auto memory handle it, or use CLAUDE.local.md.

Next capsule: 05 - CLAUDE.local.md — personal preferences you don't share with the team.


Additional resources

  1. Claude Code Memory — Anthropic Docs — Official documentation on auto memory and CLAUDE.md
  2. Claude Code Settings — Scope configuration: global, project, local
  3. Claude Code Best Practices — Recommendations for managing auto memory and permissions
  4. Claude Code CLI Reference — Commands for managing memory and settings
  5. Claude Code Interactive Mode — Permission management in interactive sessions
  6. Claude Code Overview — Claude Code's general architecture