把架构决策写成 lint
同一份 AGENTS.md 里,有命令的规则会在三台操作系统上亮红。只有路径的那条,重命名之后没人发现。
- 调用点是不是匿名字面量lib.rs L261
- 注释名字是否等于参数名lib.rs L222
- 被调方是不是 workspace cratelib.rs L177
- CI 是否三平台同时跑rust-ci.yml L174
- Markdown 路径是否存在AGENTS.md L35
- Feature 是否登记在穷尽表lib.rs L379
- 开发中特性默认必须关闭tests.rs L18
新人接到任务:改 MCP 工具调用。它打开 AGENTS.md,抄下第 35 行的路径。文件不存在。真实文件叫 connection_manager.rs,就在同一个目录。文档里那个带 mcp_ 前缀的名字,是一次重命名之后没改干净的残留。
出处:AGENTS.md 第 32 至 36 行;codex-rs/codex-mcp/src/connection_manager.rs 第 1 至 15 行
同一份文件里,位置参数少了 /*base_url*/,本地命令会红。改了 Cargo.toml 忘刷 Bazel 锁,CI 会红。第 35 行那条路径没有检查器。Markdown 不会自己核对文件在不在。
先改 API,让调用点自己能读。foo(false) 的读者必须跳到定义才能知道这个 false 管什么。改不了 API,才允许 /*param_name*/。lint 是退路。
出处:AGENTS.md 第 14 至 20 行
实现住在独立的 Dylint 库,当一次 rustc。类型解析完成后,才能拿到被调方的参数名。入口只看函数调用和方法调用,宏展开出来的直接跳过。
检查按这个顺序走。
1. 只查本仓库 crate,std 和 tokio 直接放过。
2. 注释从参数前的空隙、前 64 字节、参数文本自身三处找。
3. 名字不对报 mismatch。错注释不会再落到没写注释那条。
4. 没写时,方法名等于唯一参数名就豁免,例如 .enabled(false)。
5. 剩下的只拦匿名字面量。None、布尔、数字要写,字符串和字符放过。
出处:tools/argument-comment-lint/src/lib.rs 第 165 至 180 行;tools/argument-comment-lint/src/lib.rs 第 261 至 274 行
仓库入口把默认 Allow 的那条抬成 deny。CI 在 Linux、macOS、Windows 各跑一次,一台失败另外两台继续跑完。人在 macOS 上绿了,Windows 目标的宏展开若多出一处 None,第三台仍会拦住。
出处:.github/workflows/rust-ci.yml 第 164 至 187 行
调用点局部、名字可解析、误报能用豁免收住。换个语言,形状一样:先改名字,改不了就要求行内名字。TypeScript 用 ESLint,Python 用 ruff,都用得上。
第 35 行和第 265 行是同一种腐坏。app-server 指南还写着 v2.rs,当前是目录 v2/,下面拆成三十多个文件。文件靠近 800 行就要拆。拆了之后,指南里的单文件路径没人改。
出处:AGENTS.md 第 260 至 266 行
模块行数规则点名五个高频文件,四个已经越过 800,一个贴着 900。chat_composer.rs 按行计有 12859 行。仓库里没有数行数的命令。行数能数,CI 不数。一次改动是不是机械,机器做不好,所以 800 行上限停在评审。
出处:AGENTS.md 第 49 至 61 行;AGENTS.md 第 125 至 131 行
把规则分成两套来读。一套有命令或编译器,合并前会亮红。一套只能被人和评审读,漏看就过。路径是否存在本来最容易检查:抽出反引号路径,对仓库根做存在性判断。仓库没做。预算花在调用点可读性上,没有花在路径存在性上。
文档不会自己复查。能局部检查却只写在 Markdown 里,重命名和拆文件的那天,文字还在,对象已经搬家。最小形态是二十行脚本核对路径,不需要 rustc 插件。
特性开关如果只靠布尔和一篇说明,漏登记、开发中默认打开、Deprecated 一直待着,都不会第一时间亮红。
Feature 枚举旁边有一张 FEATURES 表。FeatureSpec 把标识、配置键、阶段、默认是否打开焊在同一行。表里找不到对应项就 unreachable!。枚举多一个变体、表少一行,运行到 key() 会直接崩。
出处:codex-rs/features/src/lib.rs 第 41 至 58 行;codex-rs/features/src/lib.rs 第 819 至 826 行;codex-rs/features/src/lib.rs 第 379 至 384 行
旁边两道测试锁住默认值。开发中的特性默认必须关闭。默认打开的特性,阶段只能是 Stable 或 Removed。阶段有五态,多出来的 Experimental 带着菜单名和公告。Deprecated 没有过期日,三个 Deprecated 项仍能打开。阶段能表达不该再用,不能表达下个版本删。
出处:codex-rs/features/src/tests.rs 第 17 至 28 行;codex-rs/features/src/tests.rs 第 82 至 94 行
穷尽表加两条测试,换语言也成立。漏登记就崩,默认值被锁住。换不来自动删除,只换来这两条不变量。
DSH:每个包必须露面,空也要解释
DeepSeek Harness 把「每个包必须拥有 ./invariant」同时写成散文和门禁。散文在 packages/AGENTS.md。门禁是 21 行的 verify-package-invariants,失败就 process.exit(1)。空安装器必须带固定前缀 No runtime invariant:。空是显式架构结论,以后引入可变状态,必须换成真正的检查。
笔记回答为什么允许空,检查器保证空必须解释。两者缺一,就会回到 Codex 第 35 行那种状态:文字还在,对象已经搬家。DSH 没有 rustc 插件去管 foo(false)。Codex 没有穷尽式包门禁去管路径存在性。
出处:packages/AGENTS.md 第 18 行;scripts/verify-package-invariants.ts 第 1 至 21 行
Grok:能局部化的决策直接丢进 clippy
Grok Build 仓库根没有 AGENTS.md。它仍把一条架构决策写成 lint:clippy.toml 禁止 canonicalize,理由是 Windows 上会得到 verbatim 前缀,破坏 git、泄漏进模型上下文。执行边界写在同一份文件:这条禁令由各 crate 的 cargo clippy presubmit 执行,只走 Bazel 的 crate 要靠人看。
和 Codex 的参数注释是同一类判断:调用点局部、误报面可控。Grok 承认 Bazel 覆盖不全。Codex 承认本地只跑当前操作系统。小团队先抄路径存在性和 21 行 verify 脚本,比抄 Dylint 便宜。
出处:clippy.toml 第 9 至 28 行
先做哪一道自动检查
AGENTS.md 第 35 行和第 265 行都是失效路径。若你只能先做一道自动检查,你检查带 codex-rs/ 前缀的路径,还是检查所有反引号里含 / 的字符串?
第一种会漏掉 app-server-protocol/src/protocol/v2.rs 这种相对写法。第二种会把命令名、crate 名和网址碎片误伤。写出你的过滤规则,并用这两条失效路径当正例。