多次局部正確的修法會合成缺陷,而合成的缺陷對每一次修法的 frame 都不可見
論述基礎與限制
本卡從一組資安章節連續跑八輪審查(主批四輪、新章四輪)之後的回看抽出。那組內容的每一輪都換 frame、每一輪的 finding 都被修完,而事後至少四處缺陷的成因是同一個:兩次修法各自正確,合起來出問題。
四個實例的形狀相同。第二輪指出某章缺進入動機,修法是把該章微案例的內容前置成開場鉤子——於是同一則故事在相隔一百多行處出現兩次、細節一字不差。第三輪指出某段的判斷標準用錯了,修法是把它換成正確的判斷標準——而第四輪指出那整段本來就不該在正文裡(它是寫給審稿人的編輯決策),第三輪的修法讓一段不該存在的內容變得更完整。另一輪要求把正文的判定與待辦清單對齊,修法是在正文寫出對齊後的狀態——而那句話把內部待辦狀態帶進了讀者要讀的文字。最後一處是模組入口:三輪各插入一句正確的補充,第三句落在句子中間,讀者讀到「這一類讀者可以直接從某章起步」之後又接到「再讀某章」。
限制:本卡談的是多輪審查或多次修改累積在同一份產物上的情形。單輪審查、或每次修改都由同一個人在同一個脈絡下完成的情形不適用——那裡的合成問題會在修改當下就被看見。
核心原則
兩次修法各自在自己的 frame 內正確時,它們的合成仍然可能有缺陷,而那個缺陷對兩個 frame 都不可見。 不可見是結構性的:第一個 frame 的視野裡沒有第二次修法(它還沒發生),第二個 frame 的視野裡有第一次修法的產物但沒有它的動機(frame 只問自己那一軸),於是「這兩件事放在一起會怎樣」不落在任何一方的職責裡。
這與「修法引入同類變體」(見 #167)是不同的機制。那裡的成因是同一個(修的人盯著表面、放過成因),所以下一輪同 frame 或近 frame 就抓得到。這裡的兩次修法成因不同、frame 不同、各自都對,缺陷只存在於它們的關係上——要看見它必須有第三個 frame,而那個 frame 的對象是「這份內容現在整體長什麼樣」,不是任何一條軸。
推論是停止判讀要多一個條件:所有 frame 都跑過、所有 finding 都修完,不等於這份內容現在是好的。修法本身改變了被審查的對象,而改變之後沒有人以整體為對象重看過一次。
第二種形態:共用產物被每一輪修改而不被任何一輪審查
同一個機制在跨檔案的層級有另一種形態,而它更難察覺。
多輪審查的對象通常是內容本身——章節、卡片、文章。而每一輪的修法除了改內容,還會動到幾份共用產物:模組或分類的入口頁(路線敘述)、待辦清單、索引。這些產物被每一輪修改,卻不在任何一輪的審查範圍裡,因為每一輪的 frame 都是對著內容設計的。
入口頁的後果最直接:它是所有讀者路線的起點,而它累積了 N 輪的插入。每一句插入單獨看都正確(補一條新分支、補一個分流、修一個過時的指涉),而三次之後那一段可能已經不成句。沒有人發現,是因為 reviewer 讀的是章節,而作者每次只看自己要插入的那個位置。
登記與除籍的不對稱是同一形態在待辦清單上的表現。登記便宜、而且做的時候動機明確(剛發現一個缺口),所以會做。除籍由「以為自己做完的人」執行,而他在檢查自己的工作——於是會出現刪掉的那一列裡有一半還沒完成。實際發生過:拆章時刪掉一列追蹤兩個微案例候選的待辦,其中一個隨新章走了、另一個留在原章而原章仍宣告著「這一格的後果最不直觀」,宣告而不履行,且追蹤這件事的那一列已經不在。
第三種形態:修正動作為量測工具開出一條新的通路
既有的兩種形態都落在可讀性層,而它們的判斷標準對量測層恆真。 「這份內容現在讀起來像一個人一次寫完的嗎」問的是文字的接縫,而修法也可能改變的是被審查對象怎麼被量測——那一層沒有接縫可看。
實測是一次改文為某款補上「並指出條文哪一句寫出了這項差異」。這句話讀起來完整、位置正確、補的是一個真實的缺口。而「依據哪一句」正是該題庫每個子問的結尾,射程內二十個題目有十七題含它——補進去的那個詞因此成為該款的獨佔詞,探針對題目結尾做字面比對就唯一選到它。那次量測裡廣度最大的一條字面通路由這次修正開出,而它的效果是讓該款的通過率看起來大幅提升。
同型另有兩例:一次補句讓某題的通過率由三份中的兩份降為一份,一次補句與另一款的問法衝突而整句被移除。
修法是讓那個詞不再獨佔,不是刪掉那句話。 該處的處置是一個字元的插入(把「哪一句」寫成「哪一語句」),因為問題在字面的唯一對應、不在那句話的內容——內容是對的,補的缺口是真的。驗收形式是改動前後各跑一次獨佔字串盤點,比對三項:總數要降(實測 406 降為 389)、移除清單要指名那一條(該款、該詞、命中的題數)、而新增清單必須是空的。最後那一項是這一類的回歸檢查——它問的是這次修法有沒有順手開出另一條通路,而前兩項全部通過而第三項非空的修法,換掉的只是通路的位置。
偵測要換對象:修完之後問這次新增的字面有沒有進到量測的輸入裡。 具體動作是把新增的詞與題目、判斷標準、答案鍵的既有字面並排比對,看有沒有新的唯一對應關係。逐條讀修法產物看不到它——每一句都是對的,缺陷在那句話與量測器材之間。
修法:每一輪收尾多一個以整體為對象的動作
每輪修完之後重讀一次全文,而不是重讀被改動的那幾段。 這一步的成本是一次通讀,而它是唯一能看見合成缺陷的位置。判斷標準是問「這份內容現在讀起來像一個人一次寫完的嗎」——合成缺陷的表徵幾乎都是「像好幾個人各自加了一段」:同一件事講兩次、前後兩句立場不同、某一段的存在只有作者知道為什麼。
共用產物要進審查範圍,而且要在最後一輪跑。 入口頁、待辦清單、索引各自重讀一次,判斷標準與內容不同:入口頁看的是「這幾條路線現在還通嗎」,待辦清單看的是「這幾列的內容真的都還沒做嗎、做完的真的都刪了嗎」。放在最後一輪是因為它們累積的是全部輪次的改動。
除籍要說出理由。 刪掉一列待辦時寫出它是被哪一次改動完成的,這個動作讓「以為做完」與「真的做完」分開——寫不出來的那些多半只做了一半。這與 #243 是同一條規則的另一個對象:要求判定的規則要規定判定留下什麼痕跡,而除籍就是一次判定。
修法落在別人的段落上時,讀完那一段再改。 合成缺陷高頻發生在「我為了修 A 而動了 B 段落」的情形——動的人只讀了要插入的那個位置。這條的成本很低而收效直接。
跟其他原則的關係
- #167 修法是新違規的來源、且常引入同類變體:兩者都說修法會產生問題,機制不同。#167 的新問題與原問題同成因,因此下一輪近 frame 抓得到,處置是把判斷標準從表面移到成因;本卡的新問題由兩次不同成因的修法合成,任何單一 frame 都看不到,處置是加一個以整體為對象的動作。#167 說「review 的 scope 要涵蓋修法後的產物」,本卡補的是那個 scope 要用什麼視角看——逐條看修法產物仍然看不見合成缺陷。
- #95 Multi-pass review 的 scope 要蓋同類風險區:那條擴的是空間範圍(改動區 → 同類風險區),本卡擴的是視角(逐條 → 整體)。兩者可以同時做滿而仍然漏掉合成缺陷,因為它在已經被涵蓋的那些段落之間。
- #148 跨輪 review 停止訊號是 frame 涵蓋、不是 finding 數遞減:本卡是它的補充條件。frame 涵蓋齊備之後還差一件事——那些 frame 各自的修法產物沒有被合起來看過。停止判讀要在 frame 清單之外加一次整體通讀。
- #245 原則層與操作層是兩份會漂移的副本:同一個「沒有人並排讀」的家族,對象不同。#245 的兩份是同一條原則的兩個副本,本卡的兩處是同一份內容的兩次修改。共同的處置方向是把「並排」變成有觸發器的動作。
- #246 被多篇當成前提的判斷缺的是住址:本卡的第二種形態與它同構。#246 的缺口要並置幾篇才看得見,本卡的共用產物要以它自己為對象才看得見;兩者都因為既有檢查的單位(單篇)小於缺陷的單位而漏掉。
- #126 寫作 review 是多軸完整性、不是單軸深度:又一條沒有人負責的軸,而它的位置特殊——它是所有維度都跑完之後才存在的東西,前提就是前面幾輪都做過,因此它排在軸的清單之外而非之中。
#248 推翻一個假說之後,替補者是在驗屍的空檔裡上位的:兩者都描述連續動作在交界處產生缺陷。本卡的兩次動作各自正確、缺陷在合成上;那裡的第一次動作正確(推翻)、第二次動作根本沒被檢查。共同的處置方向都是在序列的收尾加一個以整體為對象的步驟。
#249 對當下段落沒有收益的標註不會自發發生:本卡的「登記與除籍的不對稱」是它在待辦清單上的姊妹形態,動機方向相反——除籍是做了沒人看得到(由以為自己做完的人執行),那裡是不做沒人看得出來(段落照樣完整)。兩者都不會自發發生,處置同型:給觸發器、不期待自覺。
#298 finding 清單列的是抽樣位置:兩者都是修法留下的問題,而偵測入口完全不同。本卡的缺陷在條目之間、要靠一次以整體為對象的通讀;那張卡的缺陷就在條目所屬的那一類裡面,一條掃描指令就抓得到——用通讀去找那一類,是把貴的工具用在便宜的問題上。
一次修法會對同一件事寫出兩種形式:相鄰而範圍與成本都不同。本卡的缺陷跨越多次修法、住在條目之間,偵測要一次以整體為對象的通讀;那一張的缺陷在同一批之內、就在兩個都被改過的位置上,範圍限本批改動、問題限形式一致性。用本卡的整體通讀去找那一類,是把貴的工具用在便宜的問題上——先跑窄的那一道,剩下的才交給通讀。
判讀徵兆
- 同一份內容經過三輪以上的修法,而沒有任何一輪的收尾動作是通讀全文。
- 某一段讀起來像好幾個人各自加了一句:同一件事講兩次、相鄰兩句的立場不同、或某句話的存在只有作者知道理由。
- 修法的理由是「回應某一輪的某個 finding」,而那個 finding 的提出者沒有讀過這一段的其餘部分。
- 模組入口、待辦清單或索引在這一批被改過三次以上,而它們從來沒有進過任何一輪的審查目標清單。
- 待辦清單有列被刪掉,而刪的人指不出它是被哪一次改動完成的。
- 一輪修法讓某段變得更完整,而下一輪指出那段本來就不該存在。