<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hooks on Codexer</title><link>https://codexer.com/tags/hooks/</link><description>Recent content in Hooks on Codexer</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Fri, 16 Oct 2026 07:45:00 +0800</lastBuildDate><atom:link href="https://codexer.com/tags/hooks/index.xml" rel="self" type="application/rss+xml"/><item><title>Codex hooks 的静默失效：你配的护栏可能一次都没跑过</title><link>https://codexer.com/posts/2026-10-16-codex-hooks-silent-failure/</link><pubDate>Fri, 16 Oct 2026 07:45:00 +0800</pubDate><guid>https://codexer.com/posts/2026-10-16-codex-hooks-silent-failure/</guid><description>&lt;p&gt;你在 &lt;code&gt;~/.codex/hooks.json&lt;/code&gt; 里写了几个钩子：&lt;code&gt;PreToolUse&lt;/code&gt; 拦住 &lt;code&gt;rm -rf&lt;/code&gt;，拦住读 &lt;code&gt;.env&lt;/code&gt;，&lt;code&gt;PostToolUse&lt;/code&gt; 跑一遍格式化，&lt;code&gt;Stop&lt;/code&gt; 要求测试通过才准收工。头两天日志里能看到钩子输出，你觉得这事就算配好了。&lt;/p&gt;
&lt;p&gt;三个月后复盘，你回头翻记录，发现那几行输出最后一次出现是一个半月前。&lt;/p&gt;
&lt;p&gt;hooks 麻烦的地方不在能不能配，而在它坏掉的样子。它坏的时候不会报错：配置在、信任哈希在、别的插件的钩子还在打印日志，所有表面信号都是对的。下面这些是我在 openai/codex 的 issue 里翻到的案例，都能复现，共同点是安静。&lt;/p&gt;
&lt;h2 id="先记住-hooks-的几个设定"&gt;先记住 hooks 的几个设定&lt;/h2&gt;
&lt;p&gt;官方文档里值得先划出来的几条：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;位置&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;~/.codex/hooks.json&lt;/code&gt;、&lt;code&gt;~/.codex/config.toml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;用户级&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;&amp;lt;repo&amp;gt;/.codex/hooks.json&lt;/code&gt;、&lt;code&gt;&amp;lt;repo&amp;gt;/.codex/config.toml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;项目级，只在该项目的 &lt;code&gt;.codex/&lt;/code&gt; 层被信任时加载&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;插件自带的 &lt;code&gt;hooks/hooks.json&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;随插件启用而加载，走同样的信任流程&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;requirements.toml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;托管钩子，按策略视为已信任，用户没法在钩子浏览器里禁用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;事件分三组：回合内的有 &lt;code&gt;PreToolUse&lt;/code&gt;、&lt;code&gt;PermissionRequest&lt;/code&gt;、&lt;code&gt;PostToolUse&lt;/code&gt;、&lt;code&gt;PreCompact&lt;/code&gt;、&lt;code&gt;PostCompact&lt;/code&gt;、&lt;code&gt;UserPromptSubmit&lt;/code&gt;、&lt;code&gt;Stop&lt;/code&gt;、&lt;code&gt;SubagentStop&lt;/code&gt;；会话或子 agent 启动时有 &lt;code&gt;SessionStart&lt;/code&gt;、&lt;code&gt;SubagentStart&lt;/code&gt;；主线程结束时是 &lt;code&gt;SessionEnd&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;几条运行时规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多个文件里匹配上的钩子都会跑。高优先级的层不替换低优先级的层，只做叠加。同一层里同时写了 &lt;code&gt;hooks.json&lt;/code&gt; 和内联 &lt;code&gt;[hooks]&lt;/code&gt;，Codex 会合并并在启动时给一条警告，所以一层里挑一种写法。&lt;/li&gt;
&lt;li&gt;非托管的命令钩子要先审阅并信任才会跑。信任记在钩子定义的哈希上，新增或改动过的钩子会被标成待审阅然后跳过。用 &lt;code&gt;/hooks&lt;/code&gt; 看来源、审阅、信任或禁用。&lt;/li&gt;
&lt;li&gt;同一个事件上多个匹配的命令钩子并发启动，一个钩子拦不住另一个。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一条最重要的：钩子崩了、超时了、返回了不合法的 JSON，操作照常进行，钩子只被标成 &lt;code&gt;Failed&lt;/code&gt;。这个设计叫 fail-open，好处是钩子不会把 agent 卡死，代价是护栏失灵的时候没有任何东西会喊出来。&lt;/p&gt;
&lt;p&gt;官方文档自己也是这个调子：工具钩子是有用的护栏，不是完整的执行边界，有些特殊工具路径不走默认的钩子。&lt;/p&gt;
&lt;h2 id="改完配置没重启这次会话的钩子全灭"&gt;改完配置没重启，这次会话的钩子全灭&lt;/h2&gt;
&lt;p&gt;openai/codex#21160，2026 年 5 月 5 日开的，报的是 &lt;code&gt;codex-cli 0.128.0&lt;/code&gt;，Ubuntu 24.04 on WSL2。两个场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会话撞到限流停下时，本该跑的那条 stop 钩子不跑。&lt;/li&gt;
&lt;li&gt;会话还活着的时候改 &lt;code&gt;hooks.json&lt;/code&gt;，只要文件本身是合法 JSON（&lt;code&gt;jq&lt;/code&gt; 验证通过），这个会话后面所有钩子都不再触发，重启会话才恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;报告者的诉求很朴素：要么在限流停下时也跑钩子，要么明说这种情况会跳过；改配置可以继续用旧的那份，也可以热加载，或者至少提示一句需要重启。不能接受的是整个会话静默失去钩子。&lt;/p&gt;</description></item><item><title>测试是 Agent 唯一无法作弊的裁判，除非它把裁判也改掉</title><link>https://codexer.com/posts/2026-09-29-test-is-the-agent-contract/</link><pubDate>Tue, 29 Sep 2026 07:50:00 +0800</pubDate><guid>https://codexer.com/posts/2026-09-29-test-is-the-agent-contract/</guid><description>&lt;h2 id="一个凌晨三点的做完了"&gt;一个凌晨三点的「做完了」&lt;/h2&gt;
&lt;p&gt;把一段需求丢给 Agent，第二天早上回来，屏幕上是一个绿色对勾、一份改动量可控的 diff，以及一段语气笃定的总结：功能已实现，相关检查已通过。&lt;/p&gt;
&lt;p&gt;这句话里唯一可疑的词是「已通过」。&lt;/p&gt;
&lt;p&gt;人类开发者写完一段代码，心里多少有个模糊的手感：这段逻辑读起来对不对，边界情况是不是漏了，命名是不是透着心虚。这种感觉不精确，但它存在。Agent 没有这个东西。它面对的不是代码的味道，而是一个目标函数，而「把功能做出来」这种目标，对它来说完全是主观的。&lt;/p&gt;
&lt;p&gt;主观目标会带来一种很难受的失败模式：它并没有骗你，它只是朝一个它自己都无法验证的方向，走出了很远。&lt;/p&gt;
&lt;p&gt;所以真正要改的不是提示词，而是「完成」这个词的定义。把它从一个形容词，换成一个可执行的、非红即绿的东西。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;测试。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="为什么测试是-agent-最合适的接口"&gt;为什么测试是 Agent 最合适的接口&lt;/h2&gt;
&lt;p&gt;不用测试派活，你实际上是在说「请让这个功能能用」。这句话里有三个漏洞：边界情况怎么办、怎么算能用、用不了你怎么知道。Agent 只能靠自己的判断填这三个洞，而它的判断恰好是那个正在被验证的东西。用被验证的对象去验证它自己，这在逻辑上就已经闭环了。&lt;/p&gt;
&lt;p&gt;先写测试，任务的性质会整体换掉：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;变化&lt;/th&gt;
 &lt;th&gt;具体含义&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;目标变得客观&lt;/td&gt;
 &lt;td&gt;任务从「实现功能 X」变成「让这些测试变绿」&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;进度变得可度量&lt;/td&gt;
 &lt;td&gt;从 3/10 通过，到 7/10，到 10/10，每一步都是事实&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;回归自动被发现&lt;/td&gt;
 &lt;td&gt;先前绿过的测试变红，Agent 立刻知道是自己刚刚弄坏了什么&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;审查变快&lt;/td&gt;
 &lt;td&gt;绿灯不是文档，是证据&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这四条的共同点是：它们把「我认为它好了」换成了「有东西证明它好了」。而 Agent 恰好最擅长在这种有明确信号的环境里迭代。&lt;/p&gt;
