Testing a skill from a fresh session
The last lesson ended with a release skill that works in the directory it was written in. This lesson runs it somewhere else: a clean clone, and a new session that has none of the conversation you had while you wrote it. Anthropic’s guide to writing skills describes the same loop. One session helps you write the skill, a fresh one uses it on a real task, and you watch where the fresh one struggles and bring that back into the file [1].
First you run the skill and read the transcript for the places where the agent hesitated, asked you something or guessed. Then you turn each of those places into a line in the skill, run it again and compare the two runs. Two short Python scripts show what the agent sees in each repository, and the course checks their output on every build.
Run it where it wasn’t written
Section titled “Run it where it wasn’t written”A clean clone is a copy of the package with its own git repository and
nothing else. It has none of the author’s tags, history or uncommitted
files. Make one outside the course repository. Claude Code reads the
CLAUDE.md files in the directory it starts in and in every directory
above it [2], so a copy inside the course repository
gets the course’s own agent instructions as well.
Auto memory saves what one session learned for the sessions after it. It is
on by default, and it is kept per git repository [2].
The same page says to turn it off for one project by setting
autoMemoryEnabled to false in that project’s settings, and the
commands below do that in .claude/settings.local.json. They also tell
git to ignore that file, so it never shows up as a change. Your personal
instructions in ~/.claude/CLAUDE.md load in every session, in every
project [2], and they still reach the test.
Run these commands from the top of your copy of the course repository, so
that the cp line finds the package. The first line deletes
~/notes-clean, so the commands also work on a second attempt. The
rm -rf __pycache__ line removes compiled files that are in the fixture
directory if you ran its check in the course repository.
rm -rf ~/notes-cleancp -R site/examples/customizing-agents/first-skill/fixture-package ~/notes-cleancd ~/notes-cleanrm -rf __pycache__git init -qecho '{ "autoMemoryEnabled": false }' > .claude/settings.local.jsonmkdir -p .git/infoecho .claude/settings.local.json >> .git/info/excludegit add .git commit -q -m "Import the notes package"The copy contains the release skill from the last lesson at
.claude/skills/release/SKILL.md. Start your agent in ~/notes-clean,
in a new session, and type /release 0.4.0. In Claude Code the skill’s
directory name is the command you type [3], and in
another agent you can ask for release 0.4.0 in plain words. The skill
changes files, commits and tags, and that’s why it runs in this copy and
nowhere else. The copy has no remote, so a push has nowhere to go.
Step 3 of the skill says to write one changelog bullet per change since the
previous release and to read git log for the list. The first script builds two
repositories and asks each one what step 3 needs. The author’s repository
has the package committed as release 0.3.0, a v0.3.0 tag on that commit,
and one change after it. The clean clone is the one you just made.
What does step 3 have to work with?
Section titled “What does step 3 have to work with?”A release skill tells the agent to write one changelog bullet per change since the previous release, using git log. A script builds two repositories from the same package: the author's, with the release tagged v0.3.0 and one commit after it, and a clean clone with the package committed once as 'Import the notes package' and no tags. For each it prints the last release tag from git describe, and the commits since it, or the whole log when there is no tag.
Predict the output for both repositories. The script prints none where
git describe --tags --abbrev=0 fails.
python3 fresh_clone.pyauthor's repository last release tag: v0.3.0 commits since it: Document where notes are stored clean clone last release tag: none whole log: Import the notes package
Output verified in CI from site/examples/customizing-agents/testing-a-skill/fresh_clone.py.
Which of the two repositories has a tag for git describe to find? Without a tag, what is left in the log?
In the author’s repository, step 3 has one commit to turn into one bullet. The clean clone gives it no tag to count from, and its only commit contains the whole package. The skill is silent about that case, and the agent has to choose for itself. Here is the kind of run that follows. It is written by the course to show the pattern, and it is not recorded from a model. Your agent’s words differ.
/release 0.4.0
Tests pass: OK.There are no tags, so I can't find the commit for 0.3.0. The log has onecommit, "Import the notes package". I'll describe the package as it is.Updated notes.py to 0.4.0.Added to CHANGELOG.md: ## 0.4.0 - Notes command with list, add and --version.release 0.4.0: okgit status shows an untracked __pycache__/ directory. Should I include itin the release commit?Read the transcript as a list of places where the skill gave no
instruction. The agent
guessed at the change list and wrote a bullet for features that shipped in
earlier releases. It asked about __pycache__/, which the test run in step
1 leaves behind. The check still printed ok, because it looks for a
changelog heading and never reads the bullets under it. Each of those
places is a line the skill is missing.
Turn each hesitation into a line
Section titled “Turn each hesitation into a line”In this run the hesitations came from a skill that assumed something about the environment, such as a tag or a clean working tree. In other runs they come from a step that assumes a tool is installed, or from a success test too vague to tell a right result from a wrong one. Anthropic’s guide says the same about scripts: don’t assume a package is installed, and name what the skill needs [1].
The fix for the first two is a short list of checks at the top of the
skill, each with what to do when it fails. Add this section above
## Steps:
## Before you start
Run these checks in order. If one fails, stop, and report the command, whatit printed and what the user has to do. Don't fix the repository yourself.
1. `git --version` and `python3 --version` each print a version. If one prints an error, that tool is missing. Name it and stop.2. `git status --porcelain` prints nothing, or only `?? __pycache__/`. An error means this directory is not a git repository. Any other line is an uncommitted change. Say which, and stop.3. `git describe --tags --abbrev=0` prints the tag of the previous release, `v` and the `__version__` in `notes.py`. If it fails, there is no release tag. Ask the user to tag the commit of the previous release, and stop. Don't work that commit out from the changelog.4. `git log --format=%s <tag>..HEAD` prints at least one line. If it prints nothing, there is nothing to release. Say so and stop.Then point step 3 at that log, and make step 5 say what to commit:
3. In `CHANGELOG.md`, add a `## <version>` heading under the title, above the previous release, with one bullet per user-visible change in the log from check 4. Leave out a commit a user would not notice. Never write a bullet that no commit in that log supports....5. Stage `notes.py` and `CHANGELOG.md` only, and commit with the message `release: <version>`. Step 1 leaves a `__pycache__` directory behind. Don't commit it. Then tag the commit with `git tag v<version>`.Check 3 is the one that answers the transcript. The agent guessed because the skill gave it nothing to do when the tag was missing. Now it has an instruction, and the instruction is to stop. A skill that stops with a clear question is better than one that finishes with an invented changelog, because you see the stop, and you might not read the changelog.
Which line answers which hesitation?
Section titled “Which line answers which hesitation?”A learner ran a release skill from a new session in a clean clone of a package and read the transcript. Each row is a place where an agent hesitated or was wrong, and the options are lines the learner could add to the skill.
Match each place where the agent hesitated to the line that answers it.
For each row, ask what the agent did not know at that moment, and which line tells it.
The last row is the vague success test. The first version of the skill’s
“Done when” section says git tag --list 'v*' shows the new tag. That is
true whenever the tag exists, wherever it points. The second fixture
releases 0.4.0 twice in the author’s repository, once with git tag run
before the release commit and once after it, and prints three checks each
time.
Which check tells the two releases apart?
Section titled “Which check tells the two releases apart?”A script releases version 0.4.0 of a small package twice. The first time it runs git tag v0.4.0 before the release commit, and the second time after it. Each time it prints the package's release check, the output of git tag --list 'v*' joined on one line, and git describe --tags, which prints the tag alone when the current commit is tagged and adds the number of commits since the tag and a hash otherwise.
Predict the six lines under the two headings. The script replaces the
commit hash that git appends with <hash>.
python3 tag_placement.pytag before the commit release_check.py: release 0.4.0: ok git tag --list 'v*': v0.3.0 v0.4.0 git describe --tags: v0.4.0-1-g<hash> tag after the commit release_check.py: release 0.4.0: ok git tag --list 'v*': v0.3.0 v0.4.0 git describe --tags: v0.4.0
Output verified in CI from site/examples/customizing-agents/testing-a-skill/tag_placement.py.
Does the release check or the tag list look at where the tag points? What does git describe add when the tag is on an older commit?
Only git describe --tags differs. When the tag is one commit early,
v0.4.0 points at the commit before the version bump, and anyone who
checks out that tag gets 0.3.0 code. So the new “Done when” says:
`python3 release_check.py` prints `release <version>: ok`,`git describe --tags` prints exactly `v<version>`, and`git status --porcelain` prints nothing, or only `?? __pycache__/`.Don't push.Report the version, the tag and the changelog entry, with the commiteach bullet came from.The whole revised skill is in the course repository at
site/examples/customizing-agents/testing-a-skill/release/SKILL.md.
Run it again and compare
Section titled “Run it again and compare”First put the clone back as it was before the first run. These two
commands delete the v0.4.0 tag, and move the branch back to the import
commit, which drops every change after it. git tag -d prints an error
if the first run made no tag, and that is fine. The reset also drops
edits you haven’t committed, so run it before you revise the skill.
cd ~/notes-cleangit tag -d v0.4.0git reset -q --hard "$(git rev-list --max-parents=0 HEAD)"Now write your revisions into .claude/skills/release/SKILL.md, or copy
the revised file from the course repository, and commit the change. The
new check 2 stops on any uncommitted change, and that includes an edited
skill. In the cp line, replace <course-repo> with the path to your
copy of the course repository. Leave the cp line out if you wrote your
own revision.
cd ~/notes-cleancp <course-repo>/site/examples/customizing-agents/testing-a-skill/release/SKILL.md .claude/skills/release/SKILL.mdgit commit -q -a -m "Revise the release skill"Claude Code picks up an edited skill in the session that is already
running [3], but start a new session anyway. The old
session still holds your answer about __pycache__/, and a rerun in it
tests the skill plus that answer. Auto memory is off in this clone, and
in Claude Code you can run /memory to see which memory files a session
can load [2]. Type /release 0.4.0 again.
A good run now stops at check 3. The agent reports that git describe
found no tag and asks you to tag the commit of the previous release. That
is the skill working: the repository lacks a fact that no agent can work
out, and the skill says to ask for it.
What does the rerun tell you?
Section titled “What does the rerun tell you?”A learner revised a release skill after a first test run, adding checks before the release steps. One check stops the agent and asks the user when the repository has no tag for the previous release. The learner reruns the skill in a new session in a clean clone that has no tags.
In the rerun, the agent stops at the new check 3 and asks you to tag the commit of the previous release. What does that result tell you about the revised skill?
Which fact was missing from the repository, and could any agent have found it on its own?
Now give the clone the history the skill expects. Tag the import commit, which holds release 0.3.0, and make one change after it:
cd ~/notes-cleangit tag v0.3.0 HEAD~1echo 'Notes are stored in `notes.json`, next to `notes.py`.' >> README.mdgit commit -q -a -m "Document where notes are stored"Start one more new session and type /release 0.4.0. Compare the
transcript with the first one. Check 4 lists two commits, the skill
revision and the README change. A user never sees the skill, so a good run
writes one bullet, for the README change, and reports the commit it came
from. After it, git describe --tags prints v0.4.0. If the agent
hesitated somewhere new, that is the next line to add, and the loop is the
same: run, read, revise, run again.
A missing line, or the skill working?
Section titled “A missing line, or the skill working?”A learner tests the first version of a release skill by running it from a new session in a clean clone and reading the transcript. That version runs the tests and stops if they fail, and fixes what a FAIL line from the release check names. It has no checks before its steps, says nothing about a missing release tag, and doesn't say which files to commit.
Did the skill tell the agent what to do at that moment?
Which line fails the wrong release?
Section titled “Which line fails the wrong release?”A release skill ends with a Done when section, the test the agent runs to decide that the release is right. In one run the agent created the tag v0.4.0 one commit before the release commit, so the tag points at the old code.
An agent tagged v0.4.0 one commit before the release commit. Which
“Done when” line fails that release?
Which of these looks at where the tag points, and which only at whether it exists or at the files?
What should the skill say when the tag is missing?
Section titled “What should the skill say when the tag is missing?”A release skill tells the agent to write one changelog bullet per commit since the tag of the previous release. In a colleague's clean clone there are no tags, and in a test run the agent wrote a changelog for the whole package.
You are revising the skill after that run. Which line do you add?
Which of these gives the agent the fact it lacks, and which ones leave it to work the fact out?
Put the test in order
Section titled “Put the test in order”The lesson tests a skill so that the conversation the author had while writing it can't fill a gap for the agent.
- Make a clean clone of the repository
- Start a new session with auto memory off
- Run the skill as a colleague would ask for it
- Read the transcript for each place the agent hesitated
- Add a line for each one, and rerun in a new session
What has to be fresh before the run, and what do you do with what you read afterwards?
Exercise
If you followed along with the walkthrough, your runs in ~/notes-clean
are the exercise, and you can go to the result below. Otherwise make a
clean clone with the commands in the first section. They delete and
recreate ~/notes-clean, and the clone has the release skill from the
last lesson in it. The skill edits notes.py and CHANGELOG.md, commits
and tags, so run it only in that copy, which has no remote to push to. In
a new session, type /release 0.4.0, and write down each place in the
transcript where the agent hesitated, asked you something or guessed. For
each one, add a check, a failure step or a sharper “Done when” line to the
skill. Then reset the clone, commit the revised skill and rerun in a new
session, as in the last section.
A good result: your list has at least two places, each with the line
that answers it. The rerun stops at the missing tag instead of guessing,
and after you tag v0.3.0 and make one change, it finishes with one
changelog bullet and git describe --tags printing v0.4.0. Which of
your hesitations came from something you knew and never wrote down?
Stretch: Ask a colleague to run your revised skill in their own clean clone, on their own machine, without help from you. Add one line for each question their agent asked.
Recap
- Test a skill in a clean clone and a new session, with auto memory off [2], so the conversation you had while writing it can’t fill a gap for the agent [1]. Your personal instructions still load, and a colleague’s machine is the test without them.
- Read the transcript for each place the agent hesitated, asked or guessed. Each one is a line missing from the skill.
- A missing line is often a check on the environment before the steps, a step that names the tool or state it needs, or a success test exact enough to fail a wrong result.
- When a fact is missing from the repository, the right line tells the agent to stop and ask. A clear stop is better than a guess that passes the check.
- Rerun in a new session after each revision, and compare the two runs.
You can now
- Packages a repeatable procedure as a skill another person's agent can use
References
Section titled “References”- Anthropic. Skill authoring best practices. Claude Platform documentation. Reference.
Claude docs best-practices - Anthropic. How Claude remembers your project. Claude Code documentation. Reference.
Claude Code memory - Anthropic. Extend Claude with skills. Claude Code documentation. Reference.
Claude Code skills