Skip to content
Pere Villega
Go back

Teaching Your Agent Standard Operating Procedures

15 min read

Skills promise to load a procedure only when it is relevant. That makes the activation decision part of the design: a good procedure provides no value when the model does not select it.

Scott Spence’s activation experiment makes the problem concrete. Different phrasing for related tasks produced different activation rates, and an explicit evaluation hook improved the result in his test set. The lesson is not that every project needs the same hook, but that activation must be measured rather than assumed.

eferro, a practitioner with decades of engineering experience, puts the upside plainly: with CLAUDE.md, your TDD guidelines, your Docker best practices, your refactoring workflow all sit in context permanently, competing for the window whether relevant or not. Skills solve this differently. CLAUDE.md loads everything. Skills load only when needed. That’s the promise.

The problem is the word “needed.” Who decides?

What Skills Are

Skills are Standard Operating Procedures as markdown files that guide agent behaviour. Think of them as runbooks the agent follows. Not vague guidelines, but step-by-step procedures for specific tasks. Type /mutation-testing and the agent inherits expertise in mutation testing. The rest of the time, that knowledge stays out of the way.

The Agent Skills specification formalises the structure. A skill is a directory containing a SKILL.md file with YAML frontmatter: a required name (max 64 characters) and a required description (max 1024 characters). Alongside it you can have scripts/ (executable code), references/ (on-demand documentation), and assets/ (templates, schemas). The spec recommends keeping SKILL.md under 500 lines and the main body under 5,000 tokens, with heavier material in referenced files.

Skills follow the Progressive disclosure pattern, in three tiers. Metadata (~100 tokens of name + description) is loaded at startup for every available skill. The SKILL.md body is loaded only when the skill activates. Everything in scripts/, references/, and assets/ is loaded on demand. Anthropic’s announcement states it plainly: “Only loads what’s needed, when it’s needed.” The name and description are the entire budget the model has to decide whether a skill is relevant. Every other byte is invisible until after activation.

That is the trade-off. CLAUDE.md is always in the room, shouting every rule at every prompt. A skill is a folder the agent might remember exists. If the metadata is not good enough, or doesn’t include some rule you care about, that rule may well never fire. There is a balancing act between the content in the body of a skill and what goes into the metadata section.

But for many use cases, a skill plus a CLI tool is lighter and more context-efficient than an MCP server. Skills give you the extensibility of MCP without the persistent token bill. This connects to the previous chapter’s argument about context.

Skills vs Commands vs Rules vs Subagents

Let’s start with Commands: Anthropic now describes custom commands as merged into Skills. Existing files under ~/.claude/commands/ still work, but the Skills format is recommended. Skills can still be triggered via a slash command, can load on demand, and support additional files and invocation controls. You can also configure a skill so that only the user can trigger it, preserving the useful part of the old command model.

Skills, Subagents, and Rules have some overlap, and the distinctions trip people up, as they are not clear-cut.

Skills live in ~/.claude/skills/. They are multi-file workflow definitions activated when the agent (or a hook, or the user) decides they’re relevant. They can trigger complex flows and use external CLIs during execution. They provide reference material to increase knowledge for a task, or reusable workflows that should be executed in the same manner each time.

Rules in ~/.claude/rules/ are markdown files Claude should always follow, split by concern (security.md, coding-style.md, testing.md). They are an extension of your CLAUDE.md, broken by themes so that they are easier to maintain, and with some additional capabilities like path-dependent activation. They load every session, as they are essentially a complement to CLAUDE.md. This means that you should use them sparingly or you’re back to the context bloat you were trying to escape when you trimmed your CLAUDE.md. Do not use them for Standard Operating Procedures, and do not add to them knowledge the agent already has. Use them to indicate particularities for the project, for example specific rules related to security or to how tests must be organised.

Subagents in ~/.claude/agents/ are the heaviest tool: standalone files, each getting a fresh context window, optionally running in parallel. The main purpose is to delegate a task to a fresh context window, while allowing the main context to act as orchestrator. This reduces costs and improves performance (smaller context window used during the task), and they can load skills themselves. This is a source of confusion: do we need a skill that does research, or an agent for research?

The answer is nuanced; it depends. You may want to have a research skill so that you can trigger it on demand, at any point. But if you always end up using a separate context window for it, having it as an agent makes sense, as it avoids the agent having to find and load the skill for that very particular task. Just remember that subagents can load skills, so when in doubt, start with a skill.

A concrete case from last month. I was writing a security review workflow. First instinct: “this is a skill, security review is a procedure.” Then I realised the review had to read 40+ files across the repository. If I put that inside a skill running in the main thread, every file read would land in my main context window, and by the time the review finished I’d be at 60% capacity with nothing interesting done yet. So the security review became a subagent: fresh context, reads what it needs, returns only the finding list. The SOP (how to review, what to look for, the severity rubric) is still there, but it is loaded inside the subagent, not the main thread.

