Turning repeated work into something the agent keeps
This lesson closes the course with one decision. We read three sessions in which a coding agent works on the same small project, and we find what the user had to explain in more than one of them. Each repeated explanation then moves to the project instructions, a skill, a hook or a context file, where the agent finds it next time. Last, we cut the lines of the instructions file that a hook and a skill now cover. The file gets shorter, because the hook and the skill hold what it used to say.
The files are in the course repository at
site/examples/customizing-agents/repeated-work/. The session logs
are in sessions/, and instructions-before.md is the project’s
instructions file as it was during the sessions. The course wrote the logs
for this lesson, and no model produced them. find_repeats.py is a short
Python script that only reads the logs. Where the lesson describes Claude
Code, the facts were checked against the vendor’s documentation on the date
at the end of the lesson. Other coding agents have the same mechanisms
under other names.
The same explanation in three sessions
Section titled “The same explanation in three sessions”The project is the frontend of a small web shop. Its pages get their data
from a billing API through a client that a generator writes into
src/api/generated/. In each of the three sessions the billing API has a
new field, so the agent must regenerate the client before it can use the
field. Each session works out again how to do that. The user corrects the
agent in each session, and some of the corrections are the same each time.
The script compares the three logs. It prints each message the user sent in more than one session, and each command the agent ran in all three.
What came up more than once
Section titled “What came up more than once”Run this in site/examples/customizing-agents/repeated-work/, and compare
with the output below.
python3 find_repeats.pySaid by the user in more than one session: sessions 1, 2, 3: Don't edit files in src/api/generated/ by hand. Change the spec or regenerate the client. sessions 1, 2, 3: Set SPEC_URL to https://billing.example.com/staging/openapi.json, that's the staging spec. sessions 1, 3: Amounts from the billing API are in cents. Use formatMoney() from src/lib/money.ts, it divides by 100 and adds the currency. sessions 2, 3: Call the billing API the way this snippet does, through useApi(), so the page gets the loading and error states: Run by the agent in every session: npm run generate make api-client SPEC_URL=https://billing.example.com/staging/openapi.json make api-client npm run lint npm run format -- src/api/generated npm test -- src/api npm test
Output verified in CI from site/examples/customizing-agents/repeated-work/find_repeats.py.
Read the output as a list of costs. Each line under the first heading is
something the user typed again that a file could have said once. The
commands under the second heading are the agent’s search for the
procedure. The first attempt is npm run generate, a script that doesn’t
exist. The make target comes next, without the variable it needs, and
only after that the command that works. A lint error then shows that the
generated files must be formatted. The same search happens in each of the
three sessions.
That’s the symptom to look for in your own sessions: the same
explanation, given twice. The fix is turning repeated work into reusable
knowledge. You move the explanation to a place the agent reads without
being told, so that no later session needs it. The Agent Engineer Course
calls knowledge of how to do a task procedural memory, and it is often
written into the agent’s instructions [1]. Claude Code’s guide to
its extensions treats such moments as the signs that a project needs one
more piece of setup [2]. When the agent has broken
the same convention twice, add the convention to CLAUDE.md. When you
have given the agent the same steps three times, write them as a skill.
And when something has to happen on every occasion, with no exception,
use a hook.
One line of the output needs a closer look. The instructions file already
says “Never edit files under src/api/generated/”, and the user still had
to say it in all three sessions. Claude Code reads the project’s
instructions file at the start of every session [3], so
the agent had the rule. It still started a hand edit in each session.
A place for each repeated step
Section titled “A place for each repeated step”Each kind of repeated step has its own place. The choice depends on what the step is and what one miss costs.
- A fact that every task needs goes into the project instructions. The instructions file is loaded at the start of every session, so its lines cost context in every session [2]. “Amounts from the billing API are integer cents” is such a fact. It matters on any page that shows money, whatever the task is.
- A procedure goes into a skill. Steps that must happen in order are a procedure. A skill puts only its description in the context, and the steps load when a task matches the description or when you type the skill’s name after a slash [4]. The regeneration is three commands in a fixed order, and the agent needs them only when the client must change.
- A rule where one miss costs too much goes into a hook. An
instruction is a request. The agent weighs it against everything else in
its context. A hook runs on its event every time, whatever the model
remembers [2]. A hand edit under
src/api/generated/is lost the next time anyone regenerates the client, and nobody notices when it disappears. - An example to copy goes into a context file. Context files are
files the team keeps for the agent to read, reviewed like code. The
useApi()snippet is a pattern to copy. It belongs in a short file such asdocs/examples/billing-call.md, with one line in the instructions that says when to read it. This placement is the convention of this course, from the lesson on reviewing memory. The best-practices page gives similar advice for prompts: point the agent to an example of the pattern in your code [5].
Claude Code also lets a skill hold reference material, such as a style guide [2]. This lesson keeps skills for procedures and context files for examples, so the question “what’s this?” has one answer.
The staging URL is a fact that the regeneration needs and other tasks
don’t. The best-practices page lists required environment variables among
the things to put in the instructions file [5],
and that suits a variable that many tasks use. Only one command uses
SPEC_URL, the first step of the regeneration. So the URL goes into that
step, and a change to the URL is one edit in one file. A default in the
Makefile itself (SPEC_URL ?= ...) is the other good fix, because then
nobody has to know the URL.
Here is the skill, as .claude/skills/regenerate-api-client/SKILL.md in the
project [4]:
---name: regenerate-api-clientdescription: Regenerate the billing API client in src/api/generated/. Use when the billing API has changed, or when a field the API returns is missing from the client.---
1. Run `SPEC_URL=https://billing.example.com/staging/openapi.json make api-client`.2. Run `npm run format -- src/api/generated`. The generator's output fails the lint rules until it is formatted.3. Run `npm test -- src/api`. If a test fails, stop and report the failure.
Done when `npm run lint` reports no problems and the API tests pass.The hook is a PreToolUse hook with the matcher Edit|Write, so Claude
Code runs it before each call to its Edit and Write tools. The hook gets
the tool call as JSON, and for these tools tool_input.file_path is
always the absolute path of the file. For a path with
/src/api/generated/ in it, the hook writes a message to standard error
and exits with code 2, which stops the edit and gives Claude the message
as the reason [6]. The matcher compares tool names, so
this hook sees only those two tools. A shell
command that writes the file, such as sed -i or cat >, goes through
the Bash tool, and this hook never sees it. Writing a hook that blocks a
mistake builds a hook of this kind step
by step. The message is all the agent learns about the rule when it is
blocked, so it says what to do:
src/api/generated/ is generated from the billing API spec, and a hand edit is lost at the next regeneration. Run /regenerate-api-client to update the client, or change the spec in the billing API.The user in the logs was a human approval step for this rule. They stopped the agent three times, and the answer was the same each time. Human in the loop says to keep approvals for the few moments that matter, so that each one gets your attention. A rule with one fixed answer doesn’t need your judgment, and the hook can give that answer. Your attention stays on the steps that do need judgment, such as reading the diff of the regenerated client before it is committed.
Where does each step go?
Section titled “Where does each step go?”The lesson places repeated steps from coding sessions by what they are: a fact every task needs goes into the project instructions, a procedure into a skill, a rule where one miss costs too much into a hook, and an example to copy into a context file.
Is it a fact for most tasks, steps in an order, a rule that must hold every time, or a pattern to copy?
Where does the staging URL go?
Section titled “Where does the staging URL go?”In three coding sessions the user told the agent the staging spec URL that the API client regeneration needs. The agent needs the URL only when it regenerates the client, and the team is writing a skill for the regeneration.
The team writes the regeneration skill. Where should the staging spec URL go?
Which tasks need the URL, and which place loads only for those tasks?
The rule was in the file
Section titled “The rule was in the file”A project's instructions file says never to edit files under src/api/generated/, because they are generated from an API spec. In three sessions the agent still started a hand edit there, and the user stopped it each time. A hand edit that slips through is lost at the next regeneration.
The instructions file already had the rule, and the agent started a hand edit in all three sessions. What do you change?
Which of these still depends on the model choosing to follow a line of text?
Cut what the hook and the skill now hold
Section titled “Cut what the hook and the skill now hold”The best-practices page suggests removing any line of the instructions file that the agent doesn’t need to avoid a mistake [5]. The same page warns that a long instructions file makes the agent miss the rules that matter.
Apply this to the long regeneration paragraph first. The skill holds the procedure now, with each step on its own line, and its description is in the context of every session [4]. Without the paragraph the agent still finds the steps, so the paragraph goes.
The line “Never edit files under src/api/generated/” is different. The
hook blocks the agent’s Edit and Write calls on those files, and its
message says what to do. It doesn’t see a shell command, and an agent
that is blocked on one tool may try the same change through another. So
the hook covers only part of the rule, and a line for the rest stays. It
can be short, and it can say where the change goes instead:
- Change files under `src/api/generated/` only by regenerating the client. A hook blocks direct edits.The line also helps the agent plan. Without it, the agent learns the rule only when a hook blocks an edit it has already planned, and it loses a turn to plan again. The skill’s description helps there too, because it names the case of a missing field.
The fact about amounts in cents and the pointer to the example file come in. Here is the change to the instructions file:
- Run the tests with `npm test`, and `npm run lint` before you commit. - Commit messages follow Conventional Commits.-- Never edit files under `src/api/generated/`. They are generated from the billing API spec.-- The API client is generated from the billing API's OpenAPI spec, and when the billing API changes you need to regenerate it, which is done with the api-client target in the Makefile, but that target needs the SPEC_URL variable set to the spec of the right environment (usually staging), and afterwards the generated files don't pass our lint rules, so format them with the format script before you run the API tests, and don't commit until those pass.+- Change files under `src/api/generated/` only by regenerating the client. A hook blocks direct edits.+- Amounts from the billing API are integer cents. Show them with `formatMoney()` from `src/lib/money.ts`.+- Before a page calls the billing API, read `docs/examples/billing-call.md` and follow it. - Pages live in `src/pages/`, shared components in `src/components/`.Delete a rule from the file only when a hook enforces the whole rule, and
shorten it when a hook enforces a part. The lint line stays whole,
because no hook in this project runs the linter. A line you cut in favor
of a hook also depends on that hook being registered on each machine. A
hook in the project’s .claude/settings.json is committed with the code,
and each clone runs it once the folder is trusted [6]. A
hook in your personal settings protects only your sessions, so keep the
full line until the hook is shared.
What happens to each line?
Section titled “What happens to each line?”A project now has a skill with the steps to regenerate the API client, and a hook in its shared settings that blocks the agent's Edit and Write calls under src/api/generated/. A shell command that writes those files doesn't reach the hook. No hook runs the linter.
Which two of these are right for the instructions file now?
For each line, what holds it or enforces it now, and does that cover all of it?
What does the hook say?
Section titled “What does the hook say?”A team cut its instructions line about files under src/api/generated/ down to one short sentence, because a hook now blocks the agent's edits there. The reason is gone from the file. The hook's message on standard error is what the agent receives when it is blocked. A skill named regenerate-api-client holds the regeneration steps.
The instructions file now says only that generated files change by
regeneration. The hook blocks a hand edit under src/api/generated/
when the agent tries one. Which message should it write?
The reason is gone from the file. Where does the agent now learn why, and what to do instead?
Where does it move?
Section titled “Where does it move?”Each row is something a user had to tell a coding agent in more than one session. The lesson places a repeated step by what it is: a fact for most tasks goes into the project instructions, a procedure into a skill, a rule where one miss costs too much into a hook, and an example to copy into a context file. It keeps skills for procedures.
Match each repeated explanation to the place it moves to.
Is it a fact for most tasks, steps in an order, a rule that must hold every time, or a pattern to copy?
The rule about the secrets file
Section titled “The rule about the secrets file”A team's instructions file says never to commit the .env file, which holds local secrets. The agent committed it once anyway. The team is deciding what to do with the rule.
What should the team do with the rule?
Which option stops the commit whatever the agent remembers, and tells it what to do?
Hook, or a line in the instructions?
Section titled “Hook, or a line in the instructions?”The lesson puts a rule into a hook that blocks the action when one miss costs too much, and keeps a rule as a line in the project instructions when it is guidance the agent may weigh against the task.
If the agent broke this rule once, what would it cost, and who would have to undo it?
The same fact, a third time
Section titled “The same fact, a third time”A developer has told a coding agent in three sessions that the staging database refuses writes from the agent's account, and that reports read from the replica. A write attempt fails with a permission error and does no harm. Almost every task in the project touches the database. The team shares one project instructions file in the repository.
You are about to type the same explanation about the staging database for the third time. What do you do with it?
Which tasks need the fact, and which place does every teammate's agent read in every session?
The same five steps again
Section titled “The same five steps again”The lesson places a repeated step by what it is: a fact for most tasks goes into the project instructions, a procedure into a skill, a rule where one miss costs too much into a hook, and an example to copy into a context file.
In four sessions you told the agent the same five steps for adding a new language to the site. Where do the steps go?
Is it a fact for most tasks, steps in an order, a rule, or an example?
Exercise
Copy instructions-before.md and the three logs to a folder of your own,
so you can change the copy freely. Read the three logs. Make a list of each
step that came up in more than one session, and next to each step write
where it goes (instructions, skill, hook or context file) and a one-line
reason. Then edit your copy of the instructions file: delete or shorten
the lines that a hook or a skill now covers, and add the lines that point
to the new places. Write the skill’s description and the hook’s message as well. This
is work on paper, and it takes about ten minutes. It practices the question
to ask the next time you type an explanation you have typed before.
A good result places the repeated steps as this lesson did: the
regeneration and its staging URL, the rule about generated files, the
amounts in cents and the useApi() example. Your file keeps the lines for
Node, the tests and the linter, the commit messages and the folders, and a
short line about generated files.
Compare with the files in model-answer/. Your wording will differ, and
the places should be the same. If you placed a step somewhere else, write
the reason down and check it against the four rules in “A place for each
repeated step”. Which
explanation did you give an agent more than once this week, and where
would it go?
Stretch: Do the same for a project of your own. Read the last three sessions you had with an agent on it, list what you said in more than one of them, sort each item the same way, and write the new instructions file. Try any hook in a branch you can throw away first.
Recap
- The sign to look for is the same explanation given twice. Each one can move to a place the agent reads without being told, so no later session needs it [2].
- A fact that most tasks need goes into the project instructions, and a procedure goes into a skill, which loads only when a task needs it [4].
- A rule where one miss costs too much goes into a hook, which runs every time. An instruction is a request, even one the agent has read [2].
- In this course, an example to copy goes into a context file, with one line in the instructions that says when to read it.
- A hook enforces only the tool calls it sees. Delete a line that a shared hook or a skill holds in full, and shorten a line that a hook covers in part, such as a rule a shell command can still break. The instructions file gets shorter, and the rules left in it stand out [5].
You can now
- Manages what the agent carries between sessions
- Adds a hook that enforces a rule the instructions cannot
References
Section titled “References”- Addy Osmani, Ivar Soares Urdalen, Leo Simons. Memory and context: context engineering, memory kinds, memory versus RAG, context rot. Agent Engineer Course. Course.
AEC-05 - Anthropic. Extend Claude Code. Claude Code documentation. Reference.
Claude Code features - 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 - Anthropic. Best practices for Claude Code. Claude Code documentation. Reference.
Claude Code best practices - Anthropic. Hooks reference. Claude Code documentation. Reference.
Claude Code hooks