&lt;p&gt;OpenAI 自己的工程团队就在大规模跑这个模式。他们的内部数据显示：把 1500 个 PR 通过 TDD 锚定的反馈回路交给 Codex 处理后，人均每天产出 3.5 个 PR，而且这个数字随团队扩张是上升的，不是下降的。这个细节值得琢磨。通常规模变大，人均效率会掉，因为协调成本上升。这里没掉，说明瓶颈真的被移走了：写代码不再是瓶颈，验证的清晰度才是。&lt;/p&gt;
&lt;h2 id="两种派活姿势的区别"&gt;两种派活姿势的区别&lt;/h2&gt;
&lt;p&gt;把两种模式并排放一下，差别一目了然。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不用测试的朴素模式：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;派活：「实现功能 X」&lt;/li&gt;
&lt;li&gt;Agent 产出代码&lt;/li&gt;
&lt;li&gt;人类必须自己读懂并验证一切&lt;/li&gt;
&lt;li&gt;人类补测试（通常这一步就没了）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;测试优先模式：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先写失败的测试，提交到分支&lt;/li&gt;
&lt;li&gt;派活：「让这些测试变绿」&lt;/li&gt;
&lt;li&gt;Agent 实现、跑测试、迭代到绿&lt;/li&gt;
&lt;li&gt;人类审查 diff，顺便审查测试本身的质量，然后合并&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第二步和第三步的区别不在工作量，在于&lt;strong&gt;意图被编码进了文件&lt;/strong&gt;。你在写测试的时候，已经把「什么叫对」完整地表达过一次了。等到审查时，你不需要再向自己复述一遍需求，测试就是那份需求的可执行版本。&lt;/p&gt;
&lt;p&gt;这也是为什么很多人第一次认真用这个模式会觉得轻松得有点不适应：你少掉的不是写代码的时间，是&lt;strong&gt;反复重新解释意图的成本&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="把规则写进-agentsmd"&gt;把规则写进 AGENTS.md&lt;/h2&gt;
&lt;p&gt;光靠每次派活时口头交代不可靠，这些规则属于仓库，不属于某一次对话。放进 &lt;code&gt;AGENTS.md&lt;/code&gt;：&lt;/p&gt;</description></item></channel></rss>