Summary that works for me: skills activate conditionally, rules load always, subagents run independently. Anthropic has a section on when to use each that may help make this clearer.

What a Good Skill Description Looks Like

This is where most people go wrong. I did too, for longer than I should have.

Dachary Carey analysed 234 skills from two groups: 218 community skills and 16 from Anthropic. Of the community set, 33% (71 of 218) failed validation outright. Anthropic’s collection was not clean either: 3 of 16 skills failed, with 8 errors and 39 warnings. The failures cluster:

The median SKILL.md body is 2,725 tokens. Oversized or badly structured supporting material can waste context or make the intended content harder for an agent to discover, even when the skill still triggers.

The description field is what determines whether the skill activates. You only get 1,024 characters. They have to say when to use the skill, not what it does in detail. Here is what bad looks like, and it’s not that much of an exaggeration:

---
name: tdd-guard
description: TDD testing unit integration behaviour-driven development test-first red-green-refactor assertions mocks spies stubs fakes quality software-engineering best-practices xunit junit
---

That is a keyword soup. The agent’s matcher has nothing to grip. Compare:

---
name: tdd-guard
description: Use when the user asks to write a new test, add a test, practise TDD, do red-green-refactor, or when a feature requires a failing test before any implementation. Enforces test-first discipline and rejects code written without a failing test first.
---

Triggers are named explicitly and the conditions are stated. A description like this is a reasonable hypothesis for prompts such as “add a test for parseSchedule” because “add a test” is an explicit trigger. The next step is to verify that hypothesis with an activation eval.

In Carey’s February 2026 snapshot, the obra/superpowers collection contained 14 skills, including brainstorming, writing-plans, executing-plans, and test-driven-development. All 14 passed her validator. The skills were lean and focused, with a median body well under the 5,000-token guideline. That validates their packaging and structure; activation still needs a separate test.

Proving the Skill Actually Fires

Writing a good description is half the job. Verifying that the description does what you think it does is the other half, and it is the half most of us skip.

Anthropic ships a skill-creator meta-skill. A skill for building skills. The March 2026 update added an eval capability: you give it a set of representative prompts, and it runs your skill against them, grading output with and without the skill loaded. It even includes a trigger-description optimisation loop that rewrites the description, tests variants against your prompts, and picks the one that fires most reliably. I wrote about the whole thing in more depth in the Skills 2.0 walkthrough, so I’ll keep this short. The important part is that you now have a way to measure instead of guessing.

A few caveats worth knowing before you reach for it.

Claude does not consult skills for short, simple prompts. If your eval set is full of one-liners like “format this data,” the skill won’t fire even with a perfect description, because the model just handles the task directly. Your prompts need enough complexity to cross the threshold where Claude actually pauses to consider whether a skill applies. Copy messy, half-formed prompts from your actual history: the ones with typos, missing context, and file paths. If you can’t find five real examples, you probably don’t need the skill yet.

The description budget bites here too. The trigger optimiser helps, but it’s fighting the 1,024-character ceiling. Anything past that is truncated, so front-load the key triggers.

One more framing worth borrowing. The skill-creator update splits skills into capability uplift (teaching the model something it can’t already do) and encoded preference (sequencing things it already can do, your team’s way). They need different evals. Capability skills need evals that detect model progress: if the base model starts passing without the skill loaded, the skill’s techniques have been absorbed into the model. Preference skills need evals that verify process fidelity: did it follow your sequence, not a plausible-looking alternative. Same tool, different question.

Worth remembering from my previous chapter: Vercel’s eval comparing skills to an 8KB compressed reference in AGENTS.md found the skill was never invoked in 56% of cases, and even with explicit instructions it only reached a 79% pass rate, while the AGENTS.md baseline hit 100%. Evals do not just tell you whether the skill works when it fires. They tell you whether it fires at all.

The Activation Problem

Using skill-creator will help you refine the description so that it triggers on the right prompt. With a caveat: the model you are using (not Claude, but the specific model) is the one deciding if it triggers. And even with a match, the model may decide not to load the skill.

Scott Spence tested skill activation across five configurations using sandboxed evals. On 22 straightforward cases, the unassisted configuration scored 55% and 50% across two runs, while a forced-evaluation hook scored 100% in both. On a harder set of 24 ambiguous, multi-skill, and irrelevant prompts, forced evaluation reached 75% overall accuracy. The hook helped in this harness; it did not provide universal certainty.

A useful finding is that explicit framework terminology correlated strongly with activation in this experiment. Generic or indirect phrasing activated the skill less often. This suggests that description wording matters, but it does not establish that activation is merely substring matching.

The forced-eval hook short-circuits the problem. It injects an instruction into the session: for every user prompt, enumerate each loaded skill, decide YES/NO whether it applies, then act. Spence’s bash script states bluntly: “You MUST call Skill() tool in Step 2. Do NOT skip to implementation.”

