The workflow
- 01Which problem does each hook solve?
- 02How are the three events connected?
- 03What did the worked example return?
- 04What does the Stop check establish?
Use a PreToolUse hook to reject edits to generated files, a PostToolUse hook to regenerate outputs after source changes, and a Stop hook to run deterministic checks. In this example, direct script tests blocked an instruction mirror, allowed its source, and passed repository consistency checks; a complete Claude-driven lifecycle session was not rerun.
Which problem does each hook solve?
A generated file can look like the easiest place to make an edit. The change appears to work until the generator runs again and removes it. A prompt asking the coding agent to remember the source of truth helps, but a script can detect the specific mistake earlier and return a useful explanation.
The example repository keeps its source instructions in SKILL.md. Its supported client instruction files are generated mirrors. The guard protects those mirror paths and tells the agent to edit the source. This is a repository-maintenance example used in an iOS tooling project, not a claim that every iOS app should generate the same files.
The second hook synchronizes mirrors after the source changes. The final hook checks that the repository is internally consistent before the turn ends. Each check has a small, inspectable responsibility. None is a replacement for compiling an app or exercising a simulator.
Anthropic's hook reference documents event configuration and hook inputs. Keep the client reference close when adapting a hook: input fields and event behavior should come from the client, while your file-protection policy should come from your project.
How are the three events connected?
The repository configures these command handlers:
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write|MultiEdit",
"hooks": [{"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/hooks/guard-generated-files.sh", "timeout": 10}]
}],
"PostToolUse": [{
"matcher": "Edit|Write|MultiEdit",
"hooks": [{"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/hooks/sync-mirrors-on-edit.sh", "timeout": 30}]
}],
"Stop": [{
"hooks": [{"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/hooks/verify-repo.sh", "timeout": 60}]
}]
}
}
The quoted project directory matters when a checkout path contains spaces. The command scripts must exist and be executable. Inspect them before enabling the configuration, and merge the hooks with existing settings instead of overwriting permissions or other handlers.
Do not copy these paths into an unrelated app and expect them to work. An app might protect generated API models or an Xcode project derived from a specification. Its generator and verification command will differ. Start by naming the actual source file, generated outputs, and command that relates them.
What did the worked example return?
I sent the guard synthetic Edit input for AGENTS.md, using the checkout's absolute path. The process returned exit code 2 and a message explaining that the file is generated from SKILL.md. It named the source file and the synchronization command. No edit was performed; this test exercises the decision script directly.
I repeated the input with SKILL.md. The guard returned exit code 0 with no error. That pair tests both sides of the policy: a protected target is rejected, while a permitted target can proceed. Testing only the rejection could hide a guard that blocks all development.
Then I ran the configured verification script directly:
bash scripts/hooks/verify-repo.sh
It returned exit code 0. The recorded inputs and results preserve these checks and a successful direct PostToolUse synchronization run. Read the guard script, synchronization handler, and verification script before enabling them; they depend on a complete source checkout. The guard message, rather than a screenshot of a green badge, is the useful evidence because it shows the corrective instruction that would be returned.
What does the Stop check establish?
The repository documentation describes mirror synchronization, instruction frontmatter, referenced documentation paths, and subagent frontmatter as consistency checks. A successful run means those checks passed for this checkout at this moment. It does not mean Swift tests ran, an app launched, or every reference page is technically correct.
For an application project, select checks that match the work. A resource-only edit might need asset compilation and a build. A persistence change needs behavior tests. Avoid describing a successful documentation script as an app acceptance result merely because both run at the same lifecycle event.
The most useful failure output identifies the check, the affected path, and the repair command. A bare failure code forces the agent to rediscover the problem. Conversely, a message claiming a test ran when the script only checked metadata creates false confidence.
What can these hooks miss?
The configured matcher covers named editing tools. It is not a filesystem security boundary. A different write path, including a shell command, needs separate consideration. Path normalization, symlinks, and platform separators also deserve tests before extending this pattern across operating systems.
A PostToolUse action occurs after a tool has run, so it cannot retroactively prevent that write. Keep generated changes reviewable and retain CI checks even when hooks work locally. Client-side feedback and remote verification serve different points in the workflow.
Limits
This article verified standalone script behavior, not the current client's complete hook dispatch. The PostToolUse script also completed successfully when invoked directly with synthetic source-edit input; it was not exercised in a model session. The example protects instruction mirrors, not arbitrary Xcode outputs. Existing documentation contains broader descriptions of generated mirrors; use the tested paths and current script when deciding what is actually protected.
Last verified
September 16, 2026. Installed Claude Code 2.1.273; direct Bash/Python hook tests on macOS; repository verification exit code 0. Repository source: docs/orchestration/hooks.md; the downloadable record states the narrower tested scope.
Example project: ios-agent-skill.
