在 Claude Code 的 Stop hook 里,exit 1 和 exit 2 只差一个数字,差的却是「检查失败之后到底有没有人管」。Stop hook 挂在会话结束这个节点上,天然适合接 lint、类型检查、单元测试——任务收尾时跑一遍,不干净就退回给模型。坑在默认语义:以 exit 1 退出属于非阻塞失败,hook 报错了,Claude 照样收工走人。你以为拦住了,其实什么都没拦住。
要让流程真正闭环,得用 exit 2。这是一个明确的阻塞信号:此时 hook 写进 stderr 的内容会被原样喂回给 Claude,成为下一轮的上下文。模型看到「哪里错了」,自己判断改什么、怎么改,再跑一次检查。整个重试不需要你手写循环,也不需要额外调度,靠的就是这个退出码把控制权交回去。
两个细节值得说。第一,stderr 别输出整屏日志,给最关键的报错行——文件、行号、失败断言,噪声会稀释信号,模型反而抓不住重点。第二,想清楚终止条件:如果检查永远过不了,Claude 会不会在同一个坑里反复打转?给 hook 加个重试上限,或者至少让错误信息足够具体,指向真实的修复路径。一个退出码的差别,把「跑完就算」变成「跑对才算」,改动成本极低,收益直接落在交付质量上。

