<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Verification on Tarragon</title><link>https://tarrragon.github.io/blog/tags/verification/</link><description>Recent content in Verification on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 15 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/verification/index.xml" rel="self" type="application/rss+xml"/><item><title>字面攔截 vs 行為精煉：驗證手段跟錯誤層次的對齊</title><link>https://tarrragon.github.io/blog/report/literal-interception-vs-behavioral-refinement/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/literal-interception-vs-behavioral-refinement/</guid><description>&lt;h2 id="結論">結論&lt;/h2>
&lt;p>驗證手段（hook / lint / CI / review / spiral / test / production observation）有不同的「錯誤偵測粒度」、必須跟&lt;strong>錯誤的層次&lt;/strong>對齊：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>錯誤層次&lt;/th>
 &lt;th>例子&lt;/th>
 &lt;th>適合手段&lt;/th>
 &lt;th>不適合手段&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>字面&lt;/td>
 &lt;td>typo、缺 field、syntax 錯、檔案沒 frontmatter&lt;/td>
 &lt;td>hook、lint、type checker、schema validation&lt;/td>
 &lt;td>multi-pass review（過殺）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>行為&lt;/td>
 &lt;td>推薦騎牆、yes/no collapse、思考偏差、judgment 錯位&lt;/td>
 &lt;td>multi-pass spiral、review、dogfood&lt;/td>
 &lt;td>hook（catch 不到、假裝有保護）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>「攔截」這個動作預設&lt;strong>已經知道錯誤的形狀&lt;/strong>（hook 寫死規則 = 已知錯誤）。&lt;strong>真正會出錯的是「不知道形狀」的錯誤&lt;/strong> — 那需要多輪 review / spiral 收斂、不是即時攔截。&lt;/p>
&lt;hr>
&lt;h2 id="為什麼-hook-對行為錯誤無能為力">為什麼 hook 對行為錯誤無能為力&lt;/h2>
&lt;p>Hook / lint / type checker 的本質是 &lt;strong>字串匹配 / structural check&lt;/strong> — 看得到形狀、看不到意圖。所以：&lt;/p>
&lt;ul>
&lt;li>抓得到「commit message 沒含 issue 號」 — 字面 pattern&lt;/li>
&lt;li>抓得到「test file 沒對應 source file」 — 結構檢查&lt;/li>
&lt;li>抓得到「YAML frontmatter 缺欄位」 — schema check&lt;/li>
&lt;li>抓不到「這個推薦不夠明確、騎牆」 — 需要理解語意&lt;/li>
&lt;li>抓不到「決策 collapse 到 yes/no、漏五維」 — 需要判斷意圖&lt;/li>
&lt;li>抓不到「思考路徑跳過 RED phase」 — 需要追溯 reasoning&lt;/li>
&lt;li>抓不到「過度疊加策略、超過必要」 — 需要 judgment&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Hook 試圖用字串規則模擬語意檢查 = 規則永遠 over-fit 或 under-fit&lt;/strong>：寫太嚴 → 大量 false positive 把好的也擋掉、寫太鬆 → 行為錯誤照樣通過。&lt;/p>
&lt;hr>
&lt;h2 id="反模式用-hook-蓋行為錯誤的代價">反模式：用 hook 蓋行為錯誤的代價&lt;/h2>
&lt;h3 id="false-confidence-比沒保護更危險">False confidence 比沒保護更危險&lt;/h3>
&lt;p>寫了 hook 之後、心理上會覺得「有保護」。實際上 hook 只擋字面、行為錯誤照常發生 — 但作者不再警覺、因為「CI 通過了應該沒事」。&lt;/p>
&lt;p>對比沒 hook 的情境：作者知道沒保護、會主動多看一次。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>狀態&lt;/th>
 &lt;th>警覺度&lt;/th>
 &lt;th>實際漏接率&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>沒 hook&lt;/td>
 &lt;td>高（知道沒保護）&lt;/td>
 &lt;td>中&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Hook 抓不到的範圍誤以為有保護&lt;/td>
 &lt;td>低（誤以為有）&lt;/td>
 &lt;td>&lt;strong>高&lt;/strong>（行為錯誤通過）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Hook 真的夠（純字面領域）&lt;/td>
 &lt;td>適中&lt;/td>
 &lt;td>低&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>第二行是最危險的組合&lt;/strong> — 加 hook 卻不知道 hook 範圍、會比沒 hook 更糟。&lt;/p>
