Coding agents: A silent hook babysits, a loud hook teaches A developer distinguishes between silent hooks that fix mechanical issues and loud hooks that teach agents by rejecting incorrect actions. The loud reject pattern is demonstrated with a Flyway migration naming hook that blocks invalid filenames and explains the rule, forcing the agent to learn the correct naming convention. The developer provides a decision table for choosing between silent fix, loud fix, and loud reject based on whether the fix is mechanical or requires agent knowledge. Hi friends Third post in a series about setting up coding agents in a real codebase part 1: structuring CLAUDE.md, skills and agents https://dev.to/hash01/how-to-structure-claudemd-skills-and-agents-2p7a , part 2: skill descriptions https://dev.to/hash01/claude-skill-description-o9n . This one is about hooks, and the difference between a hook that fixes and a hook that teaches. A hook is code that runs on the agent's actions - the surface for rules that must never be skipped. Instructions can be rationalized away "it's just a filename, close enough" ; a hook can't. But hooks come in two flavors: Silent is fine when the fix is mechanical and always correct: we run prettier from a hook after every turn, the agent never learns to format, nobody cares. The interesting case is when the fix needs knowledge the hook doesn't have . Then loud is mandatory. Our migrations use Flyway, and the file name IS the API: V012 Billing AddInvoiceIndex.sql Get it wrong and nothing crashes on your machine - Flyway just skips the file or orders it wrong, and you find out in a deploy. It's exactly the kind of rule agents violate: the SQL is perfect, the filename is add invoice index.sql . Could a hook silently rename it? No. A rename needs the next version number, the right schema, a description - knowledge that lives on the agent's side. A silent fix here isn't just pedagogically worse, it's less correct . So the hook's job is to refuse, and say why: bash /usr/bin/env bash PreToolUse hook on Write|Edit: enforce Flyway migration naming. set -euo pipefail input=$ cat file=$ python3 -c 'import json,sys; print json.load sys.stdin .get "tool input",{} .get "file path","" ' <<<"$input" "$file" = /db/migrations/ && exit 0 name=$ basename -- "$file" if "$name" =~ ^ VB 0-9 {3} A-Za-z A-Za-z0-9 A-Za-z0-9 +\.sql$ ; then { echo "Blocked: '$name' is not a valid Flyway migration name." echo "Expected: V<3-digit-version