Last lesson ended with four runs and four re-derivations of the same house rules. There is one place to write them down: a skill. What makes it work is not the file itself. It's that Claude reads two fields of it every session, and the rest only when it's needed.
What a run re-derives
Be concrete about what each run works out from scratch. Three things, all named at the end of Lesson 2.2.
| What | What the runs did |
|---|---|
| Branch names | No rule on disk. Run one picked fix/apply-discount, run two fix/pricing-bug, run three bugfix-pricing, run four patch/discount. Four runs, four answers, and none of them is yours. |
| The test command | Only CI knows it. Two runs guessed (pytest, python -m pytest tests/), one read ci.yml and ran pytest -v, and one stopped and asked you. |
| Files it must never touch | Nothing to read at all. Three runs guessed right. The fourth edited workflows/ci.yml to go green. There is no default for this one. |
You might be thinking: put it in the prompt. You can, and it works for that one command. But then your project's conventions live inside a command string you typed once, days ago, in a session you have since closed, and every run for the next seven days reads that version of them.
A skill lives in the repo instead. It's edited with a diff, reviewed in a pull request, and picked up by the next run without you being awake for it.
One folder, one file
A skill is a folder under .claude/skills/, one folder per skill, holding one
SKILL.md. You commit it with the code.
---
name: fix-conventions
description: How to fix a failing test in loop-engineering-demo.
Branch naming, the test command, and the files that must
never be edited. Use when triaging, fixing, or verifying
a test failure in this repo.
---
# Fixing a failing test here
- Branch from master as fix/<slug>. Never push to master.
- Run the suite with pytest -v. Green means exit 0 and a
non-zero collected count — not exit 5.
- Never edit .github/workflows/. CI changes go to a human.
- A test on the quarantine list gets quarantined, not fixed.
The top half is frontmatter: a name, and a description of what the skill
covers and when to use it. The bottom half is prose: your rules, in your words.
The split that matters
The two halves load differently, and that difference is the whole trick.
- The top half is always loaded. Every session, every run, whether or not the skill is ever used. Two fields of resident context, and they are the entire advertisement.
- The bottom half is loaded on match. On every run where the description doesn't match, it costs nothing. It is read only when the description wins.
So the loop doesn't relearn your conventions, and it doesn't carry them around either. An always-loaded instructions file pays for every word of itself on every turn, so it has to stay short and generic. A skill pays for two fields until it's needed, so it can afford to be specific, which is the only kind of convention worth writing down.
The description is the trigger
That puts all the weight on one field. Here is what most people write:
description: Fix conventions.
It's true and short, and it will almost never fire. Nothing in a failing-test run reads like the words "fix conventions", so the body is never opened. And a skill that never fires looks exactly like a skill you didn't need.
Here is the one that fires:
description: How to fix a failing test in loop-engineering-demo. Branch naming, the test command, and the files that must never be edited. Use when triaging, fixing, or verifying a test failure in this repo.
It names the repo, the situation, and the moment to reach for it. Each of those phrases is a handle the run's own words can catch on.
Write the description first. It is the only part of a skill Claude sees before it decides, so the body is a reward the description has to win.
Same command, more knowing
The command has not changed. This is the third lesson running with it, word for word:
/loop 1d "Check the repo for newly failing tests. Rerun each 3x to rule out flakiness. If a real failure remains, fix it and keep going until the suite passes, then push the branch (not master)."
What changed is what the run knows when it starts.
What the skill buys you:
- Every run opens with the same branch name, the same test command and the same do-not-touch list. Four runs, one answer.
- It's tracked, so it follows into every worktree for free, unlike
.loop/guard.py, which Lesson 2.2 had you copy across by hand. - A convention change gets a diff, an author and a review, like code. Prompt text gets none of those.
What it doesn't:
- A skill is prose, and Lesson 1.2 already said what prose is: a request the model honours, not a ceiling it can't argue past. If a rule has to hold, it's a hook.
- It is silent when it misses. There's no error for a skill that didn't fire. You find out by reading a run that got the branch name wrong.
- It goes stale like any other document. Nothing checks that the branch rule still matches what your team does. A skill is only as true as its last edit.
After this lesson
You will be able to:
- name what an unattended run re-derives every time, and why a prompt is the wrong home for it;
- write a
SKILL.mdunder.claude/skills/and commit it with the code; - explain why the frontmatter is always loaded and the body only on a match;
- write a description that names the repo, the situation and the moment, so the skill actually fires;
- tell which of your rules can live in a skill and which need a hook.
What comes next
The loop now knows how you fix things. It still can't open the pull request: it fixes, pushes, and leaves you a report to act on. In Lesson 2.4, connectors: giving the loop a real action to take, and gating the ones you can't take back.