論述基礎與限制

一次對兩份規則文件(一份 496 行的寫作規範、一份 264 行的工具專屬補充)的理解取樣,十三個低階模型探針,指令逐字相同、其中一欄要求回報「照這一節工作時我的第一個具體動作是什麼」。這一欄產出了五處,全部是同一個形態。

限制:單次量測、單一專案、對象都是規則類文件。敘事型與教學型內容沒有測過,那裡的「執行者」不是同一種角色。五處之中有兩處是同一份文件的同一段歷史遺留,不算五個獨立的成因。

核心原則

一條規則的可執行性,由「照它工作的人第一個動作指得出來嗎」決定,不由它寫得清不清楚決定。 一條讀起來完整、每個字都懂、卻沒有指向任何打得開的東西的規則,執行與不執行在產物裡沒有差別。

失效有三種形態,成因不同而後果相同:

形態例子讀者看到的
名字指向不存在的東西規範首條寫「本檔(blog/codex.md)」,而該檔在改名後已不存在一個看起來很具體的路徑
名字指向的東西從來就不是實體「更新章節大綱(outline 為唯一 backlog)」,而 outline 不是任何檔案一個像是專有名詞的詞
根本沒指名任何東西「寫作規範掃描:核心先行、正向陳述、案例補足」三個形容詞,讀起來像在說做什麼

第三種最難發現,因為前兩種至少有一個錯的名字可以被查,而第三種沒有名字可查。它靠的是讀者用自己的知識把缺口補起來——每個讀「寫作規範掃描」的人都知道那大概是什麼意思,於是那一步讀起來成立。

為什麼規則文件特別容易留下這種缺陷

規則文件的讀者全部自帶答案。 讀 AGENTS.md 的人知道自己打開的是哪一個檔,所以「本檔(blog/codex.md)」讀起來沒有問題——名字錯了,而讀者用手上那份實體覆蓋掉了它。寫的人更是如此,寫的當下腦中有完整的地圖,步驟名稱只是那張地圖的索引。

補完發生在讀者這一側,而且無聲。因此這類缺陷可以通過任意多輪的人工審查與任意多個高階 reviewer:每一位都補了同一個缺口,也都沒有察覺自己補過。

第二個成因是改名。名字寫進規則的那一刻是對的,改名的那次提交動了實體而沒有動指向它的規則。這一種與死鏈的差別在於它不會報錯——路徑寫在括號裡的散文中,沒有任何工具會去解析它。

判斷標準

問一句話就分得開:照這一條工作,我的第一個動作是打開什麼檔、執行什麼指令、或叫用哪一個具名的東西。

答得出名字,接著驗那個名字現在還在不在(開一次、或 ls 一次,成本是秒級)。答不出名字,這一步就是壞的,不論它讀起來多完整。

三種修法對應三種形態:

  • 名字錯了:換成現在的名字,並在同一次提交裡搜全庫還有沒有別處沿用舊名。
  • 名字不是實體:把它換成實體。若那個概念確實沒有對應的實體,那代表規則要求的東西還不存在,處置是建立它或刪掉這條規則,不是換一個更好聽的名字。
  • 沒指名:把形容詞換成動作加對象。「寫作規範掃描:核心先行、正向陳述」改成「叫用某個具名的檢查集合,照它的清單逐類掃過,每個命中走一次語意判定」。

廢除紀錄要指名接手的機制

同一個問句管的不只是規則,也管廢除紀錄——刪掉一項檢查時寫的那句話。「職責已由其他機制覆蓋」在版本紀錄裡讀起來是一個完整的理由,而它沒有指名,於是接下來沒有人打得開任何東西。

代價落在下游而不落在寫的人身上。一次實測:兩個 hook 在本專案刪除,理由寫的正是那一句;而另一個消費端問起「這兩個是為什麼廢除的」時,而三份紀錄(版本紀錄、規範檔、刪除提交)沒有一份說得出接手的是誰。要回答那個問題得從 git 歷史把腳本取出來讀,才發現它們根本不是被取代的:一個檢查的是某種前端框架的事件處理函式,本專案沒有那種原始碼;另一個檢查某個目錄底下的表格,而本專案的目錄名不同。真正的理由是不適用,而「已被覆蓋」與「不適用」導向相反的處置——「已被覆蓋」要下游找到接手的機制跟著移除,「不適用」要把它留給還用得到的那一端。

第四種之所以會被寫下來,是因為它是「這東西不屬於這裡」最近便的說法。 上面那次的刪除判斷後來被獨立驗證是對的——那兩個 hook 的主題確實不屬於它們所在的 skill,同一個共用庫裡其餘七支帶 hook 的 skill,hook 主題全部與該 skill 一致。刪的人感覺到了不屬於,而寫下來的是「已被其他機制覆蓋」。直覺對而紀錄錯,往下傳的是紀錄。 所以卡在第四種的時候,要問的是自己感覺到的那件事還沒被說出來——通常它是「這一項的主題不在這裡」,而那句話本身就是一個合格的不適用條件。

