Skip to main content

Emitting Lessons

When you make and correct a mistake during a session, you can emit a structured #lesson tag. The scanner harvests these automatically — no manual entry required.


The #lesson tag format

#lesson
tool: Bash
trigger: git stash
problem: git stash silently omits untracked files, risking data loss
solution: Use `git stash -u` to include untracked files
tags: tool:git, severity:data-loss
#/lesson

Required fields

FieldDescription
problemWhat went wrong and why. Not just "it failed" — explain the root cause. Min 20 chars.
solutionThe concrete correction. Commands, flags, code. Copy-pasteable. Min 20 chars.

Optional fields

FieldDescription
toolThe Claude Code tool name: Bash, Read, Edit, Write, Glob
triggerA command, file path, or pattern that will become the trigger. Do not use prose gerunds — "git stash" not "running git stash".
tagsComma-separated category:value tags. See tag taxonomy.
scopeproject to restrict to the current project only. Omit for a global lesson.

Scope field

To restrict a lesson to the current project only, add scope: project:

#lesson
tool: Bash
trigger: just test
problem: just recipe leaks env vars into sub-shells
solution: Use `just --set KEY val` instead of export
tags: tool:just
scope: project
#/lesson

Omit scope entirely for a global lesson that applies across all projects.


Cancelling a lesson

To retroactively cancel a lesson tag emitted earlier in the same session:

#lesson:cancel
problem: first ~60 chars of the problem field to cancel
#/lesson:cancel

Use /lessons:cancel for an interactive cancel workflow (also handles DB records).


When to emit

Emit a #lesson tag whenever you:

  • Discover why a tool call failed and apply a different approach
  • Catch yourself about to repeat a known mistake
  • Receive a user correction ("no", "wrong", "that's not right")
  • Identify a root cause after debugging

Do not force lesson tags where none apply. Only tag genuine problem→solution sequences.


Examples

Bash command with wrong flags

#lesson
tool: Bash
trigger: pytest
problem: Running bare `pytest` in Claude Code causes the process to hang waiting for TTY input in non-interactive environments
solution: Use `python -m pytest --no-header -p no:faulthandler` instead
tags: lang:python, tool:pytest, severity:hang
#/lesson

Wrong API namespace

#lesson
tool: Bash
trigger: mock.patch
problem: mock.patch needs to patch where the object is used, not where it's defined — using the wrong namespace causes the mock to have no effect
solution: Patch `mymodule.os.path.exists`, not `os.path.exists`, when `mymodule` imports and uses `os.path.exists`
tags: lang:python, topic:testing
#/lesson

File path pattern

#lesson
tool: Edit
trigger: package-lock.json
problem: Editing package-lock.json directly breaks npm's integrity checksums, causing install failures
solution: Let npm manage package-lock.json — run `npm install` to update it, never edit manually
tags: tool:npm, severity:silent-failure
#/lesson

Session-start reasoning reminder

#lesson
problem: Assuming Bash is available in subagents — Agent tool spawns may have a restricted tool set, and calling a missing tool wastes a turn
solution: Check available tools before assuming Bash is present; use Read/Grep/Glob for file operations in potentially restricted contexts
tags: topic:agents
#/lesson

(No trigger means it becomes a type: 'protocol' lesson — injected at session startup, not on a specific tool call.)


What happens next

The scanner picks up #lesson tags on the next session startup. The background scan:

  1. Reads new bytes in the session JSONL
  2. Extracts the tag
  3. Validates fields (length, no placeholders, no duplicate)
  4. Writes a Tier 1 candidate with confidence >= 0.75

If it passes validation, the candidate auto-promotes to the lesson store and the manifest is rebuilt.

If it fails validation (too short, near-duplicate of existing lesson, etc.), it appears in /lessons:review for manual adjustment.


Tag format

Tags follow category:value format. Common categories:

CategoryValues
langpython, typescript, javascript, go
toolpytest, git, npm, docker, vim
severityhang, data-loss, silent-failure, error
topictesting, auth, networking, types, agents

See the full tag reference for all established tags.


Embedding in code fences

If you're emitting a lesson inside a markdown code block (e.g., in a documentation comment), the scanner strips the fence delimiters before parsing:

```
#lesson
tool: Bash
trigger: git stash
problem: ...
solution: ...
#/lesson
```

Both forms are recognized. The outer backticks are not included in the parsed content.


Compliance threshold

The reporting protocol is injected at session startup. If you find yourself not emitting tags even when mistakes occur, consider increasing the priority of the session-start protocol lesson or reviewing your compactionReinjectionThreshold setting to ensure the protocol re-injects after /compact.