The self-improving codebase pattern transforms every correction into permanent institutional knowledge. Instead of repeating the same instructions every session, you build a system where Claude learns from mistakes automatically.
Claude makes mistake
↓
Human corrects it
↓
Claude writes it to lessons.md [x1]
↓
Mistake happens again → [x2]
↓
Third occurrence → [x3] → PROMOTE to rules file
↓
Permanent rule enforced forever
The compounding effect: after 3 months, your rules files contain dozens of hard-won lessons that prevent dozens of recurring mistakes — across every session, without you needing to remember any of them.
Create .claude/rules/lessons.md:
# Lessons — Shared Correction Log
## Purpose
This file records mistakes Claude made that a developer corrected.
Encoded as concise rules to prevent recurrence.
## Hard Cap: 15 entries maximum
15 entries × 4 lines = ~60 lines. Negligible token cost. Forces curation.
## Entry Format (4 lines max — no exceptions)
---
## [YYYY-MM-DD] [Category] [x1]
**Mistake:** [one sentence — exactly what went wrong]
**Rule:** [one sentence — what to do instead, always]
**Applies:** [all tasks | specific tech | specific pattern]
---
## Categories
- Scope — touching code outside the request
- Verification — claiming something without reading it
- Code Style — formatting, comments, naming
- Communication — how answers are structured
- Architecture — structural decisions
- Tech:[stack] — tech-specific errors## [2026-04-01] Tech:SQLAlchemy [x1]
**Mistake:** Used sync SQLAlchemy session in an async FastAPI route handler.
**Rule:** Always use AsyncSession with `await session.execute()` in FastAPI routes.
**Applies:** All FastAPI database route handlers
## [2026-03-28] Verification [x2]
**Mistake:** Claimed a feature was implemented without reading the actual code first.
**Rule:** Always grep/read before claiming something is or isn't implemented.
**Applies:** All feature verification tasks
## [2026-03-15] Scope [x1]
**Mistake:** Refactored adjacent utility functions while fixing a single bug.
**Rule:** Fix only what was asked. Separate PR for adjacent cleanup — always.
**Applies:** All bug fix tasksWhen an entry reaches [x3], it graduates from "reminder" to "permanent rule":
Step 1: Identify the matching rules file
Scopemistakes →core-behaviors.md §5Tech:Pythonmistakes →code-standards.mdPython sectionArchitecturemistakes →propertyharbor-constraints.mdVerificationmistakes →verification-and-reporting.md
Step 2: Add the rule in the appropriate section
Old lessons.md entry ([x3]):
## [2026-03-15] Tech:SQLAlchemy [x3]
**Mistake:** Used sync SQLAlchemy in async routes.
**Rule:** Use AsyncSession with await in all FastAPI routes.
**Applies:** All FastAPI database route handlersNew entry in code-standards.md:
### SQLAlchemy in FastAPI (enforced rule)
Always use `AsyncSession` with `await session.execute()` in FastAPI route handlers.
Never use sync `Session` in async contexts — this causes blocking I/O in the event loop.
```python
# Required pattern
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
return result.scalar_one_or_none()Step 3: Delete the entry from lessons.md
The mistake is now permanently encoded in the rules file. Every future session loads it automatically.
For the loop to work automatically, you need this in CLAUDE.md:
## Self-Improvement Loop
When the user corrects a mistake during any session:
1. BEFORE proceeding with the corrected approach — write the lesson
2. Open `.claude/rules/lessons.md`
3. Check if this exact mistake already has an entry — if yes, increment [xN]
4. If no existing entry — add a new one in the 4-line format
5. If the entry is now [x3] — promote Rule to the matching rules file, delete entry from lessons.md
6. THEN continue with the task
Correction signals that trigger this:
- User says "that's wrong", "not like that", "you missed X"
- User re-states something already said earlier in the session
- User explicitly points out a repeated mistake
- User overrides a decision Claude made independently
Do NOT write a lesson for:
- Preference changes mid-task (user changed mind, not a mistake)
- Clarifications never stated before
- Requests to try a different approach when first approach was reasonableAfter months of this loop, you accumulate a layered rules system:
.claude/rules/
├── core-behaviors.md # How to approach work (task structure, scope, verification)
├── first-principles.md # Hard constraints with metrics (numeric gates)
├── code-standards.md # Code quality requirements (error handling, DRY, logging)
├── verification-and-reporting.md # How to verify and report status
├── leverage-patterns.md # Process patterns (SDD, parallel agents, adaptive depth)
├── lessons.md # Correction log (max 15 entries, escalation at [x3])
└── propertyharbor-constraints.md # Project-specific constraints
Each file has a clear domain. When a lesson promotes, it goes to the right file. No rules live in two places.
core-behaviors > first-principles > code-standards > verification-and-reporting > leverage-patterns
When rules appear to conflict, the higher-precedence file wins. This is documented in CLAUDE.md so Claude can apply it correctly.
For personal preferences that aren't project rules, use the memory system:
lessons.md → project rules (applies to all developers)
memory/feedback_*.md → personal preferences (applies to you specifically)
Example feedback memory:
---
name: Response Style Preference
type: feedback
description: How to format responses for this user
---
Always lead with the answer, not the reasoning.
No filler words, no preamble, no "certainly!".
Use bullet points for multi-step processes.
Code blocks before explanations, not after.
**Why:** User has confirmed this saves time — they read code first.
**How to apply:** All responses in this project.Complement lessons.md with an append-only session log for decisions that don't repeat but matter for continuity:
# blackbox/session-log.md
## 2026-04-06T14:30:00Z
### Decisions
- Chose Redis over Postgres for rate limiter — Postgres had lock contention in load test
- Deferred OAuth2 to next sprint — out of scope per PM
### Files Modified
- src/auth/rate_limiter.py — new Redis-based rate limiter
- tests/auth/test_rate_limiter.py — 6 new tests
### Deferred
- OAuth2 implementation → next sprint issue #89
---Session log = what happened this session. Lessons = what to do differently next time.
These are complementary, not redundant.
When a multi-step plan in docs/plans/ is modified mid-execution — steps split, inserted, skipped, reordered, or abandoned — record it in two places:
- A
## Mutations Logtable in the plan file itself:
## Mutations Log
| Session | Task N | Type | Reason |
|---------|--------|------|--------|
| 2026-04-07 | Task 3 | Split | Step too large — split into 3a and 3b |
| 2026-04-07 | Task 5 | Skipped | Out of scope per PM |- An entry in the session log under
### Decisions:
Plan mutation: [type] on Task N of docs/plans/YYYY-MM-DD-<feature>.md — [reason]
This makes plan history auditable across sessions without loading the full plan into context.
Convention: folder name = diagram prefix. src/auth/ maps to docs/diagrams/auth-flow.md.
Rules:
- If files modified this session are covered by an existing diagram in
docs/diagrams/→ update that diagram before ending the session - If a new service, flow, or integration was built and no diagram exists yet → generate a Mermaid diagram and save to
docs/diagrams/[feature-name]-flow.md - Note in session log
### Decisions:"Updated docs/diagrams/[name].md — [reason]"or"Created docs/diagrams/[name].md"
Rather than waiting for an explicit "write the log" command, the session log can be written automatically when the user sends exit phrases. This is implemented either as a hook rule (UserPromptSubmit matching exit phrases) or as CLAUDE.md instructions + Stop hook.
Trigger phrases:
"that's all for now"
"done for today"
"heading out"
"talk later"
"closing the window"
"wrapping up"
"end of session"
"bye" / "goodbye" (as session closer)
On exit signal detection:
- Check if any code was changed this session (
git diff --stat) - If YES → immediately write the session log entry
- If NO (conversation-only) → skip per the "skip if conversational" rule
- Do NOT batch saves — write immediately on signal
Exit signal vs mid-task pause: "hold on", "hmm", "one more thing", "actually" do NOT trigger.
On the first session of a new month:
- Rename
session-log.md→archive-YYYY-MM.md - Create a fresh
session-log.md
This keeps the active log file manageable without losing history.
After 1 month: How many lessons have been written? How many promoted?
# Count lessons entries
grep -c "^\#\# \[" .claude/rules/lessons.md
# Count rules in code-standards.md (size proxy for accumulated knowledge)
wc -l .claude/rules/code-standards.md
# Find lessons at x2 or higher (recurring mistakes)
grep "\[x[2-9]\]" .claude/rules/lessons.mdA healthy system has:
lessons.md: 5–10 active entries (rest have graduated)- Rules files: growing over time with escalated lessons
- Few
[x2]entries (mistakes caught early) - Very few
[x3]entries (most caught before third occurrence)
- CLAUDE.md Design — Building the rules architecture
- Memory — Personal preferences vs project rules
- Example: Rules File Set — Complete reference implementation