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