論述基礎與限制

本卡從一次重演抽出。一個工作階段裡跑同一組掃描兩次,第二次整組回報零命中,而那個零來自 shell 的展開規則、不是來自內容。這個機制在本庫已經有兩份記錄:一張卡把它列進機制表(#291),另有一則 memory 專門記它,而那則 memory 每個工作階段都會載入上下文。兩份記錄都在場,重演照樣發生。

限制原本是單一個案、沒有對照組。後續有一個併行的批次提供了部分對照,記在下一段;它支持本卡的機制,而「補了前置步驟之後失效率如何」仍在證據範圍之外。可推翻的地方也明確:找到一次「因為讀過那份記錄而在事前避開」的實例,本卡對記錄載體的判斷就要收窄。

第二個個案(部分對照)。 另一次四輪審查裡,同一類失誤發生三次:reviewer 明寫某個 finding 是「全部命中:X 處加 Y 等處」,而修的人只改了它列出的第一處;下一輪換成兩張表中的一張。三次的動作差別只有一項——修完有沒有拿特徵字串再掃一遍。前兩次沒掃、失敗;第三次掃了、成功。而規定要掃的那條規則(#298)在三次之前就已經讀過,寫成的是原則。這一組的價值在於它把變因收窄到「動作有沒有執行」,而不是「記錄在不在場」——記錄在三次裡都在場。


核心原則

一份記錄要被讀到,讀的人得先有理由去打開它。而值得記錄的失效,多數的定義特徵正是當下沒有任何理由起疑。 卡片、memory、規範文件都是被查詢的載體——它們假設有人帶著問題來。這個假設對「已知有這回事、只是細節記不得」的情形成立,對「完全沒想到這裡會有問題」的情形不成立,而沒想到的那一種才是失效重演的主力。

同一件事寫成兩種形態,生效的條件不同。 寫成成因(「shell 不對變數做分詞」)的那一份,要在已經懷疑管道的時候才用得上;寫成動作的固定前置步驟(「掃描的第一列固定放一個已知會命中的字串」)的那一份,不需要任何人先起疑。兩份的內容可以完全一樣,而只有寫成前置步驟的那一份在最需要它的時刻會動。

判斷標準是一個問句:這條防線要生效,需不需要有人先想到它。 需要,它就只在最不需要的時候有用。答「不需要」的時候要指認那個觸發落在哪裡——哪一行、哪個欄位、哪一次執行的哪一段輸出;指認不到就記成「需要」。少了這一步,每一條防線都描述得出一個觸發點,而問句退化成一律答不需要。


那次重演的兩層成因

第一層是輸入形狀換了而驗證沒跟著換。 同一個階段裡,同一組掃描先用單一檔案跑過一次,回報的數字正常;第二次換成三個檔案,二十四類全部回零。單一路徑不需要 shell 分詞,多路徑需要——指令的輸入從一個變成多個時走的是另一條程式路徑,而第一次的成功恰好完全繞過那條路徑。第一次成功因此不是證據,卻正是第二次不設防的理由。

第二層是救回它的並不是那兩份記錄。 二十四類全部回零這個形狀太荒謬,所以引起了懷疑。這是後備防線而不是設計出來的防線,而它的可靠度隨類別數變化——掃三類的時候全零並不荒謬,同一個失效就會直接通過。後備防線在類別少的時候失效,而類別少正是它看起來最不需要的時候。


反模式

把「下次要記得」當成處置。 它把負擔放在未來某個沒有理由起疑的時刻,而那個時刻的定義就是不會想起這件事。同一件事第二次發生時,補一份更詳細的記錄多半不會改變第三次。

用同一個階段裡的一次成功當作指令已驗證。 驗證的射程由那次輸入的形狀決定,而形狀變了就換了一條路徑。這是 #291 的「沿用一條在別的資料上有效的指令」在最短時距上的形態——同一次工作、間隔幾分鐘,而正因為間隔短,「剛剛才驗過」的感覺最強。

依賴輸出的荒謬感。 它是免費的,所以值得留著;把它當成防線則是把可靠度交給批次大小這個與正確性無關的變數。


修法

把正對照放進輸出,而不是放進守則。 掃描的第一列固定是一個已知會命中的字串,它的計數與其餘各類一起印出來。這一列不是零,整批的零命中才算數;這一列是零,整批作廢。成本是一行,而它不要求任何人記得任何事——壞掉與乾淨在產物上就此分得開。

正對照要與被驗的掃描共用同一次呼叫、同一個輸入表達式。 另開一條指令先跑一次確認是最省力的遵循方式,而它會通過——那次失效的位置正是輸入的展開,另一條指令用的是另一次展開。同一個道理適用於任何要等待的前置條件:對一個還沒就緒的目標發出的查詢回的是空輸出而不是錯誤,而空輸出與「這一項不存在」在產物上相同。所以正對照那一行還要印出展開後的實際輸入——掃到哪幾個檔、連到哪一個目標、對方回報的版本是什麼——數量或身分對不上就與零命中同等處置。

同一個設計在別處已經有先例:術語探針同批混入已知通用的控制詞(#289),控制詞不收斂就整批作廢。兩者是同一件事——先用已知答案證明工具會動,再拿它去量未知。

把可執行的那一半搬到動作的路徑上。 記錄可以留在卡裡,而要生效的那一步得住進會被執行的位置——腳本的第一行,或產物必須包含的一個欄位。「在規範裡加一句『執行前先做 X』」不算搬過去:規範是被讀的載體,而本卡的核心原則正是被讀的載體要人先起疑。驗收是一個減法測試——把那句話從它現在的位置刪掉,產物會不會不同;不會,就還沒搬。

搬過去之後還有一個維度:那個動作本身有深淺。 「執行了」與「沒執行」不是二元的。第二個個案的三次其實是三個層級——不掃、掃了但只掃同形、掃同形再加逐項對照實例數;第三次執行了掃描並抓到同形殘留,仍然漏掉一處字面完全不同而承載同一個缺陷的地方。所以把動作寫進收尾清單只解決了「會不會做」,「做到哪一層」要由清單自己逐條列出來,寫成「掃一遍」的那一行會停在它最便宜的解釋上。

重演發生時,先問記錄的住址而不是記錄的內容。 同一類錯誤第二次發生而它已經被寫下來過,缺的通常不是細節。細節不足會表現成「照著做而做錯」,住址不對表現成「根本沒去做」,兩者的處置相反。


判讀徵兆

徵兆為什麼是訊號下一個動作
同一類錯誤第二次發生,而它已經有記錄缺的是觸發而不是內容找那條防線在動作路徑上的落點
一條防線的措辭是「要記得」「先確認」它把生效條件押在執行者當下的警覺上改寫成一個會留下產物的步驟
剛剛才用過的指令,這次換了輸入的形狀驗證的射程綁在輸入形狀上當成沒驗過
一批檢查全部回報最乾淨的那個結果這個形狀同時是「都通過」與「都沒跑到」正對照缺席本身就是徵兆,不必等結果看起來荒謬
處置寫成「下次注意」而沒有留下任何產物它在第三次發生時不會有任何差別換成一個可以在產物裡看到的欄位或步驟
執行者面對一組選項而選不出來或乾脆不用缺觸發與分類軸錯位在這裡症狀相同問他有沒有打開那份東西——打開了而對不上是軸錯了,沒想到要打開是缺觸發

這條原則什麼時候不成立

執行者在該動作當下會被觸發的場景不成立。 這裡要避開一個看起來成立而實際不成立的版本:「有明確前置閱讀要求的流程」不算——本卡那次重演的其中一份記錄是每個工作階段都會自動載入上下文的 memory,那比任何前置閱讀要求都強,而重演照樣發生。送達與觸發是兩件事,而分辨它們的證據是那一次執行留下的產物:產物裡看得到那一步發生過,才算被觸發。

只發生一次的事不成立。 本卡處理的是重演,而第一次發生時記錄下來仍然是正確的處置——沒有那份記錄,第二次連「這是重演」都認不出來。兩份記錄在本次的作用正是這個:它們讓成因在事後三十秒內就指認得出來。

算不算同一類,看機制而不看觸發的形狀。 本卡那兩次的觸發形狀不同(一次是沿用別的資料上有效的指令,一次是同一階段裡輸入從單檔變多檔),而機制是同一個:掃描回零而管道壞掉。用形狀判會把每一次重演都判成新的第一次,這個豁免就永遠成立;判定時要寫下引用的是哪一張既有卡。

代價低於防線成本的失效不成立,而代價算的是誤信之後的下游動作、不是重跑一次的成本。 這兩者差幾個量級,寫成後者的話這個豁免自己就成立了。判別直接沿用 #291:這個數字會不會決定接下來改什麼。不會,錯了重查一次就好,不值得投資;會,那個零被寫進報告當成通過之後的每一步都建立在它上面。


邊界:在路徑上、標題就是關鍵字、仍然沒有查

本卡處理的是記錄不在路徑上。與它相鄰而處置不同的一種是:記錄在路徑上、標題正好是撞到問題時會搜的字、實證逐字描述了眼前的情形,而動作之前沒有人去查。一次外部自陳同一天出現兩次重新推導既有 pattern,兩次性質不同——一次是記錄的標題限定在某個測試場景而找不到(泛化標題可解),一次是找得到而未查。第二種泛化標題無效,因為問題不在可尋性;它需要的是動作之前的一個觸發點,讓「查一下有沒有人處理過」成為那個動作的前置步驤而不是事後的懊悔。本卡的修法對第二種沒有效力,兩者要分開記。

這兩種與另一組失效成對:知識在庫裡而沒被取用(本卡與上一段),對比知識被取用了而它涵蓋的值域不夠(條文全部照做,缺口仍在條文的射程外)。前者改觸發時機,後者改內容值域,而兩者都不是執行紀律的問題——加強「請務必記得」對兩者都無效,因為第一種的人沒有想到要記、第二種的人記得而且做了。

第三種由併行 session 的兩個實例補上,它讓上面那組不只兩個:知識取用得到、也在路上,而它的分類軸不是執行者手上的軸。那個實例是一組豁免標記的類別名——測試資料/示意/搬遷,三個都描述這段文字打哪來,而執行者當下知道的是它在這一句裡的用途(這個字面是被使用還是被談論)。症狀與第一種相同(人選不出來、或乾脆不用),歸因不同:第一種缺觸發,這一種觸發到了而問錯了維度。分辨的方法是問他有沒有打開那份記錄——打開了而對不上分類是軸錯了,沒想到要打開是缺觸發;兩者不可因症狀相同而共用修法,重新命名對缺觸發無效,改落點對軸錯位無效。

**第四種讓這一組滿四個,而它的讀者做完了前三種要求的每一件事:打開了那份文件、逐字讀完、還把其中一款抄進自己的依據句,行為仍然不變。**成因是那一款在他的程序裡來得太晚——位置落在他取值的那一步之後。本卡的修法對它沒有著力點(住址已經對了),泛化標題也無效(他根本不需要搜尋),補值域同樣無效(值域已經涵蓋了)。分辨的動作是查引用:那一款在讀者的產出裡出現過嗎——出現過而行為沒變就是第四種,處置是把它往前搬到讀者取值的那一步之前。判斷標準與五個實例見 #348 規範文字的效力由它在讀者程序裡的位置決定。

同一組判別也用在「知識在庫裡沒被取用」與「取用了而值域不夠」之間,而那一組有一個讀得到的答案:問這條規則寫下來的觸發條件與例示把它涵蓋進去了嗎,不問這條規則該不該涵蓋它(後一個問題的答案永遠是該)。例示界定值域,而例示是讀得到的文字、不必推測——一次實測裡待判的事項是「他人對系統行為的陳述」,而兩條相關規則的觸發條件分別寫著「檔案是否存在、HEAD 是什麼、grep 是否命中」(三個例示全是本地世界狀態的二元查詢)與「git status 有我沒印象的變更」(限定於工作區對帳),兩條都不含它,判定因此是值域不足。

同一條不對稱另有一個方向,而它多出不可推翻這一層:把偶發現象誤判成工具缺陷。 一次實測把背景作業改寫工作區的中間狀態讀成「這具工具的產出不是格式化的固定點」,據此寫下的處置是「每次跑完都要補跑一次格式化」——工作區清空之後單獨再跑一次,產出與版本庫逐位元相同,那個缺陷不存在。這種誤判產生的多餘步驟事後不會被推翻,因為做了它之後看起來就是有效的:補跑的那一次確實讓檔案合規(它本來就合規),於是每一次執行都替那條規則背書。與上一段的值域誤判相比,那一種無效而無害,這一種有害且自證。處置是宣稱任何工具有缺陷之前,在乾淨的工作區單獨跑它一次——成本是一次執行,而寫錯的結論會留在規則裡。

分錯的代價不對稱,所以答不出來時預設走值域那一邊。 把觸發落點問題誤判成值域不足,結果是擴了值域而人還是不會去用,無效而無害;把值域不足誤判成觸發落點,結果是規則長出一個新的待遵守步驟,有害——它為一個「規則沒涵蓋到」的問題發明一個要人記得的新步驟,把值域問題轉譯成觸發問題,並新增一個往後同樣會不觸發的落點。值域補上之後,執行者面對的是同一條既有規則的自然延伸,不需要另一個機械化步驟。修法也因此不同——補一句說明只是保留那個映射步驟,用執行者答得出來的維度重新命名類別才消除它。

同一批另給出位置這一軸的一個尖銳形態:**知識寫在阻擋訊息裡時,它的讀者集合恰好是已經付過代價的人。**那份知識確實在路上、確實會被讀到,而讀到的時點在事故之後;要讓沒撞到的人也對得上預期,它得住在他寫那一行的當下看得到的地方,或者讓兩支檢查處置一致而不需要那份知識。這一條與版號擋不住並行覆蓋的豁免標記段互為表裡——那裡談標記怎麼寫,這裡談它為什麼選不出來。

跟其他抽象層原則的關係

#322 比對回報零差異時,那個零只證明這個粒度看不到差異 是正對照這個裝置的另一個擺放位置——本卡放進掃描的輸出,那張卡放進比對工具的選擇。「第一次成功恰好繞過失效路徑」的形態兩卡共用:粗層工具在別的比對上給過正確答案,正是這一次不設防的理由。

#291 掃描指令壞掉時不會報錯,它報一個數字 是本卡的機制本體,本卡答的是它自己沒回答的那個問題——那張卡寫下來了,而同一個機制照樣重演。兩張卡的分工因此是:那裡列失效的形態與探測的方法,這裡處理探測方法為什麼沒被執行。那張卡的修法段已經寫了「探測輸入先於真實輸入」,本卡加的是這句話要住在哪裡才會發生。

#249 對當下段落沒有收益的標註不會自發發生 是最近的親戚,共用「一個動作的收益不落在執行它的當下」這個結構。差別在缺席的原因:那裡執行者知道該標而沒有動機,這裡執行者連該做這一步都沒想到。所以那裡的修法是補檢查表或觸發點皆可,這裡只有觸發點有效——檢查表同樣要人先想到去打開它。

#243 要求執行者做判定的規則,要一併規定判定留下什麼痕跡 與本卡是同一條軸的兩端。那裡問的是判定做了沒有(產物相同時規則會塌陷),本卡問的是防線執行了沒有,而兩者的修法同形:把要求變成一個在產物裡看得到的欄位或步驟。正對照那一列正是掃描這個動作的痕跡。

#266 可核對的錨要跟它該偵測的狀態連動 給的是指標的選擇判斷標準,本卡是它再前面一層——一個完全符合那條的指標,配一條沒被驗過的指令,加上一份沒人會打開的記錄,仍然回報恆定的健康值。三者要一起才成立:指標會動、指令會動、而讓指令被驗的那一步不靠人記得。

#232 自審 sweep 的偵測方法要對齊規則類型 講 grep 對無關鍵詞的規則是看不見而不是 clean,本卡是它的機械版本:那裡偵測方法與規則類型不匹配,這裡偵測方法根本沒跑到目標。兩者的輸出都是 clean,而分辨它們的動作不同——那裡要換偵測方法,這裡只要一個正對照。

#315 真值住在稿件之外的宣稱,讀多少遍都驗不出來 與本卡共用「知識不等於動作」這個軸。那裡是閱讀取代不了執行,這裡是記錄取代不了執行;兩張卡的修法都是把一個原本靠人的環節換成一個會自己動的環節。 #329 掃描的作用域會默默對齊「我這次動過什麼」 與本卡補的「執行本身有深淺」是兩條正交的軸:那裡問動作作用在多大範圍上,這裡問動作做得多深。同一次收尾可以深度到位而範圍窄,兩者的報告都是乾淨的,所以收尾清單要把它們分開列。 #334 代理比本體便宜 與本卡相鄰而軸不同:本卡問防線放在哪裡(要人先起疑才會打開),那一張問走到防線要花多少錢。兩者合起來解釋「知道」為什麼不等於「做到」——一個是觸發不到,一個是觸發到了而代理更便宜。 #348 規範文字的效力由它在讀者程序裡的位置決定 是本卡這條位置軸上更後面的一格,兩者的修法方向相反——本卡把內容搬到動作路徑上,那張把內容在一份已經被讀的文件內部往前搬。共用的推論是「送達」證明不了「生效」,而兩張卡各自給了一種送達成立而生效不成立的形態:本卡是送達了沒被打開,那張是打開了還引用了仍不綁定。