# Sage Self Learning

> Detects mistakes, stores prevention rules, promotes them across scope — the 'experience' layer for any AI agent that recurringly hits the same bugs, miscommunications, or wrong-tool choices across sessions. Use when: (1) a command or operation fails unexpectedly, (2) the user corrects the agent, (3) the agent discovers non-obvious behavior through debugging, (4) an API or tool behaves differently than expected, (5) a better approach is found for a recurring task. Also searches past learnings before starting tasks to avoid known pitfalls. Activate alongside the sage-memory skill — they share the same MCP backend but serve different purposes (sage-memory = durable context, sage-self-learning = agent mistakes and gotchas).

- Skill: `xoai/sage-self-learning` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add xoai/sage-self-learning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xoai/sage-self-learning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: xoai (https://skillmd.com/u/xoai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xoai/sage-self-learning

---


<!--
  Fallback copy — sage's vendored prose for users without the
  sage-memory MCP package installed.

  Canonical version: ships with sage-memory >= 0.8.0 and is deployed
  automatically by `sage update` (which runs `sage-memory
  install-skills` when the package is present). Edits here only
  affect users who don't have sage-memory installed.

  To override locally for everyone: create a project-level skill
  under .claude/skills/ with a different name.
-->

# Self-Learning

Learn from mistakes. Don't repeat them.

Captures what went wrong, what was non-obvious, and what the agent
should do differently. Every learning includes a **prevention rule** —
a forward-looking instruction that changes future behavior.

**Part of the unified knowledge system.** Self-learning stores through
sage-memory (or files) with the `self-learning` tag / `learning` type.
During recall, learnings surface as warnings alongside regular knowledge.

## Capabilities by Backend

| Capability | MCP | Files |
|------------|-----|-------|
| Store learnings | ✅ `sage_memory_store` | ✅ `.sage-memory/lrn-*.md` files |
| Search learnings | ✅ BM25 + `filter_tags` | ⚠️ scan `lrn-` files by name |
| Update learnings | ✅ `sage_memory_update` | ✅ edit file |
| Delete learnings | ✅ `sage_memory_delete` | ✅ delete file |
| Browse by type | ✅ `sage_memory_list` | ✅ scan `lrn-` files |
| Link to entities | ✅ `sage_memory_link` | ⚠️ `relations:` frontmatter (see sage-ontology skill) |
| Multi-hop graph recall | ✅ `sage_memory_graph` | ❌ single-hop scan only |
| Namespace isolation | ✅ `filter_tags` | ✅ `lrn-` filename prefix |

**How to detect backend:** At session start, call `sage_memory_set_project`
with the project root. If it responds, use MCP. If not, use
`.sage-memory/` files.

## Recall: Search Before You Work

At task start, search for learnings relevant to the current task.

### With MCP

**Basic recall (keyword):**
```
sage_memory_search(
  query: "<task-relevant keywords>",
  filter_tags: ["self-learning"],
  limit: 5
)
```

Always include `filter_tags: ["self-learning"]` — this excludes all
non-learning entries.

**Targeted recall (graph-based):** When you know the current task's
ontology entity ID:
```
sage_memory_graph(
  id: "<task_entity_memory_id>",
  relation: "applies_to",
  direction: "inbound",
  depth: 1
)
```

Returns learnings explicitly linked to this task — more precise than
keyword search.

**Hot spot detection:**
```
sage_memory_graph(
  id: "<module_entity_id>",
  relation: "applies_to",
  direction: "inbound",
  depth: 1
)
```
If 5+ linked learnings → flag the area as mistake-prone.

### With Files

Scan `.sage-memory/` for `lrn-` prefixed files. Read filenames and
identify those relevant to the current task. Read matching files for
their prevention rules.

For a broad search: list all `lrn-*.md` files and scan names.
For a focused search: look for keywords in filenames like
`lrn-stripe-webhook-*.md` when working on Stripe webhooks.

### Reporting

When learnings are found, report the **prevention rule**, not the
incident. Say: "Before working with Stripe webhooks, verify that body
parsing middleware is skipped for the webhook route."

When nothing is found, say nothing.

## Capture: Detect and Store

### Five Learning Types

| Type | Trigger |
|------|---------|
| `gotcha` | Non-obvious behavior discovered through debugging |
| `correction` | User corrected the agent |
| `convention` | Undocumented project/team pattern discovered |
| `api-drift` | API/library behaves differently than expected |
| `error-fix` | Recurring error with a known solution |

### How to Store

**Title:** `[LRN:<type>] <specific description>`

**Content:** Four-part structure:
1. **What happened** — the symptom
2. **Why it was wrong** — root cause
3. **What's correct** — the right approach
4. **Prevention** — what to check BEFORE this happens again

**With MCP:**
```
sage_memory_store(
  title: "[LRN:gotcha] Stripe webhook requires raw body before JSON parsing",
  content: "What happened: Webhook signature verification failed with 400.
    Why: Express body parser replaced raw body with parsed JSON.
    What's correct: Use express.raw() for the webhook route.
    Prevention: Before implementing any webhook handler that verifies
    signatures, check whether the SDK requires the raw request body.",
  tags: ["self-learning", "gotcha", "stripe", "webhooks"],
  entities: [
    {name: "Stripe", type: "TECHNOLOGY"},
    {name: "Express", type: "TECHNOLOGY"}
  ],
  scope: "project"
)
```

**Extract Before Store (0.9+).** Pass an `entities` array naming the
technologies, services, or modules the learning is about. The
Prevention line in particular should usually reference a real entity
(library, service, module) so future graph traversal from that entity
surfaces this learning. Optional `relations` array if the learning
connects two entities (e.g., `{from: "Express", to: "Stripe", rel:
"contradicts"}` for a body-parser/signature-verification conflict).

**With files:**
```
File: .sage-memory/lrn-stripe-webhook-raw-body.md

---
tags: [self-learning, gotcha, stripe, webhooks]
type: learning
scope: project
created: 2026-03-20
---

[LRN:gotcha] Stripe webhook requires raw body before JSON parsing

What happened: Webhook signature verification failed with 400
"No signatures found matching the expected signature."

Why: Express body parser replaced raw body with parsed JSON before
the Stripe SDK could verify the signature.

What's correct: Use express.raw({type: 'application/json'})
middleware for the webhook route, before the global body parser.

Prevention: Before implementing any webhook handler that verifies
signatures (Stripe, GitHub, Twilio), check whether the SDK requires
the raw request body. If yes, ensure body parsing middleware is
skipped or deferred for that route.
```

### Link to Ontology Entities (MCP only)

After storing a learning, link it to the relevant entity:
```
sage_memory_link(
  source_id: "<learning_memory_id>",
  target_id: "<task_or_module_entity_id>",
  relation: "applies_to"
)
```

**With files:** Skip linking. Mention the related entity in the content
if the connection is important: "Related entity: task_a1b2 (Fix payment
timeout)."