The hook script outputs an instruction on stdout, which Claude Code treats as context for that turn (per the hooks reference, UserPromptSubmit stdout is added as context Claude sees). The instruction asks the model to decide YES or NO for each available skill and invoke the matching ones before implementation.

Without an activation test, you do not know how often a skill is being considered. A forced-evaluation hook is one option when missed activation is costly, but it adds context and latency to every prompt. Measure that trade-off for the actual skill set.

One recommendation, though: if you use the hook, ask your model of choice (Opus 4.7 at the time of this writing) to evaluate the text and improve it. It can make optimisations to reduce token cost, according to its own capabilities.

Improving the Description

Consider a tdd-guard skill with a broad description: “TDD workflow, test-first development, red-green-refactor, testing best practices.” A request such as “add a test” is not named explicitly, so an activation eval may expose a missed trigger.

There are two possible fixes. Rewrite the description to name the observed triggers (“Use when the user asks to write a new test, add a test, practise TDD…”), using eval results rather than intuition. If missed activation remains costly, add a forced-evaluation hook and rerun the same cases.

Lesson I wish I’d learned earlier: write the description around the language people actually use, then test semantically similar prompts which do not repeat the obvious keywords. Spence’s harness behaved closer to keyword matching than semantic matching, but that is an observed limitation to measure rather than a complete model of activation.

Lifecycle Events for Workflow Enforcement

Skills tell the agent what to do. Command hooks can enforce specific lifecycle conditions in classical software; prompt hooks add another model judgement and should be treated accordingly.

Hooks fire at specific points in the Claude Code lifecycle. The official reference covers roughly 28 events. The handful you’ll actually use: PreToolUse (fires before a tool call; exit 2 to block), PostToolUse (after a tool completes; ideal for auto-formatting), UserPromptSubmit (before Claude processes the prompt; stdout is injected as context, which is how forced-eval works), and Stop (when the main agent finishes; ideal for verification gates).

At first glance, hooks seem a tool for advanced users with esoteric flows. In practice, they are essential even for simple workflows. They are the deterministic layer that constrains the stochastic agent. Let me show some examples of simple but powerful use.

For example, a PostToolUse hook can run Prettier on .ts/.tsx/.js/.jsx files after edits:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-on-save.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Example 2: a PreToolUse hook blocking .md creation unless the filename is README or CLAUDE.md stops the agent from scattering documentation files across the repository. (If you’ve ever returned to a branch and found six new markdown files you didn’t ask for, you know why this exists.)

Example 3: Trail of Bits uses what I’d call an anti-rationalisation gate. A Stop hook that reviews Claude’s final response and rejects it if Claude is rationalising incomplete work: claiming issues are “pre-existing,” deferring to unrequested “follow-ups,” listing problems without fixing them, skipping test and lint failures with excuses. Their hook uses type: "prompt" rather than type: "command", sending the hook’s prompt plus Claude’s response to a fast model (Haiku) that returns a yes/no judgment. If the judgment is “rationalising,” the reason becomes Claude’s next instruction: go back and actually finish. Worth stealing. Agents are remarkably good at explaining why they didn’t finish something.

Nick Tune went further, building hooks as a workflow adapter. The aggregate is a proper state machine: planning → developing → reviewing → committing. git commit is banned in the developing state; you can only transition to committing after reviewing. Hooks check whether a requested tool call is allowed in the current state and block it if not. State-specific instructions are injected at each transition. The workflow engine has 100% test coverage, because it’s a programming language, not a prompt. Sophisticated, but the move is simple: make invariants explicit, enforce them at the harness layer.

If writing hook configurations in JSON isn’t your idea of a good time, the hookify plugin lets you create hooks conversationally. Describe what you want (“block rm -rf outside the project directory”), get a validated configuration file placed in .claude/, active immediately.

By the way, you can be very granular with hooks, to the point of defining particular hooks that only exist inside a given skill or subagent. These hooks only live while the skill or subagent is active, but they let you build precisely targeted gates on the behaviour.

Workflow Systems

Put it together: skills as SOP definitions, hooks as state machines and enforcement layer, subagents for isolated context. Stacked up, this stops looking like “prompting” and starts looking like workflow engineering. The vocabulary changes with it.

Three projects are worth studying because they take the idea seriously. GSD (Get Stuff Done) wraps skills and hooks around a spec-driven development loop. ed3d ships Claude Code plugins as reusable workflow components you can compose. obra’s superpowers treats skills as the unit of reuse and hooks as the enforcement mechanism that keeps the agent on rails. Different framings, same move: codify the workflow once, let the agent execute it every time, instead of rolling the dice on interpretation.

Test a skill against prompts people actually use, including near misses and prompts which should not activate it. Measure the activation rate and context overhead. Use a hook when missed activation is costly, and rerun the eval when the model or harness changes.


Share this post on:

Previous Post
Building a Personal Brain OS for Your AI Agent
Next Post
The Great Agent Tooling Debate