In Part 1, you learned that AI Agents operate through an Agentic Loop: LLM thinks → calls tools → receives results → repeats. But what does the LLM think based on? The answer: Context — all the information the agent has within a session.
Context determines whether Claude is smart or clueless, whether it follows your conventions or makes things up. This Part 2 explains the context mechanism under the hood, how you control it via CLAUDE.md and Memory, every command you need to know, and when to use which mode.
Context Window — Short-Term Memory
How It Works Internally
Every time the LLM is invoked within the Agentic Loop, it receives the entire conversation as input:
graph TD
subgraph CW["Context Window (~200K tokens)"]
direction TB
A["System Prompt<br/><i>Claude Code's internal instructions</i>"]
B["CLAUDE.md Contents<br/><i>Project instructions - all levels</i>"]
C["Memory Files<br/><i>Learnings from previous sessions</i>"]
D["Rules Files<br/><i>.claude/rules/*.md</i>"]
E["Tool Definitions<br/><i>JSON schema for each tool</i>"]
F["Message 1: User → Your first prompt"]
G["Message 2: Assistant → Response + tool calls"]
H["Message 3: Tool result → Results"]
I["..."]
J["Message N: User → Your latest prompt"]
end
A --> B --> C --> D --> E --> F --> G --> H --> I --> J
style A fill:#4a90d9,color:#fff
style B fill:#50b356,color:#fff
style C fill:#50b356,color:#fff
style D fill:#50b356,color:#fff
style E fill:#4a90d9,color:#fff
style F fill:#f5a623,color:#fff
style G fill:#f5a623,color:#fff
style H fill:#f5a623,color:#fff
style I fill:#f5a623,color:#fff
style J fill:#f5a623,color:#fff
All of this combined = the context window. Claude Code has a context window of roughly 200K tokens (approximately ~150K words or ~500 pages of text).
Compaction — When Context Gets Full
As the conversation grows longer, the context approaches its limit. Claude Code automatically compacts — summarizing old messages to free up space:
graph LR
subgraph BEFORE["Before Compaction"]
direction TB
B1["System + CLAUDE.md +<br/>Memory + Rules + Tool defs"]
B2["100 detailed messages:<br/>reading files, editing,<br/>building, discussing..."]
B3["New prompt"]
B1 ~~~ B2 ~~~ B3
end
BEFORE -- "Compact" --> AFTER
subgraph AFTER["After Compaction"]
direction TB
A1["System + CLAUDE.md +<br/>Memory + Rules + Tool defs<br/><b>RELOADED intact</b>"]
A2["1 summary message:<br/><i>Implemented cancel order<br/>endpoint, fixed build error...</i>"]
A3["New prompt"]
A1 ~~~ A2 ~~~ A3
end
style B1 fill:#50b356,color:#fff
style B2 fill:#e74c3c,color:#fff
style B3 fill:#f5a623,color:#fff
style A1 fill:#50b356,color:#fff
style A2 fill:#f5a623,color:#fff
style A3 fill:#f5a623,color:#fff
The key point: CLAUDE.md, Memory, and Rules are always reloaded intact after compaction. Conversation history gets summarized (details are lost). This is why you must put important information in CLAUDE.md, not just mention it in chat.
graph TD
subgraph SURVIVE["Survives Intact"]
S1["CLAUDE.md<br/><i>all levels</i>"]
S2["Memory files<br/><i>~/.claude/projects/.../memory/</i>"]
S3[".claude/rules/*.md"]
end
subgraph SUMMARIZED["Summarized - Details Lost"]
M1["Contents of files read"]
M2["Terminal output"]
M3["Previous discussions"]
end
subgraph LOST["Lost Entirely"]
L1["Nuance in your wording"]
L2["Small details not captured<br/>in the summary"]
end
style SURVIVE fill:#d4edda,stroke:#28a745
style SUMMARIZED fill:#fff3cd,stroke:#ffc107
style LOST fill:#f8d7da,stroke:#dc3545
style S1 fill:#28a745,color:#fff
style S2 fill:#28a745,color:#fff
style S3 fill:#28a745,color:#fff
style M1 fill:#ffc107,color:#000
style M2 fill:#ffc107,color:#000
style M3 fill:#ffc107,color:#000
style L1 fill:#dc3545,color:#fff
style L2 fill:#dc3545,color:#fff
TIPWhen a session runs long and Claude starts “forgetting” instructions, type
/compactto trigger manual compaction. Claude will summarize the history and reload CLAUDE.md — like a memory “refresh”.
Context Loading — Load Order at Session Start
flowchart TD
A["Session starts"] --> B["Load System Prompt"]
B --> C["Load User CLAUDE.md\n~/.claude/CLAUDE.md"]
C --> D["Load Project CLAUDE.md\n./CLAUDE.md"]
D --> E["Load Folder CLAUDE.md\nsubdirectory (if exists)"]
E --> F["Load Memory Files"]
F --> G["Load Tool Definitions"]
G --> H["✅ Ready for prompt"]
CLAUDE.md — Project DNA
CLAUDE.md is the single most important file when working with Claude Code. It serves as system instructions for your project, read at the start of every session and after every compaction.
Three Levels of CLAUDE.md
graph TD
A["User-level<br/><b>~/.claude/CLAUDE.md</b><br/><i>All projects, only you</i><br/>Not committed to git"]
B["Project-level<br/><b><repo>/CLAUDE.md</b><br/><i>This project, whole team</i><br/>Committed to git"]
C["Folder-level<br/><b><repo>/src/Api/CLAUDE.md</b><br/><i>Specific folder</i><br/>Committed to git"]
A -->|"Personal preferences"| MERGED["Claude reads ALL<br/>at once"]
B -->|"Team conventions"| MERGED
C -->|"Specific rules"| MERGED
style A fill:#9b59b6,color:#fff
style B fill:#3498db,color:#fff
style C fill:#1abc9c,color:#fff
style MERGED fill:#f39c12,color:#fff
| Level | File | Scope | Commit to git? |
|---|---|---|---|
| User | ~/.claude/CLAUDE.md | All projects, only you | No |
| Project | <repo>/CLAUDE.md | This project, whole team | Yes |
| Folder | <repo>/src/Api/CLAUDE.md | Specific folder | Yes |
User-Level CLAUDE.md
Things you want Claude to always do, regardless of the project:
# ~/.claude/CLAUDE.md
## Preferences
- Communicate in Vietnamese
- Never commit automatically, always ask first
- When creating PRs, write descriptions in English
- Prefer dotnet CLI over Visual Studio
## My Context
- Senior .NET developer, 5 years of experience
- Team of 8, using GitHub Actions + AKS
- Currently migrating from monolith to microservices
Project-Level CLAUDE.md
Things the entire team needs Claude to know. Committed to git so everyone shares the same context:
# CLAUDE.md — OrderService
## Project Overview
.NET 8 microservice handling order lifecycle.
MassTransit for messaging, EF Core for data access.
## Commands
| Command | Purpose |
|---------|---------|
| `dotnet build src/OrderService` | Build |
| `dotnet test tests/OrderService.Tests` | Run tests |
| `dotnet ef migrations add <Name> --project src/OrderService.Data` | New migration |
| `docker compose up -d` | Start local infra (RabbitMQ, SQL Server) |
## Architecture
- CQRS via MediatR: Commands/Queries in Features/
- MassTransit State Machines for order flow (StateMachines/)
- EF Core Code-First, DbContext in Data/
- Result<T> pattern, no throwing exceptions for business logic
## Code Conventions
- Feature folder structure: Features/{Name}/Command.cs, Handler.cs, Validator.cs
- record types for DTOs, Commands, Queries, Events
- PascalCase methods, _camelCase private fields
- FluentValidation for request validation
- xUnit + FluentAssertions for testing
## Important Patterns
- Order states: Submitted → Accepted → Processing → Completed/Cancelled
- Saga pattern: OrderStateMachine handles multi-step workflows
- Outbox pattern: reliable messaging via MassTransit Outbox
- Idempotency: every consumer must handle duplicate messages
Folder-Level CLAUDE.md
When Claude is working inside the StateMachines/ folder, it automatically picks up the CLAUDE.md there:
# src/OrderService/StateMachines/CLAUDE.md
## MassTransit State Machine Rules
- Inherit from MassTransitStateMachine<TState>
- States: public State property
- Events: public Event<T> property
- Transitions: During(state, When(event).Then(...).TransitionTo(next))
## IMPORTANT
- DO NOT use async void in activities
- State instance MUST have a CorrelationId (Guid)
- Always handle Faulted state
- Test with InMemoryTestHarness
WARNINGThe more specific your CLAUDE.md is, the more accurately Claude follows your conventions from the very first attempt. Don’t write vague things like “write clean code”. Be specific: “use record types for DTOs”, “Result
instead of throwing exceptions”.
Memory — Remembering Across Sessions
The Problem
You tell Claude: “This project uses SQL Server, not PostgreSQL.” Claude understands and writes correct code. The next day you start a new session — Claude has forgotten everything.
Memory solves this by persisting information to files on disk.
How It Works
Memory files live at ~/.claude/projects/<project-hash>/memory/. Each file has frontmatter:
---
name: database-config
description: Database configuration per service
type: project
---
OrderService uses SQL Server (Azure SQL).
PaymentService uses PostgreSQL.
NotificationService uses MongoDB.
Connection strings in appsettings.{Environment}.json.
Claude reads all memory files at the start of every session, just like CLAUDE.md.
Saving Memory
Method 1: Tell Claude directly
"Remember that OrderService uses SQL Server, PaymentService uses PostgreSQL"
Claude creates a memory file automatically.
Method 2: Claude figures it out (Auto Memory) When you confirm a pattern multiple times, Claude may save it on its own. For example: you keep correcting Claude when it uses var → Claude saves a memory note “team uses explicit types, no var”.
Method 3: Forgetting
"Forget the rule about using var — the team has switched back to allowing var"
Memory vs CLAUDE.md — When to Use Which?
| CLAUDE.md | Memory | |
|---|---|---|
| Scope | Whole team (committed to git) | Only you (personal) |
| Content | Conventions, architecture, commands | Patterns you’ve discovered, personal decisions |
| Example | ”Use FluentValidation" | "User XuanPD prefers explicit types” |
| Management | Edit the file manually | Tell Claude to remember/forget |
.claude/rules/ — Topic-Based Rules
Rules work like CLAUDE.md but are organized by topic:
.claude/rules/
ef-core.md → "AsNoTracking for read-only queries, explicit Include()"
masstransit.md → "Consumers must be idempotent, always handle Faulted"
react.md → "TanStack Query for server state, Zustand for client"
testing.md → "xUnit + FluentAssertions, Arrange-Act-Assert"
git.md → "Conventional commits: feat(order): description"
Commands — Controlling Claude Code
Essential Commands
| Command | Function | When to Use |
|---|---|---|
/help | View all commands | When you forget a command |
/model | Switch model (Opus/Sonnet/Haiku) | When you need a different model |
/fast | Toggle fast mode | Simple tasks, need speed |
/compact | Compress conversation history | Long session, Claude “forgets” context |
/clear | Wipe all context, start fresh | Switching to a completely different task |
/plan | Enter Plan Mode | Complex tasks, need to design first |
/cost | View token cost of current session | Tracking expenses |
/effort | Adjust thinking level | Tasks needing deeper/faster reasoning |
Advanced Commands
| Command | Function | Example |
|---|---|---|
/init | Auto-generate CLAUDE.md for project | First-time setup |
/add-dir | Add another directory to context | /add-dir ../shared-lib when you need a reference |
/agents | Manage SubAgents | View, create, edit custom agents |
/mcp | Manage MCP servers | View, add MCP servers |
/resume | Resume a previous session | Continue unfinished work |
/config | Configure settings | Memory, thinking mode, theme |
/rename | Name the session | /rename order-cancel-feature |
Special Characters in Prompts
@ — Reference a file/directory:
"Explain @src/OrderService/OrderStateMachine.cs"
Claude automatically reads the file — no need to say “read file X first”.
# — Reference a file (older syntax):
"Refactor OrdersController following the pattern in #ProductsController.cs"
Both syntaxes automatically include the CLAUDE.md from the referenced file’s directory.
Modes — Different Ways of Thinking
Normal Mode (default)
Claude receives the prompt, thinks, and acts immediately. Full tool access.
Use for: bug fixes, feature implementation, small refactors, Q&A.
Plan Mode
Problem it solves: Complex tasks where Claude acts immediately → goes in the wrong direction → has to undo → wastes time and tokens.
How it works internally: Claude is only allowed to call read-only tools (Read, Glob, Grep). No Edit, Write, or Bash. It analyzes the codebase, lists files that need changes, and proposes an execution order.
/plan Add a caching layer for OrderService queries
Claude (Plan Mode):
1. [Read] Read OrdersController → sees IMediator injection
2. [Grep] Find query handlers → 5 handlers, all query DB directly
3. [Read] Read appsettings.json → no Redis config yet
4. [Glob] Find existing cache patterns → none found
Proposed plan:
Step 1: Add Redis package + configuration
Step 2: Create ICacheService interface + RedisCacheService
Step 3: Create CachingBehavior (MediatR pipeline behavior)
Step 4: Decorate query handlers that need caching
Step 5: Add cache invalidation in command handlers
Step 6: Update docker-compose.yml to add Redis container
Estimated files: 8 new files, 6 modified files
Risks:
- Cache invalidation for OrderStateMachine state changes
- Race condition when multiple instances invalidate simultaneously
You review the plan, provide feedback (“add distributed lock for invalidation”), then approve → Claude switches to Normal Mode and implements.
Enter Plan Mode: /plan or Shift+Tab (cycle through modes) or --permission-mode plan at startup.
TIP
/planaccepts a direct argument:/plan migrate from Dapper to EF Core. No need to enter plan mode first and then type.
Fast Mode
Mechanism: Uses Sonnet model (faster, cheaper) instead of Opus. Significantly faster output, suitable for simple tasks. Toggle with /fast.
Use for: scaffolding boilerplate, fixing typos, adding imports, generating DTOs, writing docs.
Do NOT use for: debugging race conditions, state machine logic, architectural decisions.
Auto-Accept Mode
How it works: Claude auto-approves all file edits without asking you. Still asks for destructive Bash commands.
Use for: when you trust Claude with the current task and want a faster workflow.
Enter: Shift+Tab from Normal Mode.
Headless Mode — Claude in CI/CD
How it works: Claude runs non-interactively, receives prompts via the -p flag, and returns results via stdout.
# Code review in GitHub Actions
claude -p "Review diff of this PR. Check for:
1. N+1 queries in EF Core
2. Missing null checks
3. Idempotency in MassTransit consumers
Output as PR comment markdown" \
--output-format json \
--allowedTools "Read,Grep,Glob,Bash(dotnet build*)"
Application for .NET/React teams:
# .github/workflows/ai-review.yml
name: AI Code Review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Claude Review
run: |
claude -p "Review the PR diff. Focus on:
- EF Core query performance
- MassTransit consumer idempotency
- React useEffect dependency arrays
- Missing FluentValidation rules
Format as markdown checklist." \
--allowedTools "Read,Grep,Glob"
WARNINGHeadless mode requires
--allowedToolsto restrict permissions. Never allow Edit/Write in a CI/CD pipeline unless you have an approval workflow in place.
Which Mode to Choose?
flowchart TD
Start["Which mode?"] --> Q1{"CI/CD pipeline?"}
Q1 -->|"Yes"| Headless["🖥️ Headless Mode\nclaude --headless"]
Q1 -->|"No"| Q2{"Complex task\n>5 files?"}
Q2 -->|"Yes"| Plan["📋 Plan Mode\nShift+Tab"]
Q2 -->|"No"| Q3{"Simple task\ntypo, import?"}
Q3 -->|"Yes"| Fast["⚡ Fast Mode\n/fast"]
Q3 -->|"No"| Q4{"Fully trust\nClaude?"}
Q4 -->|"Yes"| Auto["🟢 Auto-Accept\nShift+A"]
Q4 -->|"No"| Normal["👤 Normal Mode"]
Hands-on: Setting Up Context for Your Project
Step 1: Create CLAUDE.md
# Option A: Let Claude auto-generate it
claude /init
# Option B: Create manually (recommended for teams)
# Create a CLAUDE.md file at the project root using the template from the CLAUDE.md section above
Step 2: Create Folder-Level CLAUDE.md
For the most complex parts of your project:
# EF Core data layer
cat > src/OrderService/Data/CLAUDE.md << 'EOF'
## EF Core Rules
- AsNoTracking for read-only queries
- Explicit Include(), no lazy loading
- Separate migrations project
- Always check generated SQL (dotnet ef migrations script)
EOF
# React frontend
cat > frontend/src/CLAUDE.md << 'EOF'
## React Rules
- Function components only
- TanStack Query for server state
- Zustand for client state
- Tailwind CSS utility classes
EOF
Step 3: Set Up Rules
mkdir -p .claude/rules
cat > .claude/rules/git.md << 'EOF'
## Git Conventions
- Conventional commits: feat(scope): description
- Scope = service name: feat(order): add cancel endpoint
- PR title < 70 chars
- PR description in English, include testing checklist
EOF
Step 4: Test It
claude "Add a GET /api/orders/active endpoint.
Only return orders with status Submitted or Accepted.
Include pagination. Verify the build."
Claude will read CLAUDE.md → understand the CQRS + Result
Part 2 Summary
| Concept | How It Works Internally | How to Optimize |
|---|---|---|
| Context Window | ~200K tokens, holds all info in a session | Put important things in CLAUDE.md (survives compaction) |
| Compaction | Summarizes old messages, reloads CLAUDE.md/Memory | /compact when Claude “forgets” |
| CLAUDE.md | 3 levels: User > Project > Folder, read every session | Be specific, keep updated, commit to git |
| Memory | Files on disk, persists across sessions | Tell Claude to remember important patterns |
| Rules | Like CLAUDE.md, organized by topic | Separate rules for EF Core, React, testing, git |
| Plan Mode | Read-only tools, analyze before coding | /plan for tasks touching >5 files |
| Headless Mode | Non-interactive, used in CI/CD | --allowedTools to restrict permissions |
Part 3 will dive into Tools, Hooks & Quality Gates — how the agent takes action and how you automate quality control.
Claude Code Mastery Series — P3: Tools, Hooks & Quality Gates up next.
- 1Claude Code Mastery (P1): What Is an AI Agent? From Traditional SDLC to ADLC
- 2Claude Code Mastery (P2): Context, Commands & Modes — How to Teach the Agent About Your Project and Control It
- 3Claude Code Mastery (P3): Tools, Hooks & Quality Gates — Automated Quality Control
- 4Claude Code Mastery (P4): MCP Servers, GitHub Integration & Stacked PRs
- 5Claude Code Mastery (P5): Skills, SubAgents & Agent Teams — Advanced Workflow for Teams
Subscribe to Newsletter
Get notified when I publish new posts. No spam, unsubscribe anytime.