### Link to Code Symbols (0.11+ Codebase Scan)

When a learning applies to a SPECIFIC function or class — not just
"the payment system" but `PaymentOrchestrator.charge` on line 47 —
link it to the actual symbol so the recall surfaces during code work
on that exact symbol.

**Detect availability:** if `sage-memory scan-codebase --help` exits
0 and a recent scan has run, code-symbol memories exist in the
project DB.

**Workflow:**

1. **Find the symbol's memory id:**
   ```
   sage_memory_search(
     query: "PaymentOrchestrator.charge",
     filter_tags: ["codebase"],
     limit: 3
   )
   ```
   The file-memory result's `id` field is the parent for all symbols
   in that file. For symbol-level linkage, query `code_symbols`
   directly via the MCP search (filter on the qualified name in tags
   or content), or accept the file-level link as the v1 granularity.

2. **Link the learning to the file memory:**
   ```
   sage_memory_link(
     source_id: "<learning_memory_id>",
     target_id: "<file_memory_id>",
     relation: "applies_to"
   )
   ```

3. **Future recall:** when an agent works on the same file, the
   graph channel surfaces this learning even if the query doesn't
   mention the file by name — entity-mediated proximity at work.

**Why bother:** "show me past mistakes on `PaymentOrchestrator`" is
the single most valuable self-learning query, and it ONLY works when
learnings are linked to the code structure, not just to free-text
file paths that drift when files move.

**With files:** Skip — relative paths in prose go stale; the
file-memory id is stable across renames as long as content_hash
doesn't change. Without MCP, document the path in the content and
re-find the file each session.

### When a Learning Causes a Bug

When you follow a stored self-learning entry and it leads to incorrect
behavior (wrong library, outdated pattern, contradicted convention):

1. **Store a NEW learning** (type: `correction`) describing what the
   original said, why it's now wrong, and what the correct approach is.

2. **Invalidate the original:**
   ```
   sage_memory_update(id: "<original_id>", status: "invalidated")
   ```

3. **Link the correction to the original:**
   ```
   sage_memory_link(
     source_id: "<correction_id>",
     target_id: "<original_id>",
     relation: "corrects"
   )
   ```

The original learning will never appear in search again. The correction
replaces it as active knowledge. The graph edge preserves the audit trail.

**With files:** Rename the original file to `lrn-INVALID-<name>.md` and
add `status: invalidated` to its frontmatter. Create the correction as
a new file.