寫廢除紀錄時三選一,不寫第四種:接手的是誰(指名,並確認那個名字打得開)、在本專案不適用(寫出不適用的條件,別人可以拿自己的環境去對)、有缺陷(寫出缺陷,因為還在跑它的人需要知道;寫不出缺陷時要分開寫「查過、沒有」與「本環境無法觸發其主要路徑、缺陷狀態未知」——目標集合為空的東西不會失敗,而不失敗會被讀成沒有缺陷,見 廢除一項共用資產時,它的前提與它的效果都在自己看不到的那一側)。「已廢除」「已由其他機制覆蓋」這類沒有指向的句子,等於把判斷成本轉嫁給每一個之後撞到它的人,而他們手上的資訊比寫的人少。

偵測

判斷標準可以自查,但自查對這一類的偵測率低,理由就是上面那條——查的人也自帶答案。

有效的偵測是把規則交給一個不持有那張地圖的讀者,並且明確要求他回報第一個動作。這一欄不能省略成「你讀懂了嗎」:讀懂與執行得出來是兩回事,而讀者的預設回答是讀懂了。低階模型在這裡特別適合,因為推理能力越強越會自動把缺口填上,填完之後那一步讀起來就成立了——判別與派發方式見 #281。

自動化只覆蓋得到一部分:寫成 markdown 連結的路徑進得了連結檢查,寫在散文或反引號裡的檔名、工具名、段名不會被任何工具解析。所以這一維的主力是探針與逐條人工核對,不是 lint。

沒這樣做的麻煩

規則存在而不被執行,跟規則不存在的差別,全部落在成本那一側:討論規則的時候大家都同意它在,出事的時候查得到它寫在那裡,而它從來沒有攔下任何東西。更貴的是它會關掉補救——既然規則已經有了,這一塊就被當成有人管了。

跟其他原則的關係

原則關係
#281 理解檢查用低階 model 取樣讀者的實際理解本卡的偵測入口——探針回報的「第一個具體動作」那一欄就是本卡的判斷標準,而本卡說明那一欄為什麼不能省
#240 路由的目的地要承接該主題同一條要求推到不同對象——#240 驗的是連結的目的地承不承接主題,本卡驗的是規則指名的東西存不存在;#240 的對象進得了連結檢查,本卡的對象寫在散文裡、任何工具都不解析
#266 可核對的錨要跟它該偵測的狀態連動兩條合起來才是完整的可執行性——本卡管規則指不指得到東西,#266 管執行完之後留不留得下可判偽的痕跡;只做本卡會得到一條指得到實體、卻仍然無法分辨做過沒做過的規則
#155 引用用語意標題、不用位置編號同一個失效機制的另一種載體——編號重排後引用句字面完好而指向錯的內容,跟改名後路徑字面完好而指向不存在的檔是同一種無聲失效,兩者都不報錯
#267 關鍵字清單只收違規義項佔多數的詞相鄰而不同層——#267 管一個清單收哪些成員,本卡管規則有沒有指到那個清單;一份規則可以在指得到清單的前提下,清單本身仍然收錯成員
  • 宣稱可機械執行的條文,要先把中文允許不決定的那幾項決定掉:同一個成因的兩個失效端。本卡是規則的指涉端(照它工作的第一個動作指不出來),那一張是判定端(照它工作的判定結果因人而異,因為基數與否定轄域中文允許不決定);共通的成因是規則文件的讀者全部自帶答案,補完發生在讀者那一側而且無聲。

判讀徵兆

  • 規則裡出現反引號包起來的檔名、目錄名、設定欄位名,而同一句沒有說它在哪裡。
  • 步驟的內容是形容詞或名詞的並列(「核心先行、正向陳述、案例補足」),沒有動詞加對象。
  • 同一個東西在文件的不同段落有兩個名字,而其中一個查不到對應的實體。
  • 探針或新加入的人反覆問同一個「這個 X 是什麼」,而回答的人每次都答得出來——答得出來說明資訊存在於回答者身上,不在文件裡。
  • 某條規則從來沒有攔下過任何東西,而大家都同意它是對的。

這條原則什麼時候不成立

概念層的陳述不適用。「Backend 是語言無關層」這種定位宣告本來就不指向實體,它的作用是劃邊界而不是驅動動作,要求它指名一個打得開的東西會把它逼成假的操作步驟。

判斷標準的成立條件是「這一段要求執行者做事」。純參考、術語定義、背景說明都不觸發。

指向外部規格與第三方文件的規則也只適用一半:名字要驗,但那個名字會改版、改路徑、下架而自己無法讓它保持有效,處置是連過去的同時寫明「到那裡要拿到什麼」。