&lt;h3 id="規則膨脹嘗試再寫一條-hook永遠補不完">規則膨脹：嘗試「再寫一條 hook」永遠補不完&lt;/h3>
&lt;p>每次行為錯誤通過、直覺反應是「再加一條 hook 規則」。但行為錯誤的形狀是無限的、規則永遠補不完。最終結果：&lt;/p>
&lt;ul>
&lt;li>規則越來越多、越來越複雜&lt;/li>
&lt;li>維護成本爆炸&lt;/li>
&lt;li>仍然漏接行為錯誤&lt;/li>
&lt;li>還產生越來越多 false positive 把好的擋掉&lt;/li>
&lt;/ul>
&lt;p>→ 規則膨脹是「用錯工具」的訊號、不是「規則寫得不夠細」的訊號。&lt;/p>
&lt;hr>
&lt;h2 id="多輪精煉的設計spiral-取代攔截">多輪精煉的設計：spiral 取代攔截&lt;/h2>
&lt;p>行為錯誤的正確驗證手段是 &lt;strong>multi-pass spiral&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">第 1 輪：先做、看結果
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> ↓ 發現 N 個問題
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">第 2 輪：依結果調整 / 補強
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> ↓ 發現 N-k 個問題
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">第 3 輪：dogfood / 實際使用 / 反向自查
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> ↓ 收斂
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">（沒新問題 → 結束、有新問題 → 繼續迭代）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>關鍵設計：&lt;strong>不是「攔截錯誤」、是「設計每輪能 catch 不同層的錯誤」&lt;/strong>。&lt;/p></description><content:encoded><![CDATA[<h2 id="結論">結論</h2>
<p>驗證手段（hook / lint / CI / review / spiral / test / production observation）有不同的「錯誤偵測粒度」、必須跟<strong>錯誤的層次</strong>對齊：</p>
<table>
  <thead>
      <tr>
          <th>錯誤層次</th>
          <th>例子</th>
          <th>適合手段</th>
          <th>不適合手段</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>字面</td>
          <td>typo、缺 field、syntax 錯、檔案沒 frontmatter</td>
          <td>hook、lint、type checker、schema validation</td>
          <td>multi-pass review（過殺）</td>
      </tr>
      <tr>
          <td>行為</td>
          <td>推薦騎牆、yes/no collapse、思考偏差、judgment 錯位</td>
          <td>multi-pass spiral、review、dogfood</td>
          <td>hook（catch 不到、假裝有保護）</td>
      </tr>
  </tbody>
</table>
<p>「攔截」這個動作預設<strong>已經知道錯誤的形狀</strong>（hook 寫死規則 = 已知錯誤）。<strong>真正會出錯的是「不知道形狀」的錯誤</strong> — 那需要多輪 review / spiral 收斂、不是即時攔截。</p>
<hr>
<h2 id="為什麼-hook-對行為錯誤無能為力">為什麼 hook 對行為錯誤無能為力</h2>
<p>Hook / lint / type checker 的本質是 <strong>字串匹配 / structural check</strong> — 看得到形狀、看不到意圖。所以：</p>
<ul>
<li>抓得到「commit message 沒含 issue 號」 — 字面 pattern</li>
<li>抓得到「test file 沒對應 source file」 — 結構檢查</li>
<li>抓得到「YAML frontmatter 缺欄位」 — schema check</li>
<li>抓不到「這個推薦不夠明確、騎牆」 — 需要理解語意</li>
<li>抓不到「決策 collapse 到 yes/no、漏五維」 — 需要判斷意圖</li>
<li>抓不到「思考路徑跳過 RED phase」 — 需要追溯 reasoning</li>
<li>抓不到「過度疊加策略、超過必要」 — 需要 judgment</li>
</ul>
<p><strong>Hook 試圖用字串規則模擬語意檢查 = 規則永遠 over-fit 或 under-fit</strong>：寫太嚴 → 大量 false positive 把好的也擋掉、寫太鬆 → 行為錯誤照樣通過。</p>
<hr>
<h2 id="反模式用-hook-蓋行為錯誤的代價">反模式：用 hook 蓋行為錯誤的代價</h2>
<h3 id="false-confidence-比沒保護更危險">False confidence 比沒保護更危險</h3>
<p>寫了 hook 之後、心理上會覺得「有保護」。實際上 hook 只擋字面、行為錯誤照常發生 — 但作者不再警覺、因為「CI 通過了應該沒事」。</p>
<p>對比沒 hook 的情境：作者知道沒保護、會主動多看一次。</p>
<table>
  <thead>
      <tr>
          <th>狀態</th>
          <th>警覺度</th>
          <th>實際漏接率</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>沒 hook</td>
          <td>高（知道沒保護）</td>
          <td>中</td>
      </tr>
      <tr>
          <td>Hook 抓不到的範圍誤以為有保護</td>
          <td>低（誤以為有）</td>
          <td><strong>高</strong>（行為錯誤通過）</td>
      </tr>
      <tr>
          <td>Hook 真的夠（純字面領域）</td>
          <td>適中</td>
          <td>低</td>
      </tr>
  </tbody>
</table>
<p><strong>第二行是最危險的組合</strong> — 加 hook 卻不知道 hook 範圍、會比沒 hook 更糟。</p>
<h3 id="規則膨脹嘗試再寫一條-hook永遠補不完">規則膨脹：嘗試「再寫一條 hook」永遠補不完</h3>
<p>每次行為錯誤通過、直覺反應是「再加一條 hook 規則」。但行為錯誤的形狀是無限的、規則永遠補不完。最終結果：</p>
<ul>
<li>規則越來越多、越來越複雜</li>
<li>維護成本爆炸</li>
<li>仍然漏接行為錯誤</li>
<li>還產生越來越多 false positive 把好的擋掉</li>
</ul>
<p>→ 規則膨脹是「用錯工具」的訊號、不是「規則寫得不夠細」的訊號。</p>
<hr>
<h2 id="多輪精煉的設計spiral-取代攔截">多輪精煉的設計：spiral 取代攔截</h2>
<p>行為錯誤的正確驗證手段是 <strong>multi-pass spiral</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">第 1 輪：先做、看結果
</span></span><span class="line"><span class="ln">2</span><span class="cl">   ↓ 發現 N 個問題
</span></span><span class="line"><span class="ln">3</span><span class="cl">第 2 輪：依結果調整 / 補強
</span></span><span class="line"><span class="ln">4</span><span class="cl">   ↓ 發現 N-k 個問題
</span></span><span class="line"><span class="ln">5</span><span class="cl">第 3 輪：dogfood / 實際使用 / 反向自查
</span></span><span class="line"><span class="ln">6</span><span class="cl">   ↓ 收斂
</span></span><span class="line"><span class="ln">7</span><span class="cl">（沒新問題 → 結束、有新問題 → 繼續迭代）</span></span></code></pre></div><p>關鍵設計：<strong>不是「攔截錯誤」、是「設計每輪能 catch 不同層的錯誤」</strong>。</p>
<h3 id="各輪的職責分工">各輪的職責分工</h3>
<table>
  <thead>
      <tr>
          <th>輪次</th>
          <th>適合 catch 什麼</th>
          <th>怎麼設計</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>第 1 輪：實作</td>
          <td>純執行、預期會有錯</td>
          <td>不要追求 perfect、跑起來看結果</td>
      </tr>
      <tr>
          <td>第 2 輪：自查 / 對比需求</td>
          <td>邏輯偏差、漏 case</td>
          <td>對比原始需求、列 Checkpoint 1（<a href="../verification-timeline-checkpoints/">#68</a>）</td>
      </tr>
      <tr>
          <td>第 3 輪：dogfood / production</td>
          <td>實際使用才浮現的問題</td>
          <td>真實 user / 真實流量、看回饋</td>
      </tr>
      <tr>
          <td>第 N 輪：反向自查</td>
          <td>上幾輪沒看到的盲點</td>
          <td>改換 frame（例如「假裝是另一個人 review」）</td>
      </tr>
  </tbody>