### When a memory is a paraphrase of an older one (0.12.0+)

When `sage_memory_store` returns a `suggested_links` entry with
`confidence: "near_duplicate"`, the new content is a semantic
paraphrase of an existing memory (cosine similarity ≥ 0.95
against the existing memory's embedding). Decide one of:

- **Link via `supersedes`** if the new wording is more accurate or
  current:
  ```
  sage_memory_link(
    source_id: "<new_id>",
    target_id: "<older_id>",
    relation: "supersedes"
  )
  ```
  Future `sage_memory_search` results will surface the older
  memory with `superseded_by: <new_id>` so agents can prefer the
  newer one. The older memory is NOT filtered or down-ranked —
  transparency over silent hiding.

- **Merge content** if the old phrasing carries useful detail the
  new one lost: `sage_memory_update(id: "<older_id>",
  content: "<merged_text>")` then `sage_memory_delete(id: "<new_id>")`.

- **Keep both** if they cover meaningfully different angles (rare
  at cosine ≥ 0.95). No action needed; both stay active.

**`supersedes` vs `corrects` — pick the right one:**
- `corrects` + `status: invalidated` (see "When a Learning Causes
  a Bug" above) is for memories that are *factually wrong* —
  outdated library names, broken patterns, contradicted
  conventions. The original is hidden from future search.
- `supersedes` is for *semantic paraphrase* where both versions
  are valid but the newer is preferred. Both stay visible;
  the older carries a pointer to the newer.

### Search Before Store (Semantic Reinforcement)

Before creating a new learning, check for existing similar learnings:

1. Search with the new learning's core content:
   ```
   sage_memory_search(
     query: "<what_happened + prevention_rule>",
     filter_tags: ["self-learning"],
     limit: 3
   )
   ```

2. If the top result describes the **same root cause and same prevention**:
   - Do NOT create a new entry
   - Update the existing entry with any new details:
     ```
     sage_memory_update(id: "<existing_id>", content: "<merged content>")
     ```
   - The access_count bump from the search already signals reinforcement

3. If no strong match → store as new.

Why: Three entries saying "check middleware order" waste search slots.
One entry that gets richer over time is more useful.

**With files:** Scan `lrn-*.md` filenames for similar topics. If a
match exists, edit that file instead of creating a new one.

### When NOT to Store

Ask: "Would this change how I approach a future task?"
- **No** → don't store
- **Yes** → store

**Budget:** 2-5 learnings per significant task.

## Review: Curate and Improve

Triggered by "sage review" or "review learnings."

### With MCP

1. **Inventory** — `sage_memory_list(tags: ["self-learning"])` → all learnings
2. **By type** — `sage_memory_list(tags: ["self-learning", "gotcha"])` etc.
3. **Stale check** — flag learnings about changed code or outdated APIs
4. **Consolidate** — merge 3+ similar → store consolidated → link to same
   entities → delete originals
5. **Promote** — identify learnings for scope escalation
6. **Hot spots** — `sage_memory_graph` on key entities → count inbound
   `applies_to` edges → report most mistake-prone areas

### With Files

1. **Inventory** — list all `lrn-*.md` files
2. **By type** — read frontmatter to group by type tag
3. **Stale check** — check creation dates, read content for outdated refs
4. **Consolidate** — manually merge file contents → create new file →
   delete originals
5. **Promote** — identify candidates, create global-scope copy
6. **Hot spots** — count `lrn-*.md` files by domain keyword in filename

## Promote: Scope Escalation

**Project → Global:** Learning applies beyond this codebase. Store a
context-independent version at global scope.

**With MCP:** `sage_memory_store(..., scope: "global")`
**With files:** Copy to `~/.sage-memory/` (global directory), remove
project-specific details.

**Global → Team:** Export to a shared file in the repo. **Read:**
`references/team-sharing.md`.

**Read:** `references/promotion-rules.md` for criteria.

## Quality Principles

**Prevention over documentation.** Every learning answers: "What should
I check before this happens again?"

**Specificity retrieves.** `[LRN:gotcha] Stripe webhook requires raw body`
retrieves. `[LRN:gotcha] API issue` does not.

**Freshness matters.** Update or delete when code changes make a learning
obsolete.

**Learnings are not memories.** "Billing uses saga pattern" is a memory.
"Agent assumed REST, broke the compensation chain" is a learning.

## References

- `references/capture-patterns.md` — Triggers, examples, prevention rules
- `references/storage-conventions.md` — Format conventions
- `references/promotion-rules.md` — Scope escalation criteria
- `references/team-sharing.md` — Export formats for teams
- `references/review-workflow.md` — Curation process
- `references/examples.md` — End-to-end scenarios
- `references/ontology-integration.md` — Graph integration

