<?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>Identifier on Tarragon</title><link>https://tarrragon.github.io/blog/tags/identifier/</link><description>Recent content in Identifier on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 28 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/identifier/index.xml" rel="self" type="application/rss+xml"/><item><title>URL slug 必須顯式定義為 fact：跨工具 identifier 用單一定義源</title><link>https://tarrragon.github.io/blog/report/url-slug-must-be-explicit-fact/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/url-slug-must-be-explicit-fact/</guid><description>&lt;h2 id="結論">結論&lt;/h2>
&lt;p>跨工具共用的 identifier（URL slug、API endpoint、route name、檔案 ID）必須&lt;strong>顯式定義在一處 fact&lt;/strong>、不能依賴各工具各自推導。多工具各自推導 = 推導鏈分歧 = silent 失敗（compile / lint 時看不出、跨工具接縫時才爆）。&lt;/p>
&lt;p>具體到 Hugo blog 的 URL slug：&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>Hugo 自動推導&lt;/td>
 &lt;td>&lt;code>title&lt;/code> 經 &lt;code>urlize&lt;/code>&lt;/td>
 &lt;td>runtime build&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>mdtools 字面比對&lt;/td>
 &lt;td>檔名（不含 &lt;code>.md&lt;/code>）&lt;/td>
 &lt;td>pre-commit&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>跨檔連結時的引用值&lt;/td>
 &lt;td>寫作者手動算 / 複製&lt;/td>
 &lt;td>寫作時&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>三個推導鏈 — 寫作者寫 &lt;code>[link](/posts/X/)&lt;/code> 時、X 應該是哪個？沒有 single source 給答案。&lt;/p>
&lt;p>&lt;strong>修法&lt;/strong>：把 slug 從 derivation 升級成 fact — 在 frontmatter 顯式定義 &lt;code>slug: &amp;lt;name&amp;gt;&lt;/code>、跟檔名對齊、所有工具基於此 fact 運作、跨檔連結用此 slug。&lt;/p>
&lt;hr>
&lt;h2 id="為什麼會散落">為什麼會散落&lt;/h2>
&lt;h3 id="各工具的預設行為都合理但不一致">各工具的預設行為都「合理但不一致」&lt;/h3>
&lt;p>每個工具在自己的領域內都做了「合理的決定」、合起來才產生不一致：&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>Hugo&lt;/td>
 &lt;td>title → slug 推導&lt;/td>
 &lt;td>不寫 slug 也能 build、降低門檻&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>mdtools&lt;/td>
 &lt;td>檔名 = slug&lt;/td>
 &lt;td>字面 lint、不執行 hugo runtime&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>寫作者&lt;/td>
 &lt;td>看心情寫&lt;/td>
 &lt;td>沒規範就靠記憶 / 複製貼上&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>每個決定本身沒錯、合起來形成「&lt;strong>沒有單一真相&lt;/strong>」的狀態。&lt;/p>
&lt;h3 id="hugo-的-urlize-不是純函式">Hugo 的 &lt;code>urlize&lt;/code> 不是純函式&lt;/h3>
&lt;p>Hugo 的 title → slug 推導用 &lt;code>urlize&lt;/code>、規則隨版本演進、對中文 / 全形字元 / 連字符的處理會變。寫的當下推導出來的 slug、未來 hugo 升級後可能變不同 — 這是「&lt;strong>runtime 推導 = 隱性依賴 hugo 版本&lt;/strong>」。&lt;/p>
&lt;p>而 frontmatter 的 &lt;code>slug: &amp;lt;value&amp;gt;&lt;/code> 是字面值、不依賴任何工具的推導邏輯、跨版本穩定。&lt;/p>
&lt;h3 id="能-build-就不寫是便利驅動偏移67">「能 build 就不寫」是便利驅動偏移（&lt;a href="../ease-of-writing-vs-intent-alignment/">#67&lt;/a>）&lt;/h3>
&lt;p>Hugo 不寫 slug 也能 build — 寫作的當下、加 slug 是「多餘工作」、看起來沒收益。便利驅動讓寫作者跳過。但這個便利是&lt;strong>借用未來的成本&lt;/strong> — 跨檔連結時、slug 推導不一致才暴露、那時要付的修復成本遠大於當初寫 slug 的成本。&lt;/p>
&lt;hr>
&lt;h2 id="fact-vs-derivationslug-該是哪一種">Fact vs Derivation：slug 該是哪一種&lt;/h2>
&lt;p>呼應 &lt;a href="../single-source-of-truth/">#44&lt;/a> 的區分：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>類型&lt;/th>
 &lt;th>定義&lt;/th>
 &lt;th>slug 的歸類&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Fact&lt;/strong>&lt;/td>
 &lt;td>設計決定、不能從別處算出&lt;/td>
 &lt;td>slug 應屬此類&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Derivation&lt;/strong>&lt;/td>
 &lt;td>從 fact 計算得出、無自主性&lt;/td>
 &lt;td>slug 不該屬此類&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>slug 必須是 fact&lt;/strong>、不是 derivation。理由：&lt;/p>
