As a project grows, the same instructions start appearing again and again:
- Run the tests.
- Check the formatter and linter.
- Read the final diff.
- Make sure no test was weakened merely to get a green build.
- Tell me exactly what passed, failed, or could not be run.
That repetition is a signal. The procedure should stop living in chat history and become a reusable skill.
If there is one Claude Code skill worth building first, make it a verification skill. Code generation saves time, but verification protects the result. It turns "Claude says it is done" into "the project gates were executed, the change was inspected, and the evidence was reported."
Why Verification Is the Best First Skill
Without a defined verification procedure, quality depends on memory. Claude finishes a refactor, and you must remember to ask for tests. It fixes a bug, and you must remember to inspect the changed assertions. It updates configuration, and you must remember to check the generated files or run a build.
That works until the one time it does not.
A verification skill makes the expected finishing procedure repeatable. For an implementation, refactor, bug fix, migration, or configuration change, it can instruct Claude to:
- Identify what changed and what behavior the change was meant to preserve or introduce.
- Run the relevant automated gates.
- Inspect the final diff, not only the files Claude remembers editing.
- Review test changes for suspicious weakening.
- Report a clear
PASS,FAIL, orBLOCKEDresult with command output and limitations.
The important shift is that completion becomes evidence-based. A clean-looking diff is not enough. A green test suite is not enough if the tests were made meaningless. A verification result should connect the requested behavior, the changed code, and the observed checks.
A Skill Is Not a Guaranteed Completion Event
There is an important distinction to get right.
Claude Code uses a skill's description to decide whether the skill is relevant to the current task. A well-described verification skill may be loaded automatically when Claude is implementing or reviewing code, and you can always invoke it directly with a command such as /verify-change.
However, a skill is still model-invoked guidance. It is not a deterministic lifecycle event that is guaranteed to run after every file edit or before every response.
Use the layers deliberately:
| Requirement | Best instruction surface |
|---|---|
| Naming conventions, architecture facts, standard project commands | CLAUDE.md |
| A reusable multi-step verification procedure | A skill |
| A check that must execute at a lifecycle point | A hook |
| A merge requirement that must be independent of the coding agent | CI and branch protection |
This separation matters. Put the procedure in a skill because Claude needs reasoning: it must choose the relevant checks, interpret failures, inspect the diff, and detect suspicious test changes. Add a hook when forgetting the procedure is unacceptable. Keep CI as the independent final authority before merge or deployment.
What a Verification Skill Should Actually Verify
A useful verification skill checks more than whether one command exited successfully.
1. The requested behavior
Claude should restate the intended outcome in concrete terms. For a bug fix, that means identifying the failing case. For a refactor, it means naming the behavior that must remain unchanged. For a migration, it means identifying compatibility and rollback concerns.
This gives the later checks a target. Otherwise, Claude may prove that the code compiles without proving that it solves the requested problem.
2. Automated quality gates
The exact commands depend on the repository, but a typical set includes:
- Formatting checks
- Static analysis or linting
- Type checking
- Unit tests
- Integration tests
- Build or packaging checks
- Schema or migration validation
- Generated-file consistency checks
The skill should run the project's real commands rather than inventing generic substitutes. When the repository already exposes make verify, task check, npm run validate, or a similar command, use that as the primary entry point.
3. The complete diff
Claude should inspect the final repository diff after the checks run. This catches accidental changes, debugging output, generated artifacts, unrelated formatting, and modifications made indirectly by tools.
At minimum, inspect:
git status --short
git diff --check
git diff --stat
git diff -- .
If staged changes are part of the workflow, inspect git diff --cached as well.
4. Test integrity
A green test suite can still be bad evidence. Tests may have been weakened to accept broken behavior.
The verification pass should look for changes such as:
- Deleted or relaxed assertions
- New
skip,xfail, disabled, or ignored markers - Broader exception handling that hides failures
- Reduced coverage thresholds
- Snapshots updated without checking whether the new output is correct
- Mocks replacing the code path that the test was supposed to exercise
- Fixtures changed to avoid the edge case that exposed the bug
- Timing thresholds widened without a documented reason
- Expected values changed to match the implementation rather than the requirement
Changing a test is not automatically suspicious. A legitimate behavior change often requires new expectations. The skill's job is to verify that the change is explained by the requirement and still tests meaningful behavior.
5. Honest reporting
The skill must not convert missing evidence into a pass.
If Docker is unavailable, an external service cannot be reached, credentials are missing, or a test suite is too expensive to run locally, the result is BLOCKED or PARTIAL, not PASS.
A reliable report states:
- Which commands ran
- Their exit status
- Which checks were skipped and why
- What the diff review found
- Whether tests changed
- Whether those test changes preserved test strength
- Remaining risks
Build the Skill as a Small Folder
An Agent Skill is a directory with a required SKILL.md entry point and optional supporting resources. A verification skill benefits from separating reasoning from deterministic commands:
.claude/skills/verify-change/
├── SKILL.md
├── references/
│ └── test-integrity.md
└── scripts/
└── check.sh
This structure uses progressive disclosure:
- The skill metadata is available for discovery.
- The
SKILL.mdbody loads when the skill is activated. - Detailed references are read only when needed.
- Scripts can be executed as tools instead of copying their entire implementation into the conversation context.
Keep SKILL.md focused on the decision process. Put long checklists in references/ and deterministic project commands in scripts/.
Example SKILL.md
Create .claude/skills/verify-change/SKILL.md:
---
name: verify-change
description: Verifies code changes after implementations, refactors, bug fixes, migrations, and configuration updates. Use when source code, tests, build files, schemas, or runtime configuration changed and the work must be proven complete with executed checks and diff evidence.
allowed-tools: Read Grep Glob Bash(${CLAUDE_SKILL_DIR}/scripts/check.sh)
---
# Verify Change
Verify the completed change before describing it as done.
## Procedure
1. Restate the requested behavior and identify the files that changed.
2. Run `${CLAUDE_SKILL_DIR}/scripts/check.sh` from the project root.
3. Run `git status --short`, `git diff --check`, and `git diff --stat`.
4. Read the complete relevant diff, including changed tests and configuration.
5. If tests changed, read [the test integrity checklist](references/test-integrity.md).
6. Confirm that test changes validate intended behavior and were not weakened merely to pass.
7. Report `PASS`, `FAIL`, or `BLOCKED` using the format below.
Do not claim a check passed unless its command was executed and its result was observed.
Do not hide failing commands, skipped checks, warnings, or environmental limitations.
Do not modify code during verification unless fixing a discovered problem, then rerun every affected gate.
## Result Format
### Verification: PASS | FAIL | BLOCKED
- **Scope:** What was verified
- **Automated gates:** Command and result for each gate
- **Diff review:** Relevant findings
- **Test integrity:** Unchanged, strengthened, justified change, or concern found
- **Limitations:** Checks not run and why
- **Remaining risks:** Any behavior not covered by available evidence
The description contains both what the skill does and when it applies. That description is the discovery surface Claude uses when deciding whether to load the skill.
The allowed-tools rule is deliberately narrow: it pre-approves only the bundled verification script. Claude can still request permission for other shell commands when needed.
Example Verification Script
The script should contain the canonical commands for your project. Do not make Claude rediscover them on every task.
For a Python project using uv, Ruff, ty, and pytest, create .claude/skills/verify-change/scripts/check.sh:
#!/usr/bin/env bash
set -uo pipefail
failures=0
run_gate() {
local name="$1"
shift
printf '\n==> %s\n' "$name"
if "$@"; then
printf 'PASS: %s\n' "$name"
else
local exit_code=$?
printf 'FAIL: %s (exit %s)\n' "$name" "$exit_code" >&2
failures=$((failures + 1))
fi
}
run_gate "format" uv run ruff format --check .
run_gate "lint" uv run ruff check .
run_gate "types" uv run ty check
run_gate "tests" uv run pytest
if ((failures > 0)); then
printf '\nVerification failed: %s gate(s) failed.\n' "$failures" >&2
exit 1
fi
printf '\nAll automated gates passed.\n'
Make it executable:
chmod +x .claude/skills/verify-change/scripts/check.sh
For another stack, keep the same reporting shape and replace the commands:
| Stack | Typical gates |
|---|---|
| Node.js or TypeScript | npm run format:check, npm run lint, npm run typecheck, npm test, npm run build |
| Go | gofmt check, go vet ./..., go test ./..., go build ./... |
| Rust | cargo fmt --check, cargo clippy -- -D warnings, cargo test, cargo build |
| Java or Kotlin | Formatter, static analysis, unit tests, integration tests, package task |
| Terraform | terraform fmt -check -recursive, terraform validate, linting, policy checks, plan review |
Prefer one project-owned command such as make verify when possible. It gives developers, Claude, local tooling, and CI the same entry point.
Example Test-Integrity Reference
Create .claude/skills/verify-change/references/test-integrity.md:
# Test Integrity Checklist
Review every changed test and test-related configuration file.
## Blocking concerns
- Assertions were removed without replacement.
- Expected values were broadened only to match the implementation.
- Tests were skipped, disabled, marked flaky, or converted to expected failures.
- Exceptions are swallowed or failures are converted into warnings.
- Coverage thresholds were reduced.
- The implementation under test was replaced by a mock.
- A regression fixture no longer represents the reported failure.
## Changes that require justification
- Snapshot updates
- Timeout or retry increases
- New mocks or stubs
- Large fixture rewrites
- Deleted edge cases
- Changes from exact to partial matching
## Evidence of stronger tests
- A regression test fails before the fix and passes after it.
- Assertions cover externally observable behavior.
- New boundary and failure cases are included.
- The test remains independent of implementation details where practical.
- The changed expectation is traceable to the requested behavior or specification.
Report the exact files and lines that support the conclusion.
The main skill stays lean, while the detailed review criteria remain available when tests actually changed.
Test the Skill, Not Only the Script
A script can pass while the skill still performs poorly. Test both invocation and behavior.
Start with trigger cases that should activate the skill:
- "Refactor this service and verify the result."
- "Fix the bug and prove the tests still cover it."
- "Update the migration, then check everything before finishing."
- "Review these changes and tell me whether they are safe to merge."
Then test negative cases that should not trigger it unnecessarily:
- "Explain what this function does."
- "Find where this environment variable is used."
- "Summarize the README."
Finally, seed failure scenarios:
- Add a lint error and confirm the result is
FAIL. - Make a required service unavailable and confirm the result is
BLOCKED, notPASS. - Delete an assertion while keeping the tests green and confirm the diff review flags it.
- Add an unrelated generated file and confirm the final diff review notices it.
- Fix a discovered problem and confirm all affected gates are rerun.
The skill's description is part of the implementation. If it triggers too often or not often enough, revise the description and retest it.
Built-In /verify Versus a Project Verification Skill
Current Claude Code versions include a bundled /verify skill intended to build and run an application so Claude can confirm behavior against the running system, rather than treating tests or type checks as the only proof.
That is useful, but it does not eliminate project-specific verification:
- The bundled skill focuses on running and observing the application.
- Your project skill defines repository-specific gates and test-integrity rules.
- A custom skill can encode migrations, generated files, policy checks, contract tests, security scans, or domain-specific invariants.
Also note that project skills can override bundled skills with the same name. Naming your custom skill verify may intentionally replace the bundled /verify; naming it verify-change keeps both available.
A strong workflow can use both:
/verify-change
/verify
The first proves repository quality gates and diff integrity. The second exercises the running application when runtime verification is relevant.
Add a Hook When Verification Must Not Be Skipped
A skill defines the procedure, but a hook can enforce that something runs at a specific lifecycle point.
For example, a Stop hook runs when the main Claude Code agent is ready to finish. A command hook can block completion by exiting with code 2. The following wrapper skips clean repositories, runs the project verification script when changes exist, and blocks stopping when a gate fails.
Create .claude/hooks/verify-if-dirty.sh:
#!/usr/bin/env bash
set -uo pipefail
if [[ -z "$(git status --porcelain)" ]]; then
exit 0
fi
if .claude/skills/verify-change/scripts/check.sh; then
exit 0
fi
echo "Verification failed. Fix the failing gates before finishing." >&2
exit 2
Make it executable:
chmod +x .claude/hooks/verify-if-dirty.sh
Then add the hook to .claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/verify-if-dirty.sh",
"timeout": 600
}
]
}
]
}
}
This is a simple backstop, not a universal configuration. A repository with long-running tests, pre-existing local changes, multiple packages, or environment-dependent checks should scope the hook more carefully. You may run a fast subset locally and reserve the complete matrix for CI.
Do not use a model-based hook for a rule that can be checked deterministically by a script. Tests, formatting, generated-file checks, and forbidden-path rules are better expressed as command hooks. Use model reasoning for questions such as whether a changed test still represents the requirement.
Keep CI as the Independent Authority
Claude verifying its own work is valuable, but it is not independent verification. The same agent that made the change is interpreting the result.
CI should rerun the canonical gates in a clean environment and protect the branch from merge when required checks fail. Ideally, local scripts and CI call the same underlying command:
make verify
Then the layers align:
CLAUDE.mdtells Claude the project conventions and canonical command.- The verification skill defines how to run checks, inspect the diff, and evaluate test integrity.
- A hook prevents Claude from casually finishing with known failures.
- CI reruns the gates independently before merge.
Each layer solves a different failure mode. Do not force one instruction surface to do every job.
Common Mistakes
Treating green tests as complete proof
Tests cover only what they assert. Verification must also inspect changed tests, the final diff, build output, and the behavior requested by the user.
Putting the whole procedure in CLAUDE.md
CLAUDE.md loads as persistent context. Long task procedures compete with architecture facts and coding conventions. Move repeated multi-step workflows into skills so their full bodies load only when relevant.
Writing a vague description
description: Checks code is too broad and gives Claude little information about when the skill should activate.
Name the work types and the expected result:
description: Verifies code changes after implementations, refactors, bug fixes, migrations, and configuration updates by running project gates, reviewing the final diff, and checking that tests were not weakened.
Hiding failed or unavailable checks
A verification skill should make uncertainty visible. Missing dependencies, unavailable services, permissions, and timeouts belong in the result.
Running the full suite after every edit
Verification should happen at meaningful boundaries. A PostToolUse hook after every Write can create excessive noise and cost. Prefer targeted fast checks during implementation and a complete verification pass before completion.
Letting verification modify code silently
When verification finds a problem, Claude may fix it, but that starts a new implementation cycle. The affected gates must run again, and the final report must describe the correction.
The Rule of Thumb
If you have typed the same multi-step instruction twice, consider turning it into a skill.
Verification is the best first example because it improves every other task. Once the pattern works, the same structure can capture:
- Release checklists
- Database migration procedures
- Pre-pull-request reviews
- Dependency upgrade validation
- Security review steps
- Incident follow-up checks
- Deployment and rollback recipes
The folder can carry concise instructions, detailed references, executable tools, templates, and examples. Only the relevant depth needs to enter context.
Recap
A verification skill changes the definition of done.
Done is not:
- The code looks plausible.
- Claude says the task is complete.
- One test command printed green.
Done means:
- The intended behavior is explicit.
- The relevant gates were executed and observed.
- The complete diff was inspected.
- Changed tests were checked for integrity.
- Failures and limitations were reported honestly.
- Independent CI still has the final say.
Build the skill in .claude/skills/verify-change/, commit it with the repository, and give the whole team the same verification procedure. Add a hook when the procedure must not be skipped. Keep CI as the final enforcement boundary.
That is how repeated checking becomes part of the engineering system instead of something everyone has to remember.
References
- Claude Code: Extend Claude with skills - code[.]claude[.]com/docs/en/skills
- Claude Code: Hooks reference - code[.]claude[.]com/docs/en/hooks
- Claude Code: How Claude remembers your project - code[.]claude[.]com/docs/en/memory
- Agent Skills: Specification - agentskills[.]io/specification
- Anthropic: Public Agent Skills repository - github[.]com/anthropics/skills
- Anthropic Claude Code: Skill development guide - github[.]com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/skill-development/SKILL.md
