Author a LESSON.md (AIP-11)
Use this skill when the user (or a runtime's distill pipeline) asks to capture a transferable lesson from a completed run. The skill produces a valid AIP-11 LESSON.md file — one lesson, one trigger, one evidence-grounded outcome — that another runtime can load and inject into future turns.
When to use
- "I just watched the agent fail on X — write up the lesson so it doesn't repeat."
- "This run worked unusually well; capture why."
- "The distill pipeline produced a candidate; turn it into a checked-in LESSON.md."
- "Several runs all hit the same gotcha — collapse them into one lesson."
When NOT to use
- The user wants to record a fact about the world ("Stripe's invoice endpoint returns paginated results") → that is declarative knowledge; use the wiki-page authoring path (AIP-10) instead. Lessons are imperative ("do X" / "avoid Y").
- The user wants to store the full trajectory of a run for replay — that is run state, not a lesson. Lessons are the distillate.
- The user wants to change the agent's prompt — that is prompt evolution (AIP-12), a different file.
Process
Follow these steps in order. Each step has a short justification — keep that justification visible to the reviewer so it's clear why the lesson ended up the shape it did.
1. Fix identity
- Pick
slug: kebab-case, 2–80 chars, imperative voice. Good:verify-page-id-before-load-more. Bad:paginationorlesson-1. - Write
title: one sentence in the imperative — the lesson as the agent would read it ("Verify the page identifier before clicking Load More").
The slug is also the filename. Slug is the unit of supersession; a new lesson
that replaces an old one cites the old slug in supersedes.
2. Write the trigger
The trigger is the most underestimated field. It decides whether the lesson injects into a future turn at all — vague triggers waste prompt budget; precise triggers win.
trigger.description: one or two sentences in plain text — what shape of task or situation invites this lesson. Frame as a detectable pattern, not as a story.trigger.tags: optional retrieval keywords. Keep them narrow. Three tags are usually enough.trigger.targets: optional operator/role/skill globs. Use this to scope a lesson to one operator (operator: marketing-lead) or a whole role (role: researcher). Empty targets means "anyone."
A test: if the trigger reads as "always" or "every time the agent runs," it is too broad; tighten or split into multiple lessons.
3. Set the outcome
outcome is one of:
success— the run succeeded because of the behaviour the lesson recommends.failure— the run failed because of the behaviour the lesson warns against. Failure-only lessons are first-class.mixed— applied across many runs, the behaviour helped sometimes and hurt sometimes; the lesson is conditional.
Do not pick success reflexively. A counter-example from a single failure is
often the more transferable lesson.
4. Cite evidence
Every lesson MUST cite at least one evidence entry pointing back to the source
run. Evidence is provenance — it lets a human re-read the original trajectory
and check the distillation.
kindis one ofrun,conversation,work-item,audit,wiki-page. (Audits are AIP-7 governance records.)refis an opaque id or path the host can resolve. Never free text. If you cannot cite a concrete reference, you are not distilling — you are speculating.noteis one short line — what happened. Not the lesson; the event. ("Agent clicked Load More twice and got a stale page.")
Multiple evidence entries are encouraged when the lesson is distilled across runs (see EXAMPLES.md pattern 7).
5. Set counts honestly
success_count and failure_count track real applications of the lesson, not
author guesses. On first creation:
- One success run that demonstrated the lesson →
success_count: 1,failure_count: 0. - One failure run that established the counter-example →
success_count: 0,failure_count: 1.
Counts are then updated by the runtime every time the lesson's trigger fires and the agent's behaviour either matches or contradicts the recommendation. Authors do not edit counts after creation; runtimes do.
6. Pick a confidence
confidence is in [0, 1]. Default 0.5 at first sighting — one data point is
one data point. Move it up only as success_count accumulates and the lesson
survives review.
The retrieve contract weighs confidence against
success_count - failure_count; runtimes are not obliged to trust
author-supplied confidence over observed counts.
7. Decide scope and TTL
supersedes: list slugs of lessons this one replaces. Supersession is explicit — the runtime never silently overwrites a lesson. When you supersede, write why in the body.expires_at: optional ISO 8601 soft TTL. Use when a lesson is tied to an external state that may change (a vendor's UI, a known bug, a deprecated API). Past the TTL, retrieval treats the lesson as absent.
A lesson with neither supersedes nor expires_at is a long-lived heuristic — that is the common case.
8. Compose the file
Write lessons/<slug>.md:
---
schema: learning/v1
slug: <kebab-slug>
title: <imperative one-sentence title>
trigger:
description: <plain-text trigger>
tags: [<topic>, <topic>]
targets:
- role: <slug-or-glob>
outcome: success | failure | mixed
evidence:
- kind: run
ref: <run-id>
note: <one-liner>
confidence: 0.5
success_count: 0
failure_count: 1
---
# <title>
## When this applies
<expanded trigger prose>
## What to do (or avoid)
<imperative steps>
## Counter-example
<short narrative of the run>
The body has three required sections — When this applies, What to do (or avoid), Counter-example — and stays short. A lesson is a heuristic, not an essay.
9. (Optional) Add a defineLesson entry
For runtimes that load lessons programmatically rather than as plain files, mirror the manifest in code:
// lesson.ts
import { defineLesson } from "<host-runtime>"
export default defineLesson({
slug: "verify-page-id-before-load-more",
title: "Verify the page identifier before clicking Load More",
trigger: {
description: "Multi-page list view with a Load More button.",
tags: ["pagination", "ui-automation"],
},
outcome: "failure",
evidence: [
{
kind: "run",
ref: "r_2026-04-21_a3f2e9",
note: "Stale page after second click.",
},
],
successCount: 0,
failureCount: 1,
confidence: 0.5,
})
The signature is normative across runtimes; consult the host's
ADAPTER.md for naming conventions in non-TS languages
(define_lesson, DefineLesson, etc.).
10. Validate
Validate the manifest against ./LESSON.schema.json:
npx ajv validate -s ./LESSON.schema.json -d ./lessons/<slug>.md
Fix any errors before declaring success. Watch in particular for:
confidenceoutside[0, 1].outcome: successwithfailure_count > 0(allowed, but flag for review — the lesson may need to bemixed).supersedesreferencing a slug that does not exist.
11. Regenerate the index
Every distill or supersession SHOULD regenerate lessons/_index.md so retrieval
has a fast directory. The runtime owns this — authors need not edit _index.md
by hand.
Output
Produce one (or two) files in the chosen folder:
lessons/
<slug>.md # the LESSON manifest
lesson.ts # OPTIONAL — defineLesson entry, only if the
# runtime loads lessons as code
Reply to the user with:
- The slug and file path written.
- A one-line summary of
trigger.targets+outcome+ counts so they can verify scope before the lesson goes into rotation. - Any open assumptions — fields where you defaulted (
confidence: 0.5, emptytargets, no TTL) so the user can tighten if needed.
Do NOT inject the lesson into a live agent yourself. Authoring ends with the file written; injection is the runtime's retrieve contract, which the author does not call directly.
See also
- AIP-11 — LESSON.md spec
- AIP-7 — governance, audit, evidence anchors
- AIP-10 — knowledge / wiki pages (declarative siblings)
- AIP-12 — prompt evolution / playbook (the prompt-side sibling)
./ADAPTER.md— implementer's guide for runtimes./EXAMPLES.md— reference LESSON.md patterns (success, failure, narrow-scope, broad-scope, supersession, conflicting evidence, multi-run distillation)