&lt;ul>
&lt;li>slug 的「值」是設計選擇 — 用什麼字串作為 URL 一部分、是 SEO / 可讀性 / 穩定性的決定、不該被自動推導左右&lt;/li>
&lt;li>一旦固定後就&lt;strong>不能改&lt;/strong>（改 slug = URL 改 = 外部連結全部死）&lt;/li>
&lt;li>「不能改」+「設計決定」= 應該是 fact&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>hugo 的 title→slug 推導&lt;/strong>：把一個 fact 偽裝成 derivation。表面上看「我只寫 title、slug 自動算出來」、實際上推導出來的 slug 變成了一個&lt;strong>新的 fact&lt;/strong>（一旦發布就不能改）、但這個 fact 的住址不在程式碼裡、在 hugo runtime 裡。&lt;/p></description><content:encoded><![CDATA[<h2 id="結論">結論</h2>
<p>跨工具共用的 identifier（URL slug、API endpoint、route name、檔案 ID）必須<strong>顯式定義在一處 fact</strong>、不能依賴各工具各自推導。多工具各自推導 = 推導鏈分歧 = silent 失敗（compile / lint 時看不出、跨工具接縫時才爆）。</p>
<p>具體到 Hugo blog 的 URL slug：</p>
<table>
  <thead>
      <tr>
          <th>推導鏈</th>
          <th>來源</th>
          <th>觸發時機</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hugo 自動推導</td>
          <td><code>title</code> 經 <code>urlize</code></td>
          <td>runtime build</td>
      </tr>
      <tr>
          <td>mdtools 字面比對</td>
          <td>檔名（不含 <code>.md</code>）</td>
          <td>pre-commit</td>
      </tr>
      <tr>
          <td>跨檔連結時的引用值</td>
          <td>寫作者手動算 / 複製</td>
          <td>寫作時</td>
      </tr>
  </tbody>
</table>
<p>三個推導鏈 — 寫作者寫 <code>[link](/posts/X/)</code> 時、X 應該是哪個？沒有 single source 給答案。</p>
<p><strong>修法</strong>：把 slug 從 derivation 升級成 fact — 在 frontmatter 顯式定義 <code>slug: &lt;name&gt;</code>、跟檔名對齊、所有工具基於此 fact 運作、跨檔連結用此 slug。</p>
<hr>
<h2 id="為什麼會散落">為什麼會散落</h2>
<h3 id="各工具的預設行為都合理但不一致">各工具的預設行為都「合理但不一致」</h3>
<p>每個工具在自己的領域內都做了「合理的決定」、合起來才產生不一致：</p>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>推導決定</th>
          <th>為什麼合理</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hugo</td>
          <td>title → slug 推導</td>
          <td>不寫 slug 也能 build、降低門檻</td>
      </tr>
      <tr>
          <td>mdtools</td>
          <td>檔名 = slug</td>
          <td>字面 lint、不執行 hugo runtime</td>
      </tr>
      <tr>
          <td>寫作者</td>
          <td>看心情寫</td>
          <td>沒規範就靠記憶 / 複製貼上</td>
      </tr>
  </tbody>
</table>
<p>每個決定本身沒錯、合起來形成「<strong>沒有單一真相</strong>」的狀態。</p>
<h3 id="hugo-的-urlize-不是純函式">Hugo 的 <code>urlize</code> 不是純函式</h3>
<p>Hugo 的 title → slug 推導用 <code>urlize</code>、規則隨版本演進、對中文 / 全形字元 / 連字符的處理會變。寫的當下推導出來的 slug、未來 hugo 升級後可能變不同 — 這是「<strong>runtime 推導 = 隱性依賴 hugo 版本</strong>」。</p>
<p>而 frontmatter 的 <code>slug: &lt;value&gt;</code> 是字面值、不依賴任何工具的推導邏輯、跨版本穩定。</p>
<h3 id="能-build-就不寫是便利驅動偏移67">「能 build 就不寫」是便利驅動偏移（<a href="../ease-of-writing-vs-intent-alignment/">#67</a>）</h3>
<p>Hugo 不寫 slug 也能 build — 寫作的當下、加 slug 是「多餘工作」、看起來沒收益。便利驅動讓寫作者跳過。但這個便利是<strong>借用未來的成本</strong> — 跨檔連結時、slug 推導不一致才暴露、那時要付的修復成本遠大於當初寫 slug 的成本。</p>
<hr>
<h2 id="fact-vs-derivationslug-該是哪一種">Fact vs Derivation：slug 該是哪一種</h2>
<p>呼應 <a href="../single-source-of-truth/">#44</a> 的區分：</p>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>定義</th>
          <th>slug 的歸類</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Fact</strong></td>
          <td>設計決定、不能從別處算出</td>
          <td>slug 應屬此類</td>
      </tr>
      <tr>
          <td><strong>Derivation</strong></td>
          <td>從 fact 計算得出、無自主性</td>
          <td>slug 不該屬此類</td>
      </tr>
  </tbody>
</table>
<p><strong>slug 必須是 fact</strong>、不是 derivation。理由：</p>
<ul>
<li>slug 的「值」是設計選擇 — 用什麼字串作為 URL 一部分、是 SEO / 可讀性 / 穩定性的決定、不該被自動推導左右</li>
<li>一旦固定後就<strong>不能改</strong>（改 slug = URL 改 = 外部連結全部死）</li>
<li>「不能改」+「設計決定」= 應該是 fact</li>
</ul>
<p><strong>hugo 的 title→slug 推導</strong>：把一個 fact 偽裝成 derivation。表面上看「我只寫 title、slug 自動算出來」、實際上推導出來的 slug 變成了一個<strong>新的 fact</strong>（一旦發布就不能改）、但這個 fact 的住址不在程式碼裡、在 hugo runtime 裡。</p>
<hr>
<h2 id="反模式分散的-derivation-鏈">反模式：分散的 derivation 鏈</h2>
<h3 id="多工具各自推導--silent-不一致">多工具各自推導 = silent 不一致</h3>
<p>當多個工具各自從不同 source derive 同一個 identifier、寫的當下都通過、跨工具接縫時才爆：</p>
<table>
  <thead>
      <tr>
          <th>工具 X 看到</th>
          <th>工具 Y 看到</th>
          <th>看到時機</th>
          <th>後果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>一致</td>
          <td>一致</td>
          <td>寫作時</td>
          <td>表面 OK、累積債</td>
      </tr>
      <tr>
          <td>一致</td>
          <td>不一致</td>
          <td>跨工具時</td>
          <td>broken link / build fail</td>
      </tr>
      <tr>
          <td>不一致</td>
          <td>不一致</td>
          <td>多版本時</td>
          <td>升級後新舊推導規則不一致</td>
      </tr>
  </tbody>
</table>
<p><strong>寫的當下看不出</strong>、是這個反模式的核心難處。</p>
<h3 id="規則膨脹誘惑教-mdtools-認-hugo-規則">「規則膨脹」誘惑：教 mdtools 認 hugo 規則</h3>
<p>碰到 mdtools 不認 hugo title 推導時、直覺反應是「教 mdtools 也跑 urlize」。這是<strong>用字面工具模擬行為層</strong>（<a href="../literal-interception-vs-behavioral-refinement/">#82</a> 的反模式）：</p>
<ul>
<li>mdtools 是字面 lint、學會 urlize → 增加實作成本、要追 hugo 版本變動</li>
<li>解決了表面症狀、但根因（slug 是 derivation）沒動</li>
<li>下一個工具（如 search index）加進來、又要再學一次 urlize</li>
</ul>
<p>正解是<strong>消滅 derivation 鏈、把 slug 升成 fact</strong>。每個工具直接讀 fact、不需要學別人的推導規則。</p>
<h3 id="之後再補-slug的-trigger-缺失">「之後再補 slug」的 trigger 缺失</h3>
<p>「先這樣、之後系統性 backfill」是 <a href="../external-trigger-for-high-roi-work/">#72</a> 的典型訊號。沒有 trigger 時、debt 永遠累積：</p>
<ul>
<li>175 篇文章沒 slug、每多寫一篇 debt 多一份</li>
<li>backfill 沒被排上 → 永遠不做</li>
<li>直到某天有人引用中文 title 的文章、broken link 才浮現</li>
</ul>
<p>修法：lint 規則加 <code>missing-slug</code> check、把 trigger 結構性建立（<a href="../escalation-trigger-quantification/">#91</a> 量化 trigger 設計）。</p>
<hr>
<h2 id="修法兩層補強">修法：兩層補強</h2>
<h3 id="規範層解根因">規範層（解根因）</h3>
<p>每篇 content 文章 frontmatter 必須有 <code>slug</code>、值跟檔名對齊（不含副檔名）：</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="nn">---</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">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Hugo 部落格支援 Mermaid 流程圖完整實現指南&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="nt">slug</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;mermaid-gitgraph&#34;</span><span class="w">   </span><span class="c"># 跟檔名 mermaid-gitgraph.md 對齊</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2025-10-08</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"></span><span class="nn">---</span></span></span></code></pre></div><p>寫好後：</p>
<ul>
<li>Hugo 用 <code>slug:</code> 不再 derive title</li>
<li>mdtools 用檔名比對 frontmatter slug、字面對齊就過</li>
<li>跨檔連結 <code>[...](/posts/mermaid-gitgraph/)</code> 直接基於 slug、不需推算</li>
<li>SSoT 集中在 frontmatter、檔名是 mirror（自動驗證一致性）</li>
</ul>
<h3 id="工具層防呆">工具層（防呆）</h3>
<p>mdtools 加 lint 規則：</p>
<table>
  <thead>
      <tr>
          <th>規則 ID</th>
          <th>檢查</th>
          <th>error / warn</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>L1-missing-slug</td>
          <td>content 文章 frontmatter 缺 slug</td>
          <td>error（強制）</td>
      </tr>
      <tr>
          <td>L1-slug-filename-mismatch</td>
          <td>slug != 檔名 stem</td>
          <td>error</td>
      </tr>
      <tr>
          <td>L2-broken-internal-link</td>
          <td><code>/posts/&lt;slug&gt;/</code> slug 不存在</td>
          <td>error（既有）</td>
      </tr>
  </tbody>
</table>
<p>把問題從「跨檔 link 時 broken」提前到「寫作時就 catch」。</p>
<h3 id="歷史-backfill">歷史 backfill</h3>
<p>175 篇沒 slug 的文章需要 backfill。可寫 script：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">for</span> f in content/posts/*.md content/work-log/*.md content/record/*.md content/report/*.md<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nv">slug</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="nv">$f</span><span class="s2">&#34;</span> .md<span class="k">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">if</span> ! grep -q <span class="s2">&#34;^slug:&#34;</span> <span class="s2">&#34;</span><span class="nv">$f</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="c1"># 在 date: 後插入 slug: &lt;檔名&gt;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    sed -i.bak <span class="s2">&#34;/^date:/a\\
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="s2">slug: \&#34;</span><span class="nv">$slug</span><span class="s2">\&#34;&#34;</span> <span class="s2">&#34;</span><span class="nv">$f</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="k">done</span></span></span></code></pre></div><p>人工 review 確認 slug 沒衝突、commit。</p>
<hr>
<h2 id="跟其他抽象層原則的關係">跟其他抽象層原則的關係</h2>
<table>
  <thead>
      <tr>
          <th>原則</th>
          <th>關係</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="../single-source-of-truth/">#44 Single Source of Truth</a></td>
          <td><strong>本卡是 #44 在 identifier 維度的具體實例</strong> — slug 散落三處、fact 升級為主修法</td>
      </tr>
      <tr>
          <td><a href="../literal-interception-vs-behavioral-refinement/">#82 字面攔截 vs 行為精煉</a></td>
          <td>mdtools 是字面 lint、hugo urlize 是 runtime 行為 — 兩層之間的 gap 用「教字面學行為」解 = 規則膨脹、正解是消除 derivation</td>
      </tr>
      <tr>
          <td><a href="../ease-of-writing-vs-intent-alignment/">#67 寫作便利度跟意圖對齊反相關</a></td>
          <td>「能 build 就不寫 slug」是便利寫法、「顯式寫 slug」是對齊意圖（不依賴推導）</td>
      </tr>
      <tr>
          <td><a href="../external-trigger-for-high-roi-work/">#72 高 ROI 無外部觸發</a></td>
          <td>「之後系統性補 slug」沒 trigger = 永遠不會做、175 篇累積債就是這條訊號</td>
      </tr>
      <tr>
          <td><a href="../escalation-trigger-quantification/">#91 升級 trigger 的量化設計</a></td>
          <td>補 lint 規則是 trigger、把「應該補 slug」從紀律升級成結構性檢查</td>
      </tr>
      <tr>
          <td><a href="../visual-tool-error-layer-alignment/">#92 視覺手段對齊錯誤層次</a></td>
          <td>同骨：工具的 ceiling（mdtools 字面 vs hugo runtime）超出就 false confidence</td>
      </tr>
  </tbody>
</table>
<p>本卡跟 #82 / #92 共同形成「<strong>工具 ceiling pattern 系列</strong>」 — 每個工具都有能擋的層 / 擋不到的層、跨層之間需要「升級 fact」或「換工具」、不是「教工具學別人的規則」。</p>
<hr>
<h2 id="套用到本系統的-case">套用到本系統的 case</h2>
<h3 id="case-1175-篇-0-slug-的累積債">Case 1：175 篇 0 slug 的累積債</h3>
<p>實證資料（2026-04-28 撤查）：</p>
<table>
  <thead>
      <tr>
          <th>資料夾</th>
          <th>文章數</th>
          <th>有 frontmatter slug</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>posts/</td>
          <td>17</td>
          <td>0</td>
      </tr>
      <tr>
          <td>work-log/</td>
          <td>12</td>
          <td>0</td>
      </tr>
      <tr>
          <td>record/</td>
          <td>53</td>
          <td>0</td>
      </tr>
      <tr>
          <td>report/</td>
          <td>93</td>
          <td>0</td>
      </tr>
      <tr>
          <td><strong>合計</strong></td>
          <td>175</td>
          <td><strong>0</strong></td>
      </tr>
  </tbody>
</table>
<p>每一篇都是潛在的 broken link 觸發點、debt 未爆出來只因為「英文檔名跟 hugo 推導剛好一樣」。</p>
<h3 id="case-2mermaid-流程圖文章的引用-broken">Case 2：mermaid 流程圖文章的引用 broken</h3>
<p>寫 <a href="../visual-tool-error-layer-alignment/">#92</a> 的 case 2 提到 <code>mermaid_gitgraph_type_color_config</code> 文章、想連到既有的 <code>mermaid流程圖.md</code>。實際軌跡：</p>
<ol>
<li>第一直覺：寫 <code>[...](/posts/hugo-部落格支援-mermaid-流程圖完整實現指南/)</code>（hugo 推導出來的 URL）</li>
<li>mdtools L1-broken-link 失敗、它認檔名 <code>mermaid流程圖.md</code></li>
<li>改寫 <code>[...](/posts/mermaid流程圖/)</code>、hugo build 後變 404（因為 hugo 認 title 推導的 slug）</li>
<li>退而求其次：去掉超連結、改純文字提及</li>
</ol>
<p>問題的根本是「mermaid流程圖.md 沒寫 slug」 — fact 缺失、就只能在「mdtools 認的字面」跟「hugo 認的推導」中二選一、兩者都不對。</p>
<p>正解：給 mermaid流程圖.md 補 <code>slug: mermaid-gitgraph</code> 或類似、檔名 rename 對齊、所有工具基於同一 fact。</p>
<h3 id="case-3跨工具-identifier-的通用-pattern">Case 3：跨工具 identifier 的通用 pattern</h3>
<p>不只是 hugo blog — 任何「多工具共用 identifier」的情境都同樣 pattern：</p>
<table>
  <thead>
      <tr>
          <th>領域</th>
          <th>identifier</th>
          <th>散落的推導鏈</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hugo blog</td>
          <td>URL slug</td>
          <td>檔名 / hugo title / frontmatter</td>
      </tr>
      <tr>
          <td>API server</td>
          <td>endpoint route</td>
          <td>controller path / OpenAPI spec / client SDK</td>
      </tr>
      <tr>
          <td>DB migration</td>
          <td>migration ID</td>
          <td>檔名 / hash / sequence</td>
      </tr>
      <tr>
          <td>Frontend route</td>
          <td>path identifier</td>
          <td>檔案位置 / route config / navigation</td>
      </tr>
      <tr>
          <td>LLM tool name</td>
          <td>tool 名稱</td>
          <td>function name / schema / prompt 引用</td>
      </tr>
  </tbody>
</table>
<p>每一類的修法都一樣：<strong>把 identifier 升成 fact、所有工具基於此 fact</strong>、不要讓各工具各自推導。</p>
<hr>
<h2 id="判讀徵兆">判讀徵兆</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>該做的事</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「這個 link 為什麼 broken」debug 半天</td>
          <td>推導鏈不一致、檢查 identifier 有沒有顯式 fact</td>
      </tr>
      <tr>
          <td>「教這個工具認另一個工具的規則」</td>
          <td>規則膨脹的開始、正解是消除 derivation</td>
      </tr>
      <tr>
          <td>「能跑就不寫 X 欄位」</td>
          <td>便利驅動、未來會在跨工具接縫爆</td>
      </tr>
      <tr>
          <td>「之後系統性補 backfill」</td>
          <td><a href="../external-trigger-for-high-roi-work/">#72</a> 缺 trigger、會永遠跳過</td>
      </tr>
      <tr>
          <td>兩個工具對同個 ID 算出不同值</td>
          <td>多源 derivation、改成單一 fact</td>
      </tr>
      <tr>
          <td>升級工具版本後 link / route 全壞</td>
          <td>依賴 runtime 推導、推導規則隨版本變</td>
      </tr>
      <tr>
          <td>「我手動算一下這個 slug 應該是什麼」</td>
          <td>identifier 不該需要心算、補 fact</td>
      </tr>
      <tr>
          <td>Lint 不報錯但 production broken</td>
          <td>字面 lint 跟 runtime 行為的 gap、補 lint 規則或補 fact</td>
      </tr>
  </tbody>
</table>
<p><strong>核心</strong>：跨工具共用的 identifier 必須是 fact、不是 derivation。<strong>Derivation 鏈把單一值散落在多工具的推導邏輯裡、寫的當下看不出問題、跨工具接縫才爆 — 而那時候 debt 已經累積到難以集中修</strong>。Fact 升級的成本（每篇加一行 frontmatter）遠小於 derivation 鏈失敗的修復成本（broken link / SEO 損失 / debugging 時間）。</p>
]]></content:encoded></item></channel></rss>