把架構決策寫成 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 名和網址碎片誤傷。寫出你的過濾規則,並用這兩條失效路徑當正例。