規則的修法逐句驗證會通過、套到案例上才互撞:改完才查得到的事實管不了動手之前的步驟,修完要拿任務重走
論述基礎與限制
這張卡接在 規則檔的修法逐輪製造新歧義:程序寫成有測試的工具,共用的概念只定義一次 之後,來自同一組規則檔的第六、七輪審查。那張卡的結構調整做完之後,同題的入口探針歧義從 25 處降到 11 處,下一輪修完又升到 14 處。把第七輪指出的 7 個位置用 git log -S 追到引進那句話的 commit:4 個的句子是第六輪修正新寫的,2 個是那次的新句子跟更早的規則套在同一個案例上互相衝突,1 個是舊句子。單一組檔案、兩輪的追溯,卡上的比例只屬於這一次;卡要歸納的是修改動作的機制。
核心原則
照 finding 修規則時,每一條修法都在它被問的那個位置、以句子為單位寫成,也以句子為單位驗收;而讀者照規則做事時,是拿一件任務從頭走到尾,一個任務同時經過好幾條規則。兩者的單位不同,所以修法在句子層通過,在任務層互相衝突。追溯出的機制有四種。「改完才查得到的事實被拿來決定動手之前的步驟」那一種是結構性的,換措辭消不掉:
改完才查得到的事實,被拿來決定動手之前的步驟。「改動的級別」由一支工具比對 diff 判定,改完才查得到;叫用寫作 skill、讀規則分冊卻要在動手之前做。用級別決定這兩步要不要做,規則裡就一定有一個「先改、再判、判完補做」的時點。探針連續兩輪三份全數指出這一處,措辭換過兩次(「只打算做排版級時」、「使用者的指示是改錯字時」),時間順序的矛盾都還在。修法是讓動手之前的步驟不依賴級別:級別只決定改完之後的事(紀錄、掃描、審查、版號)。定義一個共用概念時,除了「判不判得出來」,還要問「在什麼時點查得到」,用它當觸發條件的規則,動作的時點不能早於它。
在被問的位置補答案,不查同一個對象上的其他規則。探針問「補 frontmatter 欄位算哪一級」,修法在級別的定義裡補一句「補欄位不是排版級」;而另一節早就要求「修改既有檔案時順手補缺的欄位」,兩條同時套到「改一個錯字」的任務上,那一次就不再是排版級,讀者不知道該分開提交還是照措辭級走。探針問「提交工具回 exit 1 怎麼辦」,修法寫「照錯誤訊息修正」;而另一條規則說「錯誤落在別人的檔案時不修」。每一句單獨讀都對,修完跑的檢查(段名引用、字句層的關鍵詞掃描)都以句子為單位,看不到衝突。
集中成唯一住址時,原位置留下摘要。「要先問使用者的情形」收成一節,而原本寫這件事的那一節留了一句「清單裡的 skill 不推」,跟新一節的「先問」處置不同。兩句是同一次修改寫的。
定義裡的例子被讀成判定標準。規則級的定義後面接了「改一個數字或一個否定字就能改掉規則的處置」,而被定義的詞(一條「規則」)本身沒有定義,讀者只能拿例子推邊界,於是問「skill 裡的每個步驟都算規則嗎」「只改理由算不算」。
反模式
- 一個共用概念的值要事後才查得到,卻拿它決定事前要不要做某一步。
- 修一則 finding 時只讀被問的那一句,修完只驗那一句。
- 探針指出的每一處都在原位補一句,下一輪的 finding 落在這些新句子上。
- 集中成唯一住址之後,原位置留著「不推」「要先問」這類短句當提示。
- 用例子代替一個沒有定義的詞,例子之後沒有定義。
修法
- 定義共用概念時寫出它的時點:這個值在工作流程的哪一步之後才查得到。用到它的規則,動作排在那一步之後;動作必須在那一步之前做時,那條規則不看這個概念,改成無條件做,或改看一個事前就查得到的事實。
- 修完拿任務重走一次,再派下一輪:把探針用的任務與這一輪 finding 的案例逐一走過修改後的規則,每一步寫出經過哪幾條規則、各自給什麼處置。同一步出現兩種處置、或某一步沒有任何規則管,就是下一輪會被指出的位置。這一步不需要派人,走一次的成本是幾個案例乘上規則條數;這一次走九個案例,在派探針之前找出三處。
- 修一則 finding 之前,列出同一個對象上的其他規則:被修的那一句作用在什麼對象上(一種檔案、一個 exit code、一個動作),全組檔案裡還有哪些規則作用在同一個對象上,逐條對過再寫。
- 集中成唯一住址時,原位置只留連結:原位置的句子要嘛刪掉,要嘛只剩「見〈某節〉」,不留帶著處置的摘要。
- 定義先於例子:被定義的詞在同一處寫出定義;例子用來說明定義,緊接在定義之後,並寫出例子示範的是定義的哪一部分。
- 修法預算:一則修法新增的限定子句,不多於它刪掉的;刪掉一條例外時不寫替代句。下一輪的追溯裡,刪掉改錯字例外時寫的替代句「不論改動大小都照表讀」,被讀成跟前一句「全文已在 context 就不必讀」衝突,刪除本身沒有造成歧義。
- 能不讓執行者判語意的分界就不判:「措辭還是規則」「哪些段落算規則」「案例從哪裡來」連續幾輪都被指出,因為分界本身就要讀者判語意。改成保守的預設(規則檔裡不是排版級的改動一律算規則級),整條分界連同它的定義一起刪掉,代價是多跑幾次審查。
- 修完的驗收交給異源的讀者,並留下可以重跑的案例:作者自己重走會帶著作者的 context(那一次自己走的九個案例都假設分冊已經在 context 裡),封閉案例題加預期答案的做法在 要理解探針列出「有兩種讀法的位置」是審查題:同一份檔案兩批的總數差得比輪與輪之間還大,改用封閉案例題加預期答案。
判讀徵兆
| 徵兆 | 判讀 |
|---|---|
| 同一處歧義換過兩次措辭仍然被指出 | 矛盾在時間順序或結構,不在措辭;先查觸發條件在什麼時點查得到 |
| 新的歧義落在這一輪剛寫的句子上 | 修法只在句子層驗收,沒有拿任務重走 |
| 探針的「兩種讀法」是兩條規則各給一種處置 | 修法沒查同一個對象上的其他規則 |
| 探針問「X 算不算」,而文字裡只有一個例子 | 被定義的詞沒有定義,例子被當成判定標準 |
跟其他抽象層原則的關係
- 要理解探針列出「有兩種讀法的位置」是審查題:同一份檔案兩批的總數差得比輪與輪之間還大,改用封閉案例題加預期答案:本卡「修完拿任務重走」由作者自己走;那張卡把驗收交給異源的探針,並指出審查題的總數判不出收斂。
- 規則檔的修法逐輪製造新歧義:程序寫成有測試的工具,共用的概念只定義一次:本卡最接近的既有卡,同一組檔案的前一輪。那張卡處理規則檔的結構(程序、共用概念、觸發條件),本卡處理修改那些結構的動作;那張卡的「共用的概念先定義再引用」少了時點這一問,本卡補上。
- 一次修法會對同一件事寫出兩種形式,而兩種都不命中舊 pattern:「集中成唯一住址時原位置留下摘要」是那張卡的機制在住址搬遷上的形態,兩種形式都是新寫的。
- finding 清單列的是抽樣位置,照著逐處修會留下同一類在沒被點名的地方:那張卡說修完要掃同一類,掃的是字串;本卡的衝突沒有字串可掃,要拿任務重走。
- 規範文字的效力由它在讀者程序裡的位置決定,逐字讀過也不保證綁定:規則在讀者程序裡的位置包括時點;事前的步驟綁在事後才有的事實上,就是位置放錯的一種。
- 要求執行者做判定的規則,要一併規定判定留下什麼痕跡:「改了意思」的判定方法寫成「拿案例走」,痕跡是那個結果變了的案例。