问题
同一段检查你用在了两个地方。一个是装在编辑器里的钩子:每次保存前看一眼这次改动,提醒有没有违反团队规范。另一个是构建服务器上的一道门:命令跑起来之前看一眼这条命令,决定放不放。
服务挂了半个小时。编辑器钩子那边,你当初写的是出错了当没看见,跳过检查接着跑,于是这半小时里每一次保存都没被检查,而且没人知道。构建服务器那边,你写的是出错就拦,于是这半小时里所有命令全被拒绝,值班的人整晚都在解释为什么。
你去找那个「正确的默认值」,结果根本没有。同一段判断,两边正确的答案是相反的。编辑器里的钩子必须放行,因为一个挡着人干活的工具活不过中午就被卸了;那道门必须拦住,因为它后面再没有第二道防线。方向不是技术问题,是「你宁可打扰谁」的问题。
解决方案
常开常闭模式说:写错误处理之前先回答一个问题——打断的代价和漏过的代价,哪个大?然后把这个答案放在看得见的地方,做成调用旁边的一个设置,而不是压在报错处理那几行里、谁也看不见的一句。
把它做成一个参数,于是同一段检查在编辑器里写着「出错就放行」,在构建服务器上写着「出错就拒绝」。这还有个好处:这个争论只需要吵一次,方向白纸黑字写在那儿,随时能指给人看。
配套还有两个习惯。一是连着出错就先停一分钟别再发了,这一分钟直接按写好的方向走,免得每次保存都白等三秒;鉴权这种一看就不会自己好的错误,第一次就该停。二是数一数门卡在那个方向的时候,放过了多少、拦下了多少——「挂了半小时」和「挂了半小时并且放过了 3800 条消息」是两起不同的事故。
问 · 答 · 拍
- 问照常把材料和题目发出去,只是多带两样东西:你最多愿意等多久,以及什么都没回来的时候往哪边倒。
- 答三秒过去了,什么也没回来。
- 拍按事先说好的方向办。这台是没人值守的门,于是这条命令被拒绝,日志里留一行;同一段代码装在编辑器里的话,这一行写的就是放行。
名字从哪来
这名字来自锁。停电的时候,逃生门必须自己弹开,让人跑得出去;金库门必须自己锁死,让人进不来。没有哪一种在所有场合更安全。你先想清楚自己更怕把谁困在里面、或者放谁进来,然后再挑锁。
什么时候用
总是。每次调用本来就有一个失败方向,不管你写没写下来;这条模式只是把它变成一行看得见、吵得起来的配置。
什么时候别用
没有。只有一种伪装要小心:凌晨三点、没人值班的「升级给人」,其实就是拒绝。那就直接写成拒绝,别骗自己。
做这件事的项目
- jev-axi — 为 TypeSafe 的 Jev 提供的 CLI:从 shell 快速获得校准判断。
- mastra-jev-moderation — 在 Jev 上为 Mastra 做输入审核:一个文件,一次类型化请求。
- foreman — 使用决策模型的智能体监督者与软件工厂工头。
看谁的代码
- jev-axi
src/commands/hook.ts:106-129方向就是命令行上的一个开关:出错时放行、追问,还是拒绝。编辑器钩子默认放行,无人值守的门默认拒绝——同一段判断,默认值相反。
- mastra-jev-moderation
jev-moderation.ts:232-241连着三次失败就停调六十秒;这一分钟里一个请求也不发,全部按写好的方向走,并留一行日志。
- foreman
src/foreman/runtime.py:243-252评估失败一律变成「升级」。这台是没人盯着的,所以它失败时倒向「停下来」,而不是倒向「接着跑」。
想看代码
async function guard(state, questions, onError) { // 钩子传 "allow",门传 "deny"
try {
const { answers } = await jev(state, questions, { timeoutMs: 3000 });
return decide(answers);
} catch {
return onError; // 超时、限流、服务挂了,都落到这一行
}
}
const verdict = await guard(state, questions, "deny");
if (verdict === "deny") return refuse(command);jevbooks 怎么认出它
我们怎么知道一个项目用没用这一招?拿它的 README 问 Jev 一句话,Jev 给个百分比,数字越高说明越像。你要抄这道题,记住一点:别问「这个项目好不好」,要问「有没有讲到这一件具体的事」。
{
"failure-mode-knob": {
"type": "noul",
"instructions": {
"what": "Does the text state what happens when Jev errors or times out: allow, deny, escalate, circuit breaker?",
"not_for": "No stated behaviour on error."
},
"criteria": {
"true": "The text explicitly describes this. One sentence is enough; other content does not cancel it.",
"false": "The text does not describe this."
}
}
}