> 把不安全的默认值修在代码里,而说明书还在显式传那个值——跟着文档做会把修复绕过去
一个工具的文档里写着:
结论先行¶
一个工具的文档里写着:
nonexpert_review.py read <paper.tex> -o /tmp/body_only.txt
同一天它被派到十一个论文会话。三个会话同时往同一个路径写。 后果不是报错,是一个读者评审了另一篇论文,并交回一份看起来完美的报告。
代码里把默认输出路径改成按论文派生,这是对的修法。
但 SKILL.md 的第 2 步仍然显式写着 -o /tmp/body_only.txt,第 3 步还按那个名字去读。
于是:
照着文档操作的人,会用一个不安全的路径去覆盖那个刚被修好的安全默认值。 而且更糟 —— 一旦所有人都显式传同一个路径, 冲突在默认行为里再也看不见了,因为没人在用默认值。
十一个会话里有两个躲过了,原因是它们各自有把临时文件放进自己 scratchpad 的本地习惯。 项目自己的评语是准确的:那是运气,不是纪律。
为什么¶
修一个默认值只在"调用方不传这个参数"时有效。 文档里的每一个显式参数,都是对默认值的一次覆盖。 所以"改默认值"和"改文档"不是一件事的两面,后者是前者的前置条件。
这里的隐蔽性在于两次改动都"成功"了:代码里确实安全了,
--help 里确实是新默认值,自检大概也过了(因为自检不传那个参数)。
唯一的受害路径是人照着散文做,而散文不在任何测试的覆盖范围里。
这个项目后来把这条上升成了设计规则:
提示词/操作步骤必须住在工具里,不住在说明文档里。
三个读者的提示由 personas <id> 子命令输出,是唯一来源,
而自检双向枚举 —— 工具里的每个 persona 在文档里被提到,
文档里提到的每个 persona 在工具里存在,并且文档某个标题里的读者数量
等于 len(PERSONAS)。文档里只留名字,不留值。
同样的缝在这个项目里还出现在别处:
一条登记了的子原因背后的代码不存在、一条检查坐在 return 之后、
一个守卫读者口头同意然后无视。"声明了"和"生效了"之间的距离是同一回事,
见 指令写进 prompt 不等于它生效了——加一道"引文必须在原文里"的机械核验。
怎么用¶
grep 你的文档里有没有在显式传默认值。
这是一次性的、五分钟的审计:把文档里所有命令行里的参数值
和代码里的默认值比一遍,相同的那些应该从文档里删掉。
文档写 -o <path> 而不是 -o /tmp/body_only.txt,
就不会有人把修好的默认值覆盖掉。
改默认值的改动必须同时搜文档。 判断标准: 如果有人照着 README 粘贴命令,他拿到的是新行为还是旧行为?
共享路径的默认值要带上调用方的身份。 派生自输入文件、 进程 ID、或会话 scratchpad 都行。关键是默认情况下不会撞, 因为并发是外部条件,不是调用方能看见的东西。 这类事故的产物是"一份完美的报告,内容是别的对象的"—— 所以产物头部应当戳上来源路径和 mtime,让下游能看出不对。 见 仓里有四个稿件目录,一张没有出处的图传得很顺,然后到错的地方。
可执行的东西要有唯一来源。 提示词、阈值、路径、步骤顺序 —— 凡是会被照着执行的,就该从代码里导出,文档只引用它的名字。 再加一条双向枚举的自检,两边就不会各自漂移。
出处¶
~/Tools/nonexpert-review:CALIBRATION.md 的
「现场报告事后补的两条告诫」第一条(含"那是运气,不是纪律")、
与「三个值得保留的设计决定」第 3 条(提示住在工具里、自检双向枚举);
nonexpert_review.py 的 personas 子命令。