</table>
<p>每輪解上一輪沒看到的問題、不是重複同一檢查。</p>
<h3 id="不同輪適合不同的不對齊">不同輪適合不同的「不對齊」</h3>
<ul>
<li>第 1 輪 vs 需求 → 看「做出來的跟要的對不對齊」</li>
<li>第 2 輪 vs 邊界 case → 看「漏哪些情境」</li>
<li>第 3 輪 vs 真實使用 → 看「用起來感覺對不對」</li>
<li>第 N 輪 vs 上層原則 → 看「有沒有違反某個 meta-原則」</li>
</ul>
<p>每輪有不同的角度、新角度才能 catch 上一輪 miss 的東西。</p>
<hr>
<h2 id="何時-hook-真的足夠">何時 hook 真的足夠</h2>
<p>某些情境純字面就夠、加 hook 是對的：</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>為什麼 hook 夠</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema validation（API、DB、config）</td>
          <td>結構是 spec、字面對 = 行為對</td>
      </tr>
      <tr>
          <td>已知的 anti-pattern 字串（<code>TODO:</code>、<code>FIXME:</code>、<code>console.log</code>）</td>
          <td>字面就是 evidence</td>
      </tr>
      <tr>
          <td>格式統一（換行、縮排、import 順序）</td>
          <td>純美化、沒語意</td>
      </tr>
      <tr>
          <td>不可破壞的 invariant（commit 訊息含 issue 號、test 名格式）</td>
          <td>結構即正確</td>
      </tr>
      <tr>
          <td>安全 critical 的 surface check（沒 secret 在 code、license header 在）</td>
          <td>漏掉成本極高、字面檢查 ROI 高</td>
      </tr>
  </tbody>
</table>
<p>五類共通：<strong>錯誤形狀完全字面、且漏掉成本高 / 字面就是 evidence</strong>。其他情境 hook 都會在某個時點走到 ceiling。</p>
<hr>
<h2 id="識別-ceiling什麼時候該換手段">識別 ceiling：什麼時候該換手段</h2>
<p>ceiling 訊號：</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>該換的手段</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「這個 lint 規則寫不出來、太多例外」</td>
          <td>改 review checklist、不寫 lint</td>
      </tr>
      <tr>
          <td>「hook pass 但 production 還是出錯」</td>
          <td>hook 已到 ceiling、補 multi-pass review</td>
      </tr>
      <tr>
          <td>「規則第 N 次補例外」</td>
          <td>規則膨脹、退回 review</td>
      </tr>
      <tr>
          <td>「false positive 比 true positive 多」</td>
          <td>hook 過殺、放寬 + 補 review</td>
      </tr>
      <tr>
          <td>「需要 understand intent 才能判斷」</td>
          <td>純字面不夠、要 LLM / human review</td>
      </tr>
      <tr>
          <td>「加了 hook 後 review 變草率」</td>
          <td>False confidence 在發生、警覺度降低</td>
      </tr>
  </tbody>
