Learn: capture session learnings into skills and rules
Turn something discovered during this session into a durable, verified edit to the guidance that should have covered it. The goal is that the next session never has to rediscover it.
This skill proposes; it does not commit. Edits land only after the
user approves the diff, and committing is handed to /commit.
The inverse skill is /author-skill: /learn folds a learning into
guidance that already exists, /author-skill creates guidance that has
no home yet. If the right destination for a learning turns out to be a
skill this repo does not have, that is an /author-skill job.
This repo lives at C:\Repos\Personal\agent-config; ~/.claude/skills
holds one junction per skill into it, so /learn may fire from a
session in any repo. All repo-relative paths and commands below (grep, the lint
script, /commit) run from that repo root, not the session's cwd.
Step 1 — Identify what was learned
Reflect on the session, not just the last message. Candidate learnings:
- A skill or rule said X; reality was Y (doc-vs-reality gap).
- A step was missing and cost debugging time.
- An error message whose cause was non-obvious.
- A constraint / limit / version change not documented anywhere here.
- A user correction that reveals a general principle.
For each candidate, state in one or two sentences: problem → root cause → correct approach → generalization. Drop anything that is one-off, already obvious from the code, or only matters to this conversation. Confirm the list with the user before proceeding if it contains more than one item or you're unsure which matters.
Step 2 — Identify which guidance was in use (automatic)
Do not ask the user which skill was used. Reconstruct it:
- Skills invoked this session — every
Skilltool call and every skill whose content appears in context (the<command-name>/ loaded-skill blocks). Record thename:of each. - Rules auto-loaded — any
rules/coding-*.mdcontent present in context, triggered by files in session scope (paths:globs). - Global CLAUDE.md or rule sections relied on — e.g. the
uvguidance (claude/CLAUDE.md) or the Fabric serialization rule (rules/fabric-git-serialization.md). - Tools used — MCP servers / CLIs (
fab,pbir, fabric-cicd, Fabric REST) point at the skill that owns them even if it wasn't explicitly invoked. Map by the skill'sdescription. - If nothing was loaded but a skill should have triggered, that is
itself a learning: the fix is the skill's
description(trigger phrases), not its body.
Output a short table: learning → owning skill/rule → section.
Step 3 — Map to the destination
| Learning is… | Destination |
|---|---|
| Domain procedure, API shape, syntax, gotcha for one product area | skills/<name>/SKILL.md at the heading where it belongs; detail or long examples go in skills/<name>/references/REFERENCE.md |
| Cross-product troubleshooting symptom (error text → cause) | skills/fabric-gotchas/SKILL.md and a one-line cross-reference from the owning skill |
| Language / style convention that should apply whenever a file type is open | rules/coding-<lang>.md (path-scoped via paths:) |
| Environment or machine-wide constraint for every session | claude/CLAUDE.md (then re-run scripts/link-claude.ps1 -Force to push it to ~/.claude/CLAUDE.md — it is a copy, not a junction) |
| Skill didn't trigger when it should have | the skill's frontmatter description (≤ 1024 chars, see scripts/lint-frontmatter.py) |
| Fact about the user or their workflow preference | auto-memory (~/.claude/projects/.../memory/) — never domain knowledge |
Weave the learning into the existing structure. Do not append a
## Learnings changelog section — skills here are curated reference,
not logs. Update the relevant heading, table row, or gotcha entry so a
reader finds it where they'd look.
Step 4 — Check existing coverage
Before writing anything:
grep -rn -i "<key term>" skills/ rules/ CLAUDE.md claude/CLAUDE.md
- Already covered correctly → nothing to do; say so.
- Covered but wrong or stale → the edit is a correction; quote the current text in the proposal.
- Covered in
fabric-gotchasbut missing from the owning skill (or vice versa) → add the cross-reference only.
fabric-gotchas is the natural magnet for everything; guard against
duplicates there most carefully.
Step 5 — Verify before encoding
A thing that failed once is not yet a rule. Before proposing, confirm at least one of:
- Official docs (
microsoft_docs_search/microsoft_docs_fetch, or the library's README / changelog) state or corroborate it. - A second reproduction in the session (different input, same result).
- The user explicitly confirms it's known behaviour, not a fluke.
If it can't be verified, still propose it but mark it clearly as
unverified in the text (e.g. "Observed Aug 2026 with v1.3; not yet
documented") so a future drift-audit can confirm or remove it.
Include the date and version where relevant — these learnings age.
Step 6 — Propose the edit
For each learning, show the user:
- Destination file and heading.
- The exact text to add / replace, as a diff or before/after block.
- Verification source (link, or "unverified — see note").
Keep the addition as short as a reader needs: typically 1–6 lines in
SKILL.md, with anything longer in references/. Match the surrounding
voice and formatting. If a description is edited, state the new
length.
Check description headroom before proposing a trigger phrase. Many
skills here sit within a few characters of the 1,024-char cap, so a new
phrase usually has to displace an existing one rather than extend the
line. Measure the current length first — lint-frontmatter.py only reports
the overflow after the edit is written. If the budget is tight, name
what to cut; if nothing can go, say so and leave the description alone
rather than silently dropping a trigger that already earns its place.
Wait for approval. Apply only what is approved, using Edit so the
rest of the file is untouched. Then run:
uv run --with pyyaml scripts/lint-frontmatter.py skills/<name>/SKILL.md
Step 7 — Hand off
Report what changed and where, then hand off to /commit (do not
commit yourself). Suggested subject shape:
docs(fabric-cicd): note parameter.yml regex is case-sensitivefix(fabric-gotchas): correct cause of 24556 snapshot conflictfeat(rules): add KQL materialize() guidance
If claude/CLAUDE.md changed, remind the user to re-run
scripts/link-claude.ps1 -Force to push it to ~/.claude/CLAUDE.md
(it is a copy, not a junction). Root CLAUDE.md is the repo's only
project-scope instruction file — there is no AGENTS.md mirror — so
there is never a hand-sync step.
Example (illustrative — not a real fabric-cicd fact)
Session: user deployed with fabric-cicd; publish_all_items skipped a
Warehouse because the item folder name contained a space, which the
skill didn't mention. Docs confirm folder names must match item
display names exactly.
learning → owning skill → section
folder name with space skipped silently → fabric-cicd → "Per-item-type caveats"
Proposal:
skills/fabric-cicd/SKILL.md → ## Per-item-type caveats
+ - **Folder names must match the item display name exactly** — a + mismatch (including whitespace) is skipped with no error; check + `change_log_level("DEBUG")` output. (Docs: <link>, verified v1.3.)Also add to
fabric-gotchasunder "Deployment" as a one-liner pointing here.
Not a learning (skip): "the deploy took four minutes" — one-off, not actionable.