/learn
Captures something worth remembering across sessions: a correction from the operator, a discovered pattern, a workaround for a specific quirk. Lands as a structured entry in .claude/memory/lessons.md (project-level) or ~/.lintel/lessons.jsonl (operator-level).
The only mechanism in Lintel that compounds learning across fresh sessions. Without it, the same correction gets made repeatedly.
When to use
- Operator just corrected the assistant's approach — record the pattern
- A debugging session revealed a non-obvious project quirk — record it
- A new convention has been agreed (naming, file layout, framework idiom) — record it
- A reusable workflow emerged that's not yet a skill — record it as a "skillify candidate"
When NOT to use
- One-off info that won't recur — wasted bytes, signal noise
- Code-level patterns better captured in code comments — comments belong in code
- Something that belongs in CLAUDE.md or design docs — those are higher-authority, write there instead
- Sensitive info (passwords, customer data, internal IDs) — Layer 2 blocks
Inputs
- Required: the lesson body (inline prose)
- Optional
--scope <project|global> — project writes to .claude/memory/lessons.md in the current repo; global writes to ~/.lintel/lessons.jsonl (default: project)
- Optional
--type <correction|pattern|quirk|skillify-candidate> — categorization (default: pattern)
- Optional
--source <text> — what triggered this (e.g. "operator correction at 16:42", "debug session for refund flow")
Workflow
- Validate scope. If
--scope project and no .claude/memory/lessons.md exists: create it with a frontmatter header. If --scope global and no ~/.lintel/lessons.jsonl exists: create empty.
- Compliance scan. Run Layer 2 patterns over the lesson body. If a secret/customer-data pattern hits: BLOCK + ask operator to rewrite without the sensitive bit.
2b. Update-phase (ADR-0006). Before appending, check what already exists:
source lib/memory.sh; lessons_find_related <keywords> — classify the candidate
add / update / supersede / no-op exactly as CAPTURE Step 2 does. Only add creates
a new entry; supersede also stamps the old lesson with superseded_by: L-NNN (date).
- Format entry. Project lessons use the L-NNN grammar — the mechanical layer
(
lessons_surface, the digest, the budget check) keys on ^## L-NNN; a dated heading
would be invisible to all of it. Next number = highest existing + 1:## L-NNN — <one-line summary>
**Rule:** <the durable rule>
**Why:** <source / what triggered it, with date>
**How to apply:** <bullets>
Global lessons:{"date": "YYYY-MM-DD", "type": "...", "source": "...", "body": "...", "repo": "..."}
- Append. Atomic write (read existing, append entry, write back).
- Audit log. One line via the unified writer:
source "${LINTEL_SOURCE_ROOT:-$(git rev-parse --show-toplevel)}/bin/_audit.sh"; audit_log lessons lesson_recorded scope=<project|global> id=<L-NNN> classification=<add|update|supersede> → .claude/runtime/audit/lessons.jsonl.
- Report.
Report format
Lesson recorded
Scope: project (.claude/memory/lessons.md)
Type: pattern
Source: operator correction at 16:42 — wanted "Start free case" not "Get started"
Body:
> CTA copy on landing pages should use "Start free case" (canonical primary CTA).
> Never use "Get started", "Start your analysis", or other variants. Refactor when seen.
Future sessions reading .claude/memory/lessons.md will surface this at session start (per repo CLAUDE.md "Review at session start" rule).
Compliance integration
- Layer 2 secret/customer-data scan on lesson body — BLOCKS if pattern hits.
- Project lessons file (
.claude/memory/lessons.md) is committed to repo — anything in it is visible to all collaborators. Sanity-scan applies.
- Global lessons file (
~/.lintel/lessons.jsonl) is local-only. Looser scanning, but still no customer-data.
Failure modes
- Lesson body too vague to be useful: WARN + ask whether to proceed. A vague lesson signals nothing actionable to future sessions.
- Duplicate lesson (same body within 30 days): report + ask whether to skip or merge.
- Compliance scan hits: BLOCK, surface what hit, refuse to write. Operator rewrites + retries.
- Project lessons file conflicts with
/code-freeze: if frozen, refuse + ask operator to /code-unfreeze first.
Examples
Operator correction:
> /learn "CTA copy must be 'Start free case' on landing, never 'Get started' — canonical primary CTA per project memory" --type correction
✓ Lesson appended to .claude/memory/lessons.md. Visible to future sessions.
Skillify candidate:
> /learn "Recurring task: regenerate /portal/cases mock data after schema change. Could be a /regen-mocks skill." --type skillify-candidate
✓ Lesson recorded. Run /skillify when ready to formalize.
Global quirk:
> /learn "On Windows, gh CLI returns case-normalized URLs (jokerman89 instead of jokerman89). Push works but display may surprise." --scope global --type quirk
✓ Lesson appended to ~/.lintel/lessons.jsonl. Visible in any repo.
See also
/skillify — turn a skillify-candidate lesson into a real skill
.claude/memory/lessons.md (project) / ~/.lintel/lessons.jsonl (global) — where lessons live
- Project CLAUDE.md "Self-improvement loop" — the discipline this skill enables
/retro — session-end reflection that may emit several /learn calls
1---2name: learn3description: Use after a correction, insight, or recurring pattern worth remembering to record it as a lesson the next session will read at startup. Reach for it whenever the operator corrects you or you discover something that should prevent the same mistake recurring.4---56# /learn78Captures something worth remembering across sessions: a correction from the operator, a discovered pattern, a workaround for a specific quirk. Lands as a structured entry in `.claude/memory/lessons.md` (project-level) or `~/.lintel/lessons.jsonl` (operator-level).910The only mechanism in Lintel that compounds learning across fresh sessions. Without it, the same correction gets made repeatedly.1112## When to use1314- Operator just corrected the assistant's approach — record the pattern15- A debugging session revealed a non-obvious project quirk — record it16- A new convention has been agreed (naming, file layout, framework idiom) — record it17- A reusable workflow emerged that's not yet a skill — record it as a "skillify candidate"1819## When NOT to use2021- One-off info that won't recur — wasted bytes, signal noise22- Code-level patterns better captured in code comments — comments belong in code23- Something that belongs in CLAUDE.md or design docs — those are higher-authority, write there instead24- Sensitive info (passwords, customer data, internal IDs) — Layer 2 blocks2526## Inputs2728- Required: the lesson body (inline prose)29- Optional `--scope <project|global>` — `project` writes to `.claude/memory/lessons.md` in the current repo; `global` writes to `~/.lintel/lessons.jsonl` (default: project)30- Optional `--type <correction|pattern|quirk|skillify-candidate>` — categorization (default: pattern)31- Optional `--source <text>` — what triggered this (e.g. "operator correction at 16:42", "debug session for refund flow")3233## Workflow34351. **Validate scope.** If `--scope project` and no `.claude/memory/lessons.md` exists: create it with a frontmatter header. If `--scope global` and no `~/.lintel/lessons.jsonl` exists: create empty.362. **Compliance scan.** Run Layer 2 patterns over the lesson body. If a secret/customer-data pattern hits: BLOCK + ask operator to rewrite without the sensitive bit.372b. **Update-phase (ADR-0006).** Before appending, check what already exists:38 `source lib/memory.sh; lessons_find_related <keywords>` — classify the candidate39 add / update / supersede / no-op exactly as CAPTURE Step 2 does. Only `add` creates40 a new entry; `supersede` also stamps the old lesson with `superseded_by: L-NNN (date)`.413. **Format entry.** Project lessons use the L-NNN grammar — the mechanical layer42 (`lessons_surface`, the digest, the budget check) keys on `^## L-NNN`; a dated heading43 would be invisible to all of it. Next number = highest existing + 1:44 ```markdown45 ## L-NNN — <one-line summary>46 **Rule:** <the durable rule>47 **Why:** <source / what triggered it, with date>48 **How to apply:** <bullets>49 ```50 Global lessons:51 ```jsonl52 {"date": "YYYY-MM-DD", "type": "...", "source": "...", "body": "...", "repo": "..."}53 ```544. **Append.** Atomic write (read existing, append entry, write back).555. **Audit log.** One line via the unified writer:56 `source "${LINTEL_SOURCE_ROOT:-$(git rev-parse --show-toplevel)}/bin/_audit.sh"; audit_log lessons lesson_recorded scope=<project|global> id=<L-NNN> classification=<add|update|supersede>` → `.claude/runtime/audit/lessons.jsonl`.576. **Report.**5859## Report format6061```62Lesson recorded6364Scope: project (.claude/memory/lessons.md)65Type: pattern66Source: operator correction at 16:42 — wanted "Start free case" not "Get started"6768Body:69> CTA copy on landing pages should use "Start free case" (canonical primary CTA).70> Never use "Get started", "Start your analysis", or other variants. Refactor when seen.7172Future sessions reading .claude/memory/lessons.md will surface this at session start (per repo CLAUDE.md "Review at session start" rule).73```7475## Compliance integration7677- Layer 2 secret/customer-data scan on lesson body — BLOCKS if pattern hits.78- Project lessons file (`.claude/memory/lessons.md`) is committed to repo — anything in it is visible to all collaborators. Sanity-scan applies.79- Global lessons file (`~/.lintel/lessons.jsonl`) is local-only. Looser scanning, but still no customer-data.8081## Failure modes8283- **Lesson body too vague to be useful:** WARN + ask whether to proceed. A vague lesson signals nothing actionable to future sessions.84- **Duplicate lesson (same body within 30 days):** report + ask whether to skip or merge.85- **Compliance scan hits:** BLOCK, surface what hit, refuse to write. Operator rewrites + retries.86- **Project lessons file conflicts with `/code-freeze`:** if frozen, refuse + ask operator to `/code-unfreeze` first.8788## Examples8990**Operator correction:**91```92> /learn "CTA copy must be 'Start free case' on landing, never 'Get started' — canonical primary CTA per project memory" --type correction93✓ Lesson appended to .claude/memory/lessons.md. Visible to future sessions.94```9596**Skillify candidate:**97```98> /learn "Recurring task: regenerate /portal/cases mock data after schema change. Could be a /regen-mocks skill." --type skillify-candidate99✓ Lesson recorded. Run /skillify when ready to formalize.100```101102**Global quirk:**103```104> /learn "On Windows, gh CLI returns case-normalized URLs (jokerman89 instead of jokerman89). Push works but display may surprise." --scope global --type quirk105✓ Lesson appended to ~/.lintel/lessons.jsonl. Visible in any repo.106```107108## See also109110- `/skillify` — turn a `skillify-candidate` lesson into a real skill111- `.claude/memory/lessons.md` (project) / `~/.lintel/lessons.jsonl` (global) — where lessons live112- Project CLAUDE.md "Self-improvement loop" — the discipline this skill enables113- `/retro` — session-end reflection that may emit several /learn calls