</table>
<p>看到任一訊號、不是「再寫一條 hook」、是<strong>接受 hook 對這個錯誤層次無能為力、改設計 multi-pass review</strong>。</p>
<hr>
<h2 id="跟其他抽象層原則的關係">跟其他抽象層原則的關係</h2>
<table>
  <thead>
      <tr>
          <th>原則</th>
          <th>關係</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="../two-occurrence-threshold/">#42 2 次門檻</a></td>
          <td>第 2 輪是 multi-pass 的最小單位、跟本卡的「多輪設計」同骨</td>
      </tr>
      <tr>
          <td><a href="../verification-timeline-checkpoints/">#68 驗收的時間軸</a></td>
          <td>#68 的四個 checkpoint = 多輪 review 的時間軸實現</td>
      </tr>
      <tr>
          <td><a href="../test-first-red-before-green/">#69 Test-First：RED before GREEN</a></td>
          <td>RED phase 是「testing the test」的多輪設計 — 純 hook 看不到</td>
      </tr>
      <tr>
          <td><a href="../external-trigger-for-high-roi-work/">#72 高 ROI 無觸發</a></td>
          <td>#72 提倡 L3-L5 結構性對策、本卡是 ceiling — L5 hook 抓不到行為錯誤、需要 L4 review / pair</td>
      </tr>
      <tr>
          <td><a href="../cards-as-living-system-iteration/">#81 卡片系統的迭代浮現</a></td>
          <td>spiral 浮現本身就是 multi-pass 的具體 case — 不靠單次「寫對」</td>
      </tr>
      <tr>
          <td><a href="../decision-dialogue-dimensions/">#79 決策對話的五維度</a></td>
          <td>「五維 collapse」是行為錯誤、hook 抓不到、要靠 reference dogfood + multi-pass review</td>
      </tr>
      <tr>
          <td><a href="../writing-multi-pass-review/">#83 Writing 的 multi-pass review</a></td>
          <td>本卡在「寫」這個動作的具體實例 — review 是 multi-pass、不是 hook</td>
      </tr>
      <tr>
          <td><a href="../naming-as-iterated-artifact/">#84 Naming 是 iterated artifact</a></td>
          <td>本卡在「命名」這個動作的具體實例 — 命名 lint 只擋字面、grep / 一致性 / impl 洩漏靠 review</td>
      </tr>
      <tr>
          <td><a href="../methodology-multi-pass-embedding/">#85 Methodology 的 multi-pass 該 embed 在 pillar</a></td>
          <td>本卡在「方法論設計本身」這一層的展現 — multi-pass 升 pillar 才結構性執行</td>
      </tr>
      <tr>
          <td><a href="../emergence-violations-need-in-stream-sampling/">#124 Emergence-class 違規規則化不了、要 stage 內抽樣</a></td>
          <td>三類分法擴展 — 本卡是 2 類分法（字面 / 行為）、#124 擴展為 3 類（字面 / 結構 / emergence）並補 timing 軸；emergence 是行為層中跨檔 / 跨樣本才浮現的子類</td>
      </tr>
  </tbody>
</table>
<p>本卡是 #72 的 sibling / 補強 — #72 推 L3-L5 結構性對策最強、本卡指出 L5 也有 ceiling、不是萬能。組合解：<strong>字面用 L5 hook、行為用 L4 pair + multi-pass</strong>。#124 進一步把行為層細分出 emergence 子類、補上對應 enforcement 時機。</p>
<hr>
<h2 id="套用到本系統的-case">套用到本系統的 case</h2>
<h3 id="case-1卡片系統本身">Case 1：卡片系統本身</h3>
<p><code>mdtools fmt --fix</code> 是 hook（字面）— 處理 frontmatter、table 對齊、檔名 slug。
卡片內容對不對、抽 meta 抽得對不對 = 行為錯誤 — 靠 spiral 浮現（<a href="../cards-as-living-system-iteration/">#81</a>）、不靠 hook。</p>
<h3 id="case-2搜尋頁-bug">Case 2：搜尋頁 bug</h3>
<p>CI 跑 playwright = 字面測試（給定輸入、output 是否符合）。
但「filter mode 切換有沒有 silent failure」這個 bug 一開始連 test case 都沒列、是 user 回報才浮現 — multi-pass dogfood 才 catch 到。</p>
<h3 id="case-3決策對話-collapse">Case 3：決策對話 collapse</h3>
<p>Hook 寫不出「這個回應 collapse 到 yes/no」的規則（語意理解）。
靠 reference 的 self-check + dogfood 例子 + 對話中 user 反饋的 multi-pass 才能 catch。</p>
<p>每個 case 都驗證同一條：<strong>字面層工具有用、但 ceiling 明確；行為層需要 multi-pass、不靠攔截</strong>。</p>
<hr>
<h2 id="判讀徵兆">判讀徵兆</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>該做的事</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>想加 hook 防某個重複出現的問題</td>
          <td>先問「是字面還是行為？」、行為的話別寫 hook</td>
      </tr>
      <tr>
          <td>寫了 hook 規則但例外越來越多</td>
          <td>ceiling 到了、改 review</td>
      </tr>
      <tr>
          <td>「CI 通過 = 沒事」這個信念</td>
          <td>檢查 CI 範圍、行為錯誤可能漏接</td>
      </tr>
      <tr>
          <td>同類錯誤不斷以新形狀出現</td>
          <td>行為錯誤、hook 無解、補 multi-pass</td>
      </tr>
      <tr>
          <td>第 1 輪做完就 ship、沒第 2 輪</td>
          <td>假設一次寫對、多半會漏行為錯誤</td>
      </tr>
      <tr>
          <td>多輪 review 每輪用同樣 frame</td>
          <td>角度沒換、後續輪 = 重跑前輪、不會新發現</td>
      </tr>
      <tr>
          <td>「下次注意」當作驗證</td>
          <td>L1 紀律、不是 L4 結構、跟 <a href="../external-trigger-for-high-roi-work/">#72</a> 同病</td>
      </tr>
      <tr>
          <td>行為錯誤反覆出現、但「再加條 hook 規則」</td>
          <td>換工具、不是換規則</td>
      </tr>
  </tbody>
