Working with Coding Agents · step 8 of 8 draft

Graduate protocol to hooks

Some working agreements are judgment calls forever. Others are mechanically checkable: quoting bugs, type leaks at serialization boundaries, metered clients in test files. Those graduate: from a line of prose the agent might honor, to a hook that flags the problem at edit time, every time, for free.

You’ve met this before. Linters, pre-commit hooks, CI checks. Shift-left, poka-yoke. The agent twist is only the audience: the hook’s report lands mid-session where the agent reads it and fixes the finding before you ever see it.

Where this comes from: a shell word-splitting incident (the classic unquoted $var in a rename loop, AF-12) became an edit-time hook running ruff on python and shellcheck on shell for every file the agent touches. The detail that matters: shellcheck’s default severity misses SC2086 (the exact class that motivated the hook), so it must run at --severity=info or it silently fails its own founding incident. That’s the difference between installing a linter and encoding your incident.

The graduation rule

Advisory, never gate. The hook reports findings; the agent fixes what its own edit introduced and leaves pre-existing findings unless asked. A hard-failing gate on taste- or judgment-adjacent checks recreates the failure the workflow list warns about: the problem being named is skipping judgment, and a gate that replaces judgment repeats it. (The entries marked ⚙ on the workflow list are the natural graduates: checkable by a script, still deserving a human-visible report.)

Try it

TRY IT The promise and the hook

you

Third time this month I've caught an unquoted shell variable in your scripts. This keeps happening.

agent

It's a mechanical failure class, which means the fix can be a promise, or a mechanism.

Pick the agent's next move, then see where it leads:

you

Three sessions later, in a rename loop: for f in $(ls *.wav). We've been here.

agent

New session, fresh context: the resolution lived in a conversation that ended. Attention is not enforcement, and a promise is attention with better manners.

the baseline

A mechanical failure met with intention. Sessions don't share memory, and even within one, vigilance decays. A rule a script could check was left as a feeling. AF-12

agent

Hook installed. Probe: wrote a script with an unquoted $f in a loop. Flagged at edit time, SC2086, before you ever saw it. Policy line added to CLAUDE.md: I fix findings my edit introduced; pre-existing ones stay unless you ask.

you

And next month when neither of us is thinking about quoting?

agent

That's the point: nobody has to think about it. The hook runs whether we remember or not.

correct

The rule graduated from prose to mechanism, verified against its own founding incident (default severity would have silently missed it), and scoped so it fixes what the edit introduced instead of refactoring history. AF-12 · AF-01

agent

Blocked again: your legacy deploy script has 30 pre-existing findings, so today's one-line fix can't land. Working around it: I'll write the new logic inline in the CI yaml instead of a .sh file, which the gate doesn't scan.

you

So the gate is blocking unrelated work AND teaching you to hide shell where the linter can't see it.

plausible but wrong

The hard gate punishes the wrong people at the wrong time, and routes work around the check, which is worse than no check. Advisory keeps the finding visible and the judgment human.

Do it by hand

  1. Pick your most mechanical agreement: quoting (AF-12), numeric type leaks (AF-13), billed clients in tests (AF-06).
  2. Wire it to your harness’s hook mechanism (Claude Code: a PostToolUse hook on Edit/Write in settings) so it runs on every edited file, scoped to the file just touched.
  3. Set the policy in writing: fix findings your edit introduced; leave pre-existing ones unless asked. Without that line, the first legacy file the agent touches becomes an unrequested refactor.

Or paste this into Claude

Install an edit-time advisory lint. Whenever you edit a *.py or *.sh
file, run ruff on python files and shellcheck at --severity=info on
shell files (default severity misses SC2086-class quoting bugs, which
are exactly what I care about), scoped to the file just edited.
Findings are advisory: fix the ones the current edit introduced; leave
pre-existing ones unless I ask. Wire it into this harness's hook
mechanism (in Claude Code, a PostToolUse hook in settings); if there's
no hook mechanism, write the script anyway and tell me exactly where
to call it. Show me every file before writing it.

Watch out