| name | lessons |
| description | Capture and integrate lessons learned. Two modes — capture (single lesson) and scan (mine session files or extended JSONL logs for skill-change proposals). |
| user_invocable | true |
| args | Optional. Free-text lesson, OR `scan` (default scan of session files), OR `scan --deep` (deep scan of JSONL transcripts). |
Model Selection
- Default model (capture mode): Haiku — appending a structured lesson is a formatted write
- Scan mode (default depth): Sonnet — clustering signals from session files into skill-change proposals is a judgment task
- Scan mode (deep): Opus when transcript count > 20 — cross-session synthesis across raw JSONL needs larger reasoning
Lessons Learned Handler
You operate in one of two modes, dispatched by the first argument:
| First token | Mode |
|---|
scan | Scan mode (default depth) — read recent session markdown summaries |
scan --deep | Scan mode (deep) — read recent JSONL extended transcripts |
| anything else (or empty) | Capture mode — record a single lesson |
Resolve scope (every mode)
Before dispatching to a mode, locate the workspace and resolve scope from the active session marker.
The skill's base directory is <workspace>/.claude/skills/lessons/; resolve <workspace> by walking up three directory levels and validate that <workspace>/.claude/.workspace exists.
Scan active markers from <workspace>/sessions/active/*.md and <workspace>/projects/*/sessions/active/*.md. Parse frontmatter (project_slug, workstream_slug, session_id).
- One match → scope is
<workspace> if project_slug = workspace, else <workspace>/projects/<project_slug>.
-
1 matches → ask the user which session this invocation is scoped to.
- 0 matches → check whether
project_slug and session_id were passed as context from /bye. If yes, derive scope from project_slug (<workspace> if project_slug = workspace, else <workspace>/projects/<project_slug>) and proceed. If no context was passed, surface as a bug and abort (manual invocation: ask the user for scope instead).
Modes A and B use <scope> for input/output paths. Mode C is workspace-wide; the marker is read for scope-attribution decoration only.
Mode A: CAPTURE (default)
Triggered when the arg is absent or is free text (not starting with scan).
Lessons in capture mode come from:
- The
/bye skill passing identified lessons
- The user invoking
/lessons "description of what was learned"
- Manual invocation with
/lessons (ask the user what was learned)
Steps
-
Gather lessons: If lessons were passed as input, use those. If invoked manually without input, ask the user what they learned.
-
Categorize each lesson into one of:
convention — code style, naming, project patterns
bug-pattern — common mistakes, gotchas, debugging insights
preference — user preferences for tools, workflow, communication
architecture — structural decisions, design patterns
tool-usage — tips for tools, CLIs, MCP servers, frameworks
-
Append to lessons file: Read <scope>/.claude/memory/lessons-learned.md with mtime capture, then append each lesson under the appropriate category section with this format:
- **[YYYY-MM-DD]** Description of the lesson
-
Store in cognee (if available): If the cognee MCP is healthy, call cognee_add with the lesson text (prefixed with its category), then call cognee_cognify to integrate it into the knowledge graph.
-
Trigger skills-manager: Invoke the /skills-manager skill to assess whether the lessons warrant updates to any skill files. Pass the lessons as context.
-
Report:
Lessons captured: [count]
Categories: [list]
Cognee: stored / skipped (MCP unavailable)
Skills review: [triggered / no changes needed]
Mode B: SCAN (default depth)
Triggered by /lessons scan (no --deep flag).
Input source: <scope>/sessions/*.md — markdown summaries written by /bye. (Working state lives at scope root, not under .claude/memory/.)
Steps
-
Select window: list files modified in the last 7 days. If fewer than 3 exist, expand to the last 14 days. Cap at 20 files — prefer the most recent.
-
Read each file and extract signals:
- User corrections ("no", "don't", "stop doing X", "that's wrong")
- User confirmations ("yes exactly", "perfect", "keep doing that")
- Tool failures or repeated retries on the same call
- Workarounds that worked after an initial failure
- Explicit mentions of a skill name + a problem
- Abandoned approaches (task started, dropped mid-session)
-
Cluster signals by target skill: for each signal, identify which skill should have prevented or handled it. Group by skill name. Signals with no clear target go into a cross-cutting bucket.
-
Draft skill-change proposals. For each cluster of 2+ related signals, draft one entry:
- **skill:** <name>
- **change:** <what to add, remove, or rewrite>
- **why:** <one-sentence rationale>
- **evidence:** <session-file refs, e.g. `session-2026-04-19_144813.md:L42`>
-
Write proposal file to <workspace>/contributions/lessons-scan-<YYYY-MM-DD>-default.md (mtime-check on write):
# Lessons scan — default mode — <YYYY-MM-DD>
Window: <N> session files, <start-date> to <end-date>
Signals found: <count>
Proposals: <count>
## Proposed skill changes
<proposals>
## Cross-cutting observations
<any signals that don't map to a single skill>
-
Hand off to skills-manager: Invoke /skills-manager with the proposal file path as context. Skills-manager runs its own holistic-review → approval-gate → apply workflow.
-
Report to the user:
Scan mode: default
Window: <N> files
Proposals written: <count> → <workspace>/contributions/lessons-scan-<date>-default.md
Skills-manager: triggered
Mode C: SCAN --deep
Triggered by /lessons scan --deep.
Input source: ~/.claude/projects/<your-project>/*.jsonl — raw harness transcripts (one file per session, turn-by-turn).
Steps
-
Select window: list JSONL files modified in the last 3 days. Note the unbounded count first — if >20, use Opus model for this scan. Then cap at 15 files, preferring the most recent.
-
Scan each JSONL. Each line is a JSON object representing one turn (user message, assistant response, tool call, tool result). Look for signals the markdown summary could have missed:
- Mid-turn user frustration ("why are you...", "stop", multiple "no" in a row)
- Tool-retry loops (same tool called 3+ times with similar args and errors)
- Long tool-result dumps the assistant ignored
- Reasoning chains that contradicted the final action
- Abandoned subagent launches
- Skills invoked that failed silently
- Repeated permission denials on the same tool
-
Cluster signals by target skill — same as default scan, but the signal vocabulary is richer (raw tool errors, turn-level sentiment, not just end-of-session summary).
-
Draft skill-change proposals — same format as default scan, but evidence refs point to JSONL files and line numbers:
- **evidence:** `010cbd9d-adb5-4730-8121-64c158238a39.jsonl:L1247`
-
Write proposal file to <workspace>/contributions/lessons-scan-<YYYY-MM-DD>-deep.md (mtime-check on write; same structure as default-mode proposal, with Mode: deep and Scope attribution: <workspace | projects/<slug> from marker> in the header).
-
Hand off to skills-manager — same as default scan.
-
Report to the user:
Scan mode: deep
Window: <N> JSONL files (<total line count>)
Proposals written: <count> → <workspace>/contributions/lessons-scan-<date>-deep.md
Skills-manager: triggered
mtime-check protocol
For every shared-file write (Mode A append; Mode B/C proposal write):
- Read the file; capture mtime.
- Compute the edit.
- Re-stat. If mtime changed since the read, re-read the file and re-compute the edit from scratch. Retry up to 3 times.
- Write.
On retry exhaustion (>3 conflicts), prompt the user.
Division of labor
This skill detects (finds what needs to change). skills-manager governs (decides and applies changes). Never edit skill files from within lessons — always hand off.
What NOT to scan
- Files older than the window (noise swamps recent signal)
- JSONL files > 50MB (probably a stuck session, skip)
- Sessions flagged as test/scratch in their filename
Downstream skills — do not duplicate
contribute operates on captured lessons to prepare upstream Memnyx contributions. Downstream of this skill.