</table>
<p><strong>核心</strong>：驗證手段的 ROI = 跟錯誤層次對齊 × 不超出 ceiling。<strong>Hook 不會思考、所以只能擋字面</strong>；<strong>行為錯誤需要 multi-pass spiral、用每輪不同角度收斂、不靠單次攔截</strong>。試圖用 hook 蓋 spiral 該做的工作 = 假裝有保護、實際比沒保護更危險。</p>
]]></content:encoded></item><item><title>Vendor Feature 時間敏感性：Claim Verification 必跑、寫作日期必標</title><link>https://tarrragon.github.io/blog/report/vendor-feature-time-sensitivity-claim-verification/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/vendor-feature-time-sensitivity-claim-verification/</guid><description>&lt;h2 id="核心vendor-feature-limitation-claim-有時間敏感性">核心：Vendor feature limitation claim 有時間敏感性&lt;/h2>
&lt;p>寫 vendor article 時、常見以下 claim 形態：&lt;/p>
&lt;ul>
&lt;li>「Vendor X 不支援 Y」&lt;/li>
&lt;li>「Vendor X 最多 Z」&lt;/li>
&lt;li>「Vendor X 預設 W」&lt;/li>
&lt;/ul>
&lt;p>這些 claim 在寫作那刻是真的、但 vendor 持續演進。寫作後 &lt;em>N 個月&lt;/em> — 6 個月、12 個月、24 個月 — claim 可能反轉、整段 audit 邏輯 invalidates。&lt;/p>
&lt;p>問題不只是 &lt;em>claim 過時&lt;/em>、是 &lt;em>基於 claim 的整段流程被推翻&lt;/em>。Migration playbook Phase 1 audit 如果以「Vendor 不支援 X」為前提、X 後來變支援、Phase 1 整段重寫。&lt;/p>
&lt;h2 id="caseplanetscale-fk-claim-反轉">Case：PlanetScale FK claim 反轉&lt;/h2>
&lt;p>寫 migrate-to-planetscale.md 跟 migrate-vitess-to-planetscale.md 時：&lt;/p>
&lt;ul>
&lt;li>Claim：「PlanetScale 不支援 Foreign Key（Vitess 限制）」&lt;/li>
&lt;li>基於此 claim：Phase 1 audit 整段「FK audit + 全 drop FK + application enforcement 改寫」&lt;/li>
&lt;li>Phase 1 是 weeks-months 工作量、第一個 phase&lt;/li>
&lt;/ul>
&lt;p>實際狀態（4-reviewer C audit catch）：&lt;/p>
&lt;ul>
&lt;li>Vitess 18（2023 末）加 FK 支援&lt;/li>
&lt;li>PlanetScale 2024 起在合適 plan 內可啟用 FK&lt;/li>
&lt;li>「不支援」是 2022 年的事實、寫作時已過時&lt;/li>
&lt;/ul>
&lt;p>修法：整段 Phase 1 audit 從「FK audit + drop」改寫成「FK 行為驗證 + cross-shard cascade 處理」。&lt;/p>
&lt;p>這不是 &lt;em>微調文字&lt;/em>、是 &lt;em>整段 framing 重做&lt;/em>。&lt;/p>
&lt;h2 id="機制為什麼會發生">機制：為什麼會發生&lt;/h2>
&lt;h3 id="1-llm-training-cutoff-vs-vendor-changelog-速度差">1. LLM training cutoff vs vendor changelog 速度差&lt;/h3>
&lt;p>LLM training data 有 cutoff date（通常滯後 12-18 個月）。Vendor major feature release 在 cutoff 後、LLM 不知道。&lt;/p>
&lt;p>寫 vendor article 時、LLM 預設用 &lt;em>training 內的 latest fact&lt;/em> — 那個 fact 可能已過時。&lt;/p>
&lt;h3 id="2-llm-預設不標-claim-的時間性">2. LLM 預設不標 claim 的時間性&lt;/h3>
&lt;p>LLM 寫「PlanetScale 不支援 FK」、不會自動標「&lt;em>as of 2022&lt;/em>」、讀者看到 &lt;em>永久性 claim&lt;/em>。&lt;/p>
&lt;p>LLM 不會主動 verify「我寫的這個 claim 是 N 個月內仍 valid 的嗎」、除非寫作流程強制 verify step。&lt;/p>
&lt;h3 id="3-基於-claim-的整段流程是結構性-anchor">3. 基於 claim 的整段流程是「結構性 anchor」&lt;/h3>
&lt;p>Migration playbook 的 Phase 1 是 &lt;em>結構錨點&lt;/em> — 後續 Phase 2-4 都 reference Phase 1 結果。Phase 1 基於過時 claim 時、修法不只是 claim、是 &lt;em>整個 anchor 重做&lt;/em>。&lt;/p></description><content:encoded><![CDATA[<h2 id="核心vendor-feature-limitation-claim-有時間敏感性">核心：Vendor feature limitation claim 有時間敏感性</h2>
<p>寫 vendor article 時、常見以下 claim 形態：</p>
<ul>
<li>「Vendor X 不支援 Y」</li>
<li>「Vendor X 最多 Z」</li>
<li>「Vendor X 預設 W」</li>
</ul>
<p>這些 claim 在寫作那刻是真的、但 vendor 持續演進。寫作後 <em>N 個月</em> — 6 個月、12 個月、24 個月 — claim 可能反轉、整段 audit 邏輯 invalidates。</p>
<p>問題不只是 <em>claim 過時</em>、是 <em>基於 claim 的整段流程被推翻</em>。Migration playbook Phase 1 audit 如果以「Vendor 不支援 X」為前提、X 後來變支援、Phase 1 整段重寫。</p>
<h2 id="caseplanetscale-fk-claim-反轉">Case：PlanetScale FK claim 反轉</h2>
<p>寫 migrate-to-planetscale.md 跟 migrate-vitess-to-planetscale.md 時：</p>
<ul>
<li>Claim：「PlanetScale 不支援 Foreign Key（Vitess 限制）」</li>
<li>基於此 claim：Phase 1 audit 整段「FK audit + 全 drop FK + application enforcement 改寫」</li>
<li>Phase 1 是 weeks-months 工作量、第一個 phase</li>
</ul>
<p>實際狀態（4-reviewer C audit catch）：</p>
<ul>
<li>Vitess 18（2023 末）加 FK 支援</li>
<li>PlanetScale 2024 起在合適 plan 內可啟用 FK</li>
<li>「不支援」是 2022 年的事實、寫作時已過時</li>
</ul>
<p>修法：整段 Phase 1 audit 從「FK audit + drop」改寫成「FK 行為驗證 + cross-shard cascade 處理」。</p>
<p>這不是 <em>微調文字</em>、是 <em>整段 framing 重做</em>。</p>
<h2 id="機制為什麼會發生">機制：為什麼會發生</h2>
<h3 id="1-llm-training-cutoff-vs-vendor-changelog-速度差">1. LLM training cutoff vs vendor changelog 速度差</h3>
<p>LLM training data 有 cutoff date（通常滯後 12-18 個月）。Vendor major feature release 在 cutoff 後、LLM 不知道。</p>
<p>寫 vendor article 時、LLM 預設用 <em>training 內的 latest fact</em> — 那個 fact 可能已過時。</p>
<h3 id="2-llm-預設不標-claim-的時間性">2. LLM 預設不標 claim 的時間性</h3>
<p>LLM 寫「PlanetScale 不支援 FK」、不會自動標「<em>as of 2022</em>」、讀者看到 <em>永久性 claim</em>。</p>
<p>LLM 不會主動 verify「我寫的這個 claim 是 N 個月內仍 valid 的嗎」、除非寫作流程強制 verify step。</p>
<h3 id="3-基於-claim-的整段流程是結構性-anchor">3. 基於 claim 的整段流程是「結構性 anchor」</h3>
<p>Migration playbook 的 Phase 1 是 <em>結構錨點</em> — 後續 Phase 2-4 都 reference Phase 1 結果。Phase 1 基於過時 claim 時、修法不只是 claim、是 <em>整個 anchor 重做</em>。</p>
<p>這比修 isolated fact 工作量大 10x — 是「invalidates premise」、不是「fix typo」。</p>
<h3 id="4-vendor-article-多用-永久性語氣-而非-時間性語氣">4. Vendor article 多用 <em>永久性語氣</em> 而非 <em>時間性語氣</em></h3>
<p>寫作習慣寫「PlanetScale 不支援 FK」（永久性）、不寫「PlanetScale 截至 2022 末不支援 FK」（時間性）。</p>
<p>讀者讀到的是 <em>當前永久狀態</em>、寫作者其實只能保證 <em>寫作那刻</em>。</p>
<h2 id="修法">修法</h2>
<h3 id="1-每篇-vendor-article-標-last-verified-date">1. 每篇 vendor article 標 <code>Last verified</code> date</h3>
<p>frontmatter 或開頭加：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">last_verified</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-05-19</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">verified_against</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span>- <span class="l">PlanetScale docs（2026-05 access）</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span>- <span class="l">Vitess 18.0 release notes</span></span></span></code></pre></div><p>讓讀者看到 <em>寫作時 verify 的 source / date</em>、不假設永久性。</p>
<h3 id="2-feature-limitation-claim-加時間註">2. Feature limitation claim 加時間註</h3>
<p>寫「Vendor X 不支援 Y」時、加 <em>as of N</em>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">PlanetScale 截至 2024 末有限支援 FK（Vitess 18+、需明確啟用）</span></span></code></pre></div><p>而非：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">PlanetScale 不支援 FK</span></span></code></pre></div><h3 id="3-claim-反轉--整段-audit-重寫不是-patch">3. Claim 反轉 → 整段 audit 重寫、不是 patch</h3>
<p>當 verify 發現 claim 已反轉（如 PlanetScale FK 從不支援變支援）、不要 <em>只改 claim 字句</em>。回頭看 <em>基於該 claim 的流程段落</em> —</p>
<ul>
<li>Migration Phase 1 audit</li>
<li>「何時不要遷」反向 recommendation</li>
<li>「跟 sibling vendor 對比」表</li>
</ul>
<p>每段都要 <em>重看是否還成立</em>、不成立的整段重寫。</p>
<h3 id="4-vendor-article-寫作前先-verify-主要-claim">4. Vendor article 寫作前先 verify 主要 claim</h3>
<p>寫作流程加 <em>verify checkpoint</em>：</p>
<ul>
<li>列出該 article 的「Vendor X 不支援 Y / 最多 Z / 預設 W」claim</li>
<li>對每個 claim、查 vendor official docs（最新 docs）/ recent release note（過去 12 個月）</li>
<li>不確定的標 <em>uncertain</em>、不要 confidence-fake</li>
</ul>
<h3 id="5-reviewer-c-必查-vendor-feature-time-sensitive-claim">5. Reviewer C 必查 vendor feature time-sensitive claim</h3>
<p>跑 4-reviewer audit 時、Reviewer C（技術準確性）必須：</p>
<ul>
<li>對每個 <em>feature limitation claim</em>、verify 是否仍 current</li>
<li>對每個 <em>vendor CLI command</em>、verify 是否真實存在（hallucinated CLI 是 sibling 問題）</li>
<li>對每個 <em>vendor default value</em>、verify 是否最新</li>
</ul>
<h2 id="hallucination-鄰近議題">Hallucination 鄰近議題</h2>
<p>LLM 寫 vendor CLI command 容易 hallucinate（例如 <code>pscale database promote-shadow</code>、<code>vtctldclient PartitionTablet</code>）— 命令不存在、是 LLM 編造。</p>
<p>跟本卡時間敏感性 <em>不完全相同</em> —</p>
<ul>
<li>時間敏感性：<em>claim 寫作時 valid、現在過時</em></li>
<li>Hallucination：<em>claim 寫作時也 invalid、是編造</em></li>
</ul>
<p>兩者修法部分重疊：</p>
<ul>
<li>寫前 verify（claim + CLI）</li>
<li>Reviewer C audit</li>
<li>不確定標 uncertain</li>
</ul>
<p>但 hallucination 是 <em>更基本的 verify failure</em>、本卡聚焦時間敏感性。</p>
<h2 id="跟既有原則的關係">跟既有原則的關係</h2>
<ul>
<li><a href="../sibling-coverage-asymmetry-blindspot-in-priority/">Sibling Coverage Asymmetry Blindspot in Priority</a>：本卡是 <em>claim 時間敏感性</em>、那卡是 <em>coverage 對稱性</em>、不同 axis</li>
<li><a href="../data-topology-as-audit-dimension/">Data Topology as Audit Dimension</a>：本卡是 <em>寫作 audit 應加時間維度</em>、那卡是 <em>content audit 應加 topology 維度</em></li>
</ul>
<h2 id="反向驗證">反向驗證</h2>
<p>不該誤用本卡：</p>
<ul>
<li><em>穩定 fact</em>（SQL syntax / RFC standard / industry-wide convention）不必標時間性、只有 <em>vendor-specific evolving feature</em> 才需要</li>
<li>不是每個 claim 都要 verify — 「MySQL replication 用 binlog」是穩定 fact、不必加 <em>as of N</em></li>
<li>過度標 <em>as of N</em> 會讓 article 變 verbose、只對 <em>limitation claim</em> 跟 <em>vendor-specific behavior</em> 套用</li>
</ul>
<h2 id="觸發再評估">觸發再評估</h2>
<p>未來累積到以下情境、本卡應 review：</p>
<ul>
<li>連續 2 個 batch 都踩 hallucinated CLI（trigger 升級到強制 <em>寫前 CLI verify</em>）</li>
<li>Feature claim 反轉 invalidates 整段流程的 case 超過 3 次（trigger 把 vendor article 改成 <em>每 N 個月 re-verify</em> 紀律）</li>
<li>LLM training cutoff 跟 vendor changelog 速度差變更大（trigger 升級 verify cadence）</li>
</ul>
]]></content:encoded></item><item><title>驗證導向的 CLI 工具文章：官方 docs 查核放過的落差類型</title><link>https://tarrragon.github.io/blog/posts/%E9%A9%97%E8%AD%89%E5%B0%8E%E5%90%91%E7%9A%84-cli-%E5%B7%A5%E5%85%B7%E6%96%87%E7%AB%A0%E5%AE%98%E6%96%B9-docs-%E6%9F%A5%E6%A0%B8%E6%94%BE%E9%81%8E%E7%9A%84%E8%90%BD%E5%B7%AE%E9%A1%9E%E5%9E%8B/</link><pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/%E9%A9%97%E8%AD%89%E5%B0%8E%E5%90%91%E7%9A%84-cli-%E5%B7%A5%E5%85%B7%E6%96%87%E7%AB%A0%E5%AE%98%E6%96%B9-docs-%E6%9F%A5%E6%A0%B8%E6%94%BE%E9%81%8E%E7%9A%84%E8%90%BD%E5%B7%AE%E9%A1%9E%E5%9E%8B/</guid><description>&lt;p>本文記錄驗證導向生產流程背後的 evidence — 為什麼官方文件查核不夠、實機驗證抓到了什麼。操作步驟維護在 &lt;code>.claude/skills/verification-driven-cli/&lt;/code>。&lt;/p>
&lt;h2 id="官方文件查核放過的五類落差">官方文件查核放過的五類落差&lt;/h2>
&lt;p>&lt;code>content/cli/&lt;/code> 五類終端機工具文章（監控 / 圖表 / 多工器 / 檔案管理 / SQL 客戶端）在實機驗證時抓到、純靠 docs 查核會放過的落差：&lt;/p>
&lt;h3 id="1-旗標改名">1. 旗標改名&lt;/h3>
&lt;p>&lt;code>zellij web&lt;/code> 文件寫有 &lt;code>--bind&lt;/code>，實際 0.43.1 是分開的 &lt;code>--ip&lt;/code> 與 &lt;code>--port&lt;/code>。讀者照文件下指令會得到 unknown flag error、但不知道正確旗標是什麼。&lt;/p>
&lt;h3 id="2-設定鍵-migrate">2. 設定鍵 migrate&lt;/h3>
&lt;p>&lt;code>lazygit&lt;/code> 的 pager 設定文件寫 &lt;code>git.paging.pager&lt;/code>，新版 0.62.2 改成 &lt;code>git.pagers&lt;/code>（list）。舊鍵啟動時會被自動 migrate、改寫設定檔 — 讀者照舊文件設定後發現設定檔被工具自己改掉。&lt;/p>
&lt;h3 id="3-隱含-schema-prefix">3. 隱含 schema prefix&lt;/h3>
&lt;p>&lt;code>dblab&lt;/code> 的查詢編輯器要 schema 限定（&lt;code>SELECT * FROM public.products&lt;/code>），裸 &lt;code>products&lt;/code> 會報 relation 不存在。原因是編輯器連線的 search_path 不含 public — 文件沒提。&lt;/p>
&lt;h3 id="4-平台特定-segfault">4. 平台特定 segfault&lt;/h3>
&lt;p>&lt;code>nvtop&lt;/code> 在 Apple Silicon mac 裝得起來，但 snapshot 模式直接 segfault。GPU 後端不穩。裝成功不代表能用 — 文件只說「支援 macOS」。&lt;/p>
&lt;h3 id="5-driver-差異">5. Driver 差異&lt;/h3>
&lt;p>同一個 Postgres，&lt;code>lazysql&lt;/code>（Go pq driver）連無 SSL 的 DB 要 &lt;code>?sslmode=disable&lt;/code>，&lt;code>pgcli&lt;/code> / &lt;code>harlequin&lt;/code>（Python psycopg）不用。同樣的連線字串在不同工具會有不同行為、文件各自不提對方。&lt;/p>
&lt;h2 id="共通模式">共通模式&lt;/h2>
&lt;p>這五類落差有個共通點：讀者照文件走會撞牆、卻在文件裡找不到答案。實機跑一次就現形，而且現形的正是文章最該寫的內容 — gotcha 段落省下讀者各自撞一次的時間。&lt;/p>
&lt;p>官方文件的 fact-check 只能驗證「文件說的是否正確」，驗不了「文件沒說的是否存在」。實機驗證補的是後者。&lt;/p>
&lt;h2 id="相關連結">相關連結&lt;/h2>
&lt;ul>
&lt;li>Verification-driven CLI skill（&lt;code>.claude/skills/verification-driven-cli/&lt;/code>）— 操作步驟&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/linux/tools/cli/cli-graphical-tools-overview/" data-link-title="終端機圖形化工具總覽：遠端操作下的 TUI、文字圖表與多工器" data-link-desc="在純文字終端機裡用 ASCII 與製圖字元做出監控儀表板、資料圖表與多視窗操作的工具總覽，並針對 SSH 伺服器、手機平板、低頻寬三種遠端情境給出選型判讀。">終端機圖形化工具總覽&lt;/a> — 用此流程生產的工具文章&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/report/single-function-per-article-sop-vs-retrospective/" data-link-title="一篇文章只承擔一種功能：SOP 跟 retrospective 混寫兩邊都做不好" data-link-desc="文章同時塞操作步驟（SOP）和批次驗證紀錄（retrospective）時，機器讀者找不到可執行的步驟、人類讀者不知道哪段是給自己看的。">#199 一篇文章只承擔一種功能&lt;/a> — 本文精簡的依據&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>本文記錄驗證導向生產流程背後的 evidence — 為什麼官方文件查核不夠、實機驗證抓到了什麼。操作步驟維護在 <code>.claude/skills/verification-driven-cli/</code>。</p>
<h2 id="官方文件查核放過的五類落差">官方文件查核放過的五類落差</h2>
<p><code>content/cli/</code> 五類終端機工具文章（監控 / 圖表 / 多工器 / 檔案管理 / SQL 客戶端）在實機驗證時抓到、純靠 docs 查核會放過的落差：</p>
<h3 id="1-旗標改名">1. 旗標改名</h3>
<p><code>zellij web</code> 文件寫有 <code>--bind</code>，實際 0.43.1 是分開的 <code>--ip</code> 與 <code>--port</code>。讀者照文件下指令會得到 unknown flag error、但不知道正確旗標是什麼。</p>
<h3 id="2-設定鍵-migrate">2. 設定鍵 migrate</h3>
<p><code>lazygit</code> 的 pager 設定文件寫 <code>git.paging.pager</code>，新版 0.62.2 改成 <code>git.pagers</code>（list）。舊鍵啟動時會被自動 migrate、改寫設定檔 — 讀者照舊文件設定後發現設定檔被工具自己改掉。</p>
<h3 id="3-隱含-schema-prefix">3. 隱含 schema prefix</h3>
<p><code>dblab</code> 的查詢編輯器要 schema 限定（<code>SELECT * FROM public.products</code>），裸 <code>products</code> 會報 relation 不存在。原因是編輯器連線的 search_path 不含 public — 文件沒提。</p>
<h3 id="4-平台特定-segfault">4. 平台特定 segfault</h3>
<p><code>nvtop</code> 在 Apple Silicon mac 裝得起來，但 snapshot 模式直接 segfault。GPU 後端不穩。裝成功不代表能用 — 文件只說「支援 macOS」。</p>
<h3 id="5-driver-差異">5. Driver 差異</h3>
<p>同一個 Postgres，<code>lazysql</code>（Go pq driver）連無 SSL 的 DB 要 <code>?sslmode=disable</code>，<code>pgcli</code> / <code>harlequin</code>（Python psycopg）不用。同樣的連線字串在不同工具會有不同行為、文件各自不提對方。</p>
<h2 id="共通模式">共通模式</h2>
<p>這五類落差有個共通點：讀者照文件走會撞牆、卻在文件裡找不到答案。實機跑一次就現形，而且現形的正是文章最該寫的內容 — gotcha 段落省下讀者各自撞一次的時間。</p>
<p>官方文件的 fact-check 只能驗證「文件說的是否正確」，驗不了「文件沒說的是否存在」。實機驗證補的是後者。</p>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Verification-driven CLI skill（<code>.claude/skills/verification-driven-cli/</code>）— 操作步驟</li>
<li><a href="/blog/linux/tools/cli/cli-graphical-tools-overview/" data-link-title="終端機圖形化工具總覽：遠端操作下的 TUI、文字圖表與多工器" data-link-desc="在純文字終端機裡用 ASCII 與製圖字元做出監控儀表板、資料圖表與多視窗操作的工具總覽，並針對 SSH 伺服器、手機平板、低頻寬三種遠端情境給出選型判讀。">終端機圖形化工具總覽</a> — 用此流程生產的工具文章</li>
<li><a href="/blog/report/single-function-per-article-sop-vs-retrospective/" data-link-title="一篇文章只承擔一種功能：SOP 跟 retrospective 混寫兩邊都做不好" data-link-desc="文章同時塞操作步驟（SOP）和批次驗證紀錄（retrospective）時，機器讀者找不到可執行的步驟、人類讀者不知道哪段是給自己看的。">#199 一篇文章只承擔一種功能</a> — 本文精簡的依據</li>
</ul>
]]></content:encoded></item></channel></rss>