<?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>Blog心得 on Tarragon</title><link>https://tarrragon.github.io/blog/tags/blog%E5%BF%83%E5%BE%97/</link><description>Recent content in Blog心得 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Sat, 02 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/blog%E5%BF%83%E5%BE%97/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog 文章模板設計：作者品質閘門與正文分工</title><link>https://tarrragon.github.io/blog/posts/blog-article-template-design/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/blog-article-template-design/</guid><description>&lt;h2 id="問題定位">問題定位&lt;/h2>
&lt;p>文章模板的責任是穩定作者流程，正文的責任是承載技術文章本身。讀者進入文章時，需要看到概念、判讀、取捨與路由；作者在寫作時，需要一組欄位檢查文章是否具備可維護的最低結構。&lt;/p>
&lt;p>本 blog 同時由人類作者、Claude Code 與 Codex 協作產生內容。模板若只放在單一 agent 的設定裡，就會形成工具分岔；模板若直接放進 backend 正文，又會把作者工作流暴露成讀者負擔。因此模板的單一真實來源放在 &lt;code>content/posts/&lt;/code>，作為本 blog 專屬的寫作設定記錄。&lt;/p>
&lt;h2 id="放置決策">放置決策&lt;/h2>
&lt;p>模板放置位置的核心判準是讀者與維護者是否一致。backend 文章面向技術讀者，report 面向可重用事後檢討，posts 面向 blog 自身的規範、設計與工具鏈紀錄。&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>&lt;code>content/backend/&lt;/code>&lt;/td>
 &lt;td>技術文章正文、概念推導、案例分析&lt;/td>
 &lt;td>保持讀者主線，作者模板留在上游&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>content/report/&lt;/code>&lt;/td>
 &lt;td>從具體 case 抽出的工程原則&lt;/td>
 &lt;td>可寫抽象原則，操作模板留在 posts&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>content/posts/&lt;/code>&lt;/td>
 &lt;td>blog 規範、設計決策、工具鏈契約&lt;/td>
 &lt;td>作為模板設計的 SSoT&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>.claude/&lt;/code>&lt;/td>
 &lt;td>Claude Code 執行規則&lt;/td>
 &lt;td>可引用本文，本文維持語意來源&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>.codex/&lt;/code>&lt;/td>
 &lt;td>Codex 執行規則&lt;/td>
 &lt;td>可引用本文，本文維持語意來源&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個分工讓模板同時支援 Claude Code 與 Codex，也保留文章對讀者的自然技術敘事。&lt;/p>
&lt;h2 id="模板責任">模板責任&lt;/h2>
&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>責任&lt;/td>
 &lt;td>這篇文章解決哪一類工程問題&lt;/td>
 &lt;td>概念定位或開頭原則段&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>判讀&lt;/td>
 &lt;td>讀者如何知道自己遇到這個問題&lt;/td>
 &lt;td>核心判讀、判讀訊號、表格&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>風險&lt;/td>
 &lt;td>判讀錯誤或缺漏會造成什麼代價&lt;/td>
 &lt;td>風險段、反模式、情境段&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>邊界&lt;/td>
 &lt;td>這篇文章處理到哪裡，交給誰接續&lt;/td>
 &lt;td>交接路由、與其他章節分工&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>模板欄位可以出現在文章大綱、作者檢查清單或 agent brief 裡。正文要以技術文章方式展開，讓讀者看到推導，作者填表痕跡留在工作流內部。&lt;/p>
&lt;h2 id="backend-技術文章最小模板">Backend 技術文章最小模板&lt;/h2>
&lt;p>Backend 技術文章的最小模板是「概念定位 → 核心判讀 → 判讀訊號 → 風險與邊界 → 交接路由」。這組欄位適合 04 / 06 / 08 這類語言無關的後端能力文章。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="gu">## 概念定位
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">{概念} 是 {能力 / 流程 / 控制面}，責任是 {它在系統中承擔什麼問題}。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">這一頁處理的是 {抽象層級}。它跟 {相鄰章節} 的分工是 {邊界}。
&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 class="gu">## 核心判讀
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">判讀 {概念} 時，先看 {第一判準}，再看 {第二判準}。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">重點訊號包括：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 1}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 2}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 3}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">| 判讀面向 | 最小可用判準 | 常見失真 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">| --- | --- | --- |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">| {面向} | {判準} | {失真} |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="gu">## 判讀訊號
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {真實服務中會看到的徵兆}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {工程團隊會踩到的操作問題}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {事故或演練會暴露的缺口}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="gu">## 交接路由
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {上游章節}：{承接內容}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">- {下游章節}：{下一步處理}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這份模板只定義文章最低結構。若某篇文章需要完整案例、方案比較或實作步驟，正文可以增加章節；增加章節時仍要保留責任、判讀、風險與路由。&lt;/p>
&lt;h2 id="案例前置欄位">案例前置欄位&lt;/h2>
&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>來源&lt;/td>
 &lt;td>案例來自事故、演練或公開實踐&lt;/td>
 &lt;td>post-incident review、SRE case&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>觸發&lt;/td>
 &lt;td>事件如何被發現&lt;/td>
 &lt;td>alert、customer ticket、vendor status&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence&lt;/td>
 &lt;td>判讀使用哪些證據&lt;/td>
 &lt;td>log、metric、trace、audit log&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Decision&lt;/td>
 &lt;td>當時做了什麼取捨&lt;/td>
 &lt;td>rollback、degradation、containment&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Impact&lt;/td>
 &lt;td>影響到誰與什麼功能&lt;/td>
 &lt;td>tenant、region、feature、financial impact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回寫&lt;/td>
 &lt;td>教訓回到哪個章節&lt;/td>
 &lt;td>04.17、6.20、8.19&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>04 / 06 / 08 的案例拆解可共用這組欄位，但正文仍要用技術文章敘事。欄位幫作者維持可比性，文章幫讀者理解機制。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題定位">問題定位</h2>
<p>文章模板的責任是穩定作者流程，正文的責任是承載技術文章本身。讀者進入文章時，需要看到概念、判讀、取捨與路由；作者在寫作時，需要一組欄位檢查文章是否具備可維護的最低結構。</p>
<p>本 blog 同時由人類作者、Claude Code 與 Codex 協作產生內容。模板若只放在單一 agent 的設定裡，就會形成工具分岔；模板若直接放進 backend 正文，又會把作者工作流暴露成讀者負擔。因此模板的單一真實來源放在 <code>content/posts/</code>，作為本 blog 專屬的寫作設定記錄。</p>
<h2 id="放置決策">放置決策</h2>
<p>模板放置位置的核心判準是讀者與維護者是否一致。backend 文章面向技術讀者，report 面向可重用事後檢討，posts 面向 blog 自身的規範、設計與工具鏈紀錄。</p>
<table>
  <thead>
      <tr>
          <th>位置</th>
          <th>適合內容</th>
          <th>本議題判斷</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>content/backend/</code></td>
          <td>技術文章正文、概念推導、案例分析</td>
          <td>保持讀者主線，作者模板留在上游</td>
      </tr>
      <tr>
          <td><code>content/report/</code></td>
          <td>從具體 case 抽出的工程原則</td>
          <td>可寫抽象原則，操作模板留在 posts</td>
      </tr>
      <tr>
          <td><code>content/posts/</code></td>
          <td>blog 規範、設計決策、工具鏈契約</td>
          <td>作為模板設計的 SSoT</td>
      </tr>
      <tr>
          <td><code>.claude/</code></td>
          <td>Claude Code 執行規則</td>
          <td>可引用本文，本文維持語意來源</td>
      </tr>
      <tr>
          <td><code>.codex/</code></td>
          <td>Codex 執行規則</td>
          <td>可引用本文，本文維持語意來源</td>
      </tr>
  </tbody>
</table>
<p>這個分工讓模板同時支援 Claude Code 與 Codex，也保留文章對讀者的自然技術敘事。</p>
<h2 id="模板責任">模板責任</h2>
<p>模板是品質閘門，責任是讓文章保留關鍵判準。它要檢查文章是否具備責任、判讀、風險、邊界與下一步路由，正文仍用技術文章的推導順序排列。</p>
<table>
  <thead>
      <tr>
          <th>模板欄位</th>
          <th>作者要回答的問題</th>
          <th>正文呈現方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>責任</td>
          <td>這篇文章解決哪一類工程問題</td>
          <td>概念定位或開頭原則段</td>
      </tr>
      <tr>
          <td>判讀</td>
          <td>讀者如何知道自己遇到這個問題</td>
          <td>核心判讀、判讀訊號、表格</td>
      </tr>
      <tr>
          <td>風險</td>
          <td>判讀錯誤或缺漏會造成什麼代價</td>
          <td>風險段、反模式、情境段</td>
      </tr>
      <tr>
          <td>邊界</td>
          <td>這篇文章處理到哪裡，交給誰接續</td>
          <td>交接路由、與其他章節分工</td>
      </tr>
      <tr>
          <td>回寫</td>
          <td>案例或事故教訓應回寫到哪個章節</td>
          <td>下一步路由、復盤段</td>
      </tr>
  </tbody>
</table>
<p>模板欄位可以出現在文章大綱、作者檢查清單或 agent brief 裡。正文要以技術文章方式展開，讓讀者看到推導，作者填表痕跡留在工作流內部。</p>
<h2 id="backend-技術文章最小模板">Backend 技術文章最小模板</h2>
<p>Backend 技術文章的最小模板是「概念定位 → 核心判讀 → 判讀訊號 → 風險與邊界 → 交接路由」。這組欄位適合 04 / 06 / 08 這類語言無關的後端能力文章。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 概念定位
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">{概念} 是 {能力 / 流程 / 控制面}，責任是 {它在系統中承擔什麼問題}。
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">這一頁處理的是 {抽象層級}。它跟 {相鄰章節} 的分工是 {邊界}。
</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 class="gu">## 核心判讀
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">判讀 {概念} 時，先看 {第一判準}，再看 {第二判準}。
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">重點訊號包括：
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> {訊號 1}
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> {訊號 2}
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> {訊號 3}
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">| 判讀面向 | 最小可用判準 | 常見失真 |
</span></span><span class="line"><span class="ln">18</span><span class="cl">| --- | --- | --- |
</span></span><span class="line"><span class="ln">19</span><span class="cl">| {面向} | {判準} | {失真} |
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">## 判讀訊號
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">-</span> {真實服務中會看到的徵兆}
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> {工程團隊會踩到的操作問題}
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">-</span> {事故或演練會暴露的缺口}
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">## 交接路由
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> {上游章節}：{承接內容}
</span></span><span class="line"><span class="ln">30</span><span class="cl">- {下游章節}：{下一步處理}</span></span></code></pre></div><p>這份模板只定義文章最低結構。若某篇文章需要完整案例、方案比較或實作步驟，正文可以增加章節；增加章節時仍要保留責任、判讀、風險與路由。</p>
<h2 id="案例前置欄位">案例前置欄位</h2>
<p>案例前置欄位的責任是讓服務案例能回寫到文章系統。它屬於作者拆案例時的內部欄位，正文只吸收欄位背後的判讀與取捨。</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>用途</th>
          <th>例子</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>來源</td>
          <td>案例來自事故、演練或公開實踐</td>
          <td>post-incident review、SRE case</td>
      </tr>
      <tr>
          <td>觸發</td>
          <td>事件如何被發現</td>
          <td>alert、customer ticket、vendor status</td>
      </tr>
      <tr>
          <td>Evidence</td>
          <td>判讀使用哪些證據</td>
          <td>log、metric、trace、audit log</td>
      </tr>
      <tr>
          <td>Decision</td>
          <td>當時做了什麼取捨</td>
          <td>rollback、degradation、containment</td>
      </tr>
      <tr>
          <td>Impact</td>
          <td>影響到誰與什麼功能</td>
          <td>tenant、region、feature、financial impact</td>
      </tr>
      <tr>
          <td>回寫</td>
          <td>教訓回到哪個章節</td>
          <td>04.17、6.20、8.19</td>
      </tr>
  </tbody>
</table>
<p>04 / 06 / 08 的案例拆解可共用這組欄位，但正文仍要用技術文章敘事。欄位幫作者維持可比性，文章幫讀者理解機制。</p>
<h2 id="agent-共用方式">Agent 共用方式</h2>
<p>Claude Code 與 Codex 共用模板時，本文是 blog 內的穩定契約。<code>.claude/</code> 與 <code>.codex/</code> 可以引用本文的欄位與判準，但實際模板語意以本文為準。</p>
<table>
  <thead>
      <tr>
          <th>使用者</th>
          <th>使用方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>人類作者</td>
          <td>寫作前確認文章是否需要這組欄位</td>
      </tr>
      <tr>
          <td>Claude Code</td>
          <td>依本文判斷正文與作者模板的分工</td>
      </tr>
      <tr>
          <td>Codex</td>
          <td>依本文建立、補寫與檢查 content 文章</td>
      </tr>
      <tr>
          <td>mdtools</td>
          <td>檢查 Markdown 結構與連結，語意模板交給作者流程</td>
      </tr>
  </tbody>
</table>
<p>這個安排避免 <code>.claude/</code> 與 <code>.codex/</code> 各自演化成不同模板，也避免把 agent 操作細節寫進 backend 讀者正文。</p>
<h2 id="使用邊界">使用邊界</h2>
<p>模板適合用在多篇系列文章、跨模組路由與案例回寫。單篇短文、事故紀錄或工具使用筆記可以採較輕的結構，只要仍能說清楚核心責任與下一步。</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>使用方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Backend 能力章節</td>
          <td>使用完整最小模板</td>
      </tr>
      <tr>
          <td>服務案例拆解</td>
          <td>使用案例前置欄位，正文呈現案例判讀與取捨</td>
      </tr>
      <tr>
          <td>Blog 工具鏈規範</td>
          <td>依主題調整，保留 posts 的規範與工具鏈定位</td>
      </tr>
      <tr>
          <td>Report 原則卡</td>
          <td>依 report 固有結構，維持 case-driven 原則抽象</td>
      </tr>
      <tr>
          <td>Skill reference</td>
          <td>使用 skill 自身 portable 結構，維持跨專案可移植</td>
      </tr>
  </tbody>
</table>
<p>模板開始主導正文時，需要降級成作者檢查清單。文章完成後的檢查重點是讀者能否理解技術推導，並確認欄位已在文章背後支撐判讀。</p>
<h2 id="完稿檢查">完稿檢查</h2>
<p>完稿檢查的責任是確認技術文章維持主線。檢查時先看正文是否能獨立閱讀，再看欄位是否完整支撐交接。</p>
<ul>
<li>首段是否先說概念責任</li>
<li>判讀訊號是否來自真實服務情境</li>
<li>表格項目是否有延伸說明</li>
<li>交接路由是否指向具體章節</li>
<li>案例欄位是否能回寫到 04 / 06 / 08</li>
<li>正文是否保留技術推導，並把欄位轉成讀者可理解的判讀</li>
</ul>
]]></content:encoded></item><item><title>Blog Markdown 寫作規範與 mdtools 檢查</title><link>https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/</link><pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/</guid><description>&lt;h2 id="這篇要解決什麼">這篇要解決什麼&lt;/h2>
&lt;p>隨著 blog 文章與知識卡片成長，純靠寫作紀律維持排版一致性越來越不可靠。反覆踩到的問題橫跨兩個層級：&lt;/p>
&lt;p>&lt;strong>結構與安全層級&lt;/strong>（這是工具鏈存在的主要理由）：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>裸 URL 在段落與表格中爆版&lt;/strong>（MD034），降低閱讀體驗。&lt;/li>
&lt;li>&lt;strong>表格管線風格混用&lt;/strong>（MD060），同一張表格有的有空白、有的沒有。&lt;/li>
&lt;li>&lt;strong>平行模板章節重複標題&lt;/strong>（MD024），例如多案例文章的 &lt;code>### 弱點環節&lt;/code> 出現 13 次。&lt;/li>
&lt;li>&lt;strong>顯示文字與實際 href 不一致&lt;/strong>（反釣魚）— 不在標準 markdownlint 規則內，但紅隊教材脈絡下必要。&lt;/li>
&lt;li>&lt;strong>卡片雙向完整性&lt;/strong>（orphan 卡片、斷連結、K4 合規）— 跨文件檢查，現成工具做不到。&lt;/li>
&lt;li>&lt;strong>Front matter schema&lt;/strong> — Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位，缺失會破壞列表渲染、排序、SEO。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>基礎格式層級&lt;/strong>（容易被忽略但影響 parser 穩定性或語義結構）：&lt;/p>
&lt;ul>
&lt;li>正文禁止使用 H1（嚴於 MD025）— Hugo front matter &lt;code>title&lt;/code> 已產生 H1。&lt;/li>
&lt;li>標題前後需保留空行（MD022），parser 才能正確識別標題邊界。&lt;/li>
&lt;li>標題結尾禁止標點（MD026）— 例如 &lt;code>## 常見問題：&lt;/code> 應改為 &lt;code>## 常見問題&lt;/code>。&lt;/li>
&lt;li>禁止用 &lt;code>**bold**&lt;/code> 段落當標題（MD036）— 破壞語義階層與 TOC 產生。&lt;/li>
&lt;li>程式碼區塊需註明語言（MD040），影響 syntax highlighting 與 accessibility。&lt;/li>
&lt;li>列表前後需空行（MD032）、fenced code block 前後需空行（MD031）— 否則部分 parser 會把列表吃進段落。&lt;/li>
&lt;li>有序列表編號風格一致（MD029）— 全部 &lt;code>1.&lt;/code> 或全部 &lt;code>1./2./3.&lt;/code>。&lt;/li>
&lt;li>檔案結尾需有換行（MD047），POSIX 規範。&lt;/li>
&lt;li>行長度上限（MD013）— &lt;strong>預設關閉&lt;/strong>，中英混用技術寫作不適用 80-char 慣例。&lt;/li>
&lt;/ul>
&lt;p>前兩類混合在同一份寫作規範裡，因為都由同一個工具鏈檢查、都要落地到相同的 pre-commit hook。純靠紀律記住這十幾條在大型 repo 上不可行，純 regex 又無法穩定處理「平行結構下的標題重複」「卡片段落歸屬」這類語意判斷。因此 blog 專案採用 Go + goldmark AST 做自訂 linter：&lt;code>scripts/mdtools&lt;/code>。本文是 linter 與寫作規範的對齊文件；AGENTS.md 引用本文作為排版規範來源。&lt;/p>
&lt;hr>
&lt;h2 id="1-工具總覽">1. 工具總覽&lt;/h2>
&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;code>mdtools fmt [--fix|--check]&lt;/code>&lt;/td>
 &lt;td>格式正規化（URL、表格、空行、列表間距、trailing newline）&lt;/td>
 &lt;td>&lt;code>--fix&lt;/code> 會改&lt;/td>
 &lt;td>pre-commit（&lt;code>--fix&lt;/code>）、pre-push / CI（&lt;code>--check&lt;/code>）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>mdtools lint&lt;/code>&lt;/td>
 &lt;td>結構檢查（標題、反釣魚、code block 語言、front matter schema）&lt;/td>
 &lt;td>否&lt;/td>
 &lt;td>pre-commit、pre-push、CI&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>mdtools cards&lt;/code>&lt;/td>
 &lt;td>跨文件完整性（連結、orphan、K4）&lt;/td>
 &lt;td>否&lt;/td>
 &lt;td>pre-commit、pre-push、CI&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>工具原始碼在 &lt;code>scripts/mdtools/&lt;/code>，binary build 到 &lt;code>bin/mdtools&lt;/code>（已 gitignore）。&lt;/p>
&lt;p>作用範圍是 &lt;code>content/**/*.md&lt;/code>。&lt;code>public/&lt;/code>、&lt;code>themes/&lt;/code>、&lt;code>node_modules/&lt;/code> 等輸出或第三方資源不檢查。&lt;/p>
&lt;hr>
&lt;h2 id="2-標題規則">2. 標題規則&lt;/h2>
&lt;h3 id="21-標題結構與格式規則">2.1 標題結構與格式規則&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>正文禁止使用 H1&lt;/strong>。Hugo 的 front matter &lt;code>title&lt;/code> 會自動產生 H1，若正文再寫 &lt;code># ...&lt;/code> 會出現兩個 H1 並列，破壞語義階層與 SEO 訊號。正文一律從 H2 開始，最深到 H6。&lt;/li>
&lt;li>&lt;strong>同一父標題（直接上層）底下，子標題文字必須唯一&lt;/strong>（MD024 siblings_only 模式）。&lt;/li>
&lt;li>不同父標題底下，子標題允許重名。&lt;/li>
&lt;li>標題前後需保留空行（MD022），&lt;code>mdtools fmt --fix&lt;/code> 自動補。&lt;/li>
&lt;li>&lt;strong>標題結尾禁止標點&lt;/strong>（MD026）— 禁用字元：&lt;code>.&lt;/code>、&lt;code>,&lt;/code>、&lt;code>:&lt;/code>、&lt;code>;&lt;/code>、&lt;code>。&lt;/code>、&lt;code>，&lt;/code>、&lt;code>：&lt;/code>、&lt;code>；&lt;/code>。允許 &lt;code>?&lt;/code>、&lt;code>！&lt;/code>、&lt;code>？&lt;/code>、&lt;code>!&lt;/code> 作為語氣結尾。&lt;code>mdtools fmt --fix&lt;/code> 自動去除結尾禁用標點。&lt;/li>
&lt;li>&lt;strong>禁止用粗體當標題&lt;/strong>（MD036）— 若段落整段只由 &lt;code>**文字**&lt;/code> 或 &lt;code>*文字*&lt;/code> 組成，視為視覺性標題濫用。&lt;code>mdtools lint&lt;/code> 只報警、不自動修；作者需手動判斷正確的標題層級（通常是 H3 / H4）並改寫。&lt;/li>
&lt;/ul>
&lt;h3 id="22-補充範例md026-與-md036-的典型誤用">2.2 補充範例：MD026 與 MD036 的典型誤用&lt;/h3>
&lt;p>MD026（標題尾標點）常見誤用：&lt;/p></description><content:encoded><![CDATA[<h2 id="這篇要解決什麼">這篇要解決什麼</h2>
<p>隨著 blog 文章與知識卡片成長，純靠寫作紀律維持排版一致性越來越不可靠。反覆踩到的問題橫跨兩個層級：</p>
<p><strong>結構與安全層級</strong>（這是工具鏈存在的主要理由）：</p>
<ul>
<li><strong>裸 URL 在段落與表格中爆版</strong>（MD034），降低閱讀體驗。</li>
<li><strong>表格管線風格混用</strong>（MD060），同一張表格有的有空白、有的沒有。</li>
<li><strong>平行模板章節重複標題</strong>（MD024），例如多案例文章的 <code>### 弱點環節</code> 出現 13 次。</li>
<li><strong>顯示文字與實際 href 不一致</strong>（反釣魚）— 不在標準 markdownlint 規則內，但紅隊教材脈絡下必要。</li>
<li><strong>卡片雙向完整性</strong>（orphan 卡片、斷連結、K4 合規）— 跨文件檢查，現成工具做不到。</li>
<li><strong>Front matter schema</strong> — Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位，缺失會破壞列表渲染、排序、SEO。</li>
</ul>
<p><strong>基礎格式層級</strong>（容易被忽略但影響 parser 穩定性或語義結構）：</p>
<ul>
<li>正文禁止使用 H1（嚴於 MD025）— Hugo front matter <code>title</code> 已產生 H1。</li>
<li>標題前後需保留空行（MD022），parser 才能正確識別標題邊界。</li>
<li>標題結尾禁止標點（MD026）— 例如 <code>## 常見問題：</code> 應改為 <code>## 常見問題</code>。</li>
<li>禁止用 <code>**bold**</code> 段落當標題（MD036）— 破壞語義階層與 TOC 產生。</li>
<li>程式碼區塊需註明語言（MD040），影響 syntax highlighting 與 accessibility。</li>
<li>列表前後需空行（MD032）、fenced code block 前後需空行（MD031）— 否則部分 parser 會把列表吃進段落。</li>
<li>有序列表編號風格一致（MD029）— 全部 <code>1.</code> 或全部 <code>1./2./3.</code>。</li>
<li>檔案結尾需有換行（MD047），POSIX 規範。</li>
<li>行長度上限（MD013）— <strong>預設關閉</strong>，中英混用技術寫作不適用 80-char 慣例。</li>
</ul>
<p>前兩類混合在同一份寫作規範裡，因為都由同一個工具鏈檢查、都要落地到相同的 pre-commit hook。純靠紀律記住這十幾條在大型 repo 上不可行，純 regex 又無法穩定處理「平行結構下的標題重複」「卡片段落歸屬」這類語意判斷。因此 blog 專案採用 Go + goldmark AST 做自訂 linter：<code>scripts/mdtools</code>。本文是 linter 與寫作規範的對齊文件；AGENTS.md 引用本文作為排版規範來源。</p>
<hr>
<h2 id="1-工具總覽">1. 工具總覽</h2>
<table>
  <thead>
      <tr>
          <th>子命令</th>
          <th>職責</th>
          <th>改檔</th>
          <th>觸發時機</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>mdtools fmt [--fix|--check]</code></td>
          <td>格式正規化（URL、表格、空行、列表間距、trailing newline）</td>
          <td><code>--fix</code> 會改</td>
          <td>pre-commit（<code>--fix</code>）、pre-push / CI（<code>--check</code>）</td>
      </tr>
      <tr>
          <td><code>mdtools lint</code></td>
          <td>結構檢查（標題、反釣魚、code block 語言、front matter schema）</td>
          <td>否</td>
          <td>pre-commit、pre-push、CI</td>
      </tr>
      <tr>
          <td><code>mdtools cards</code></td>
          <td>跨文件完整性（連結、orphan、K4）</td>
          <td>否</td>
          <td>pre-commit、pre-push、CI</td>
      </tr>
  </tbody>
</table>
<p>工具原始碼在 <code>scripts/mdtools/</code>，binary build 到 <code>bin/mdtools</code>（已 gitignore）。</p>
<p>作用範圍是 <code>content/**/*.md</code>。<code>public/</code>、<code>themes/</code>、<code>node_modules/</code> 等輸出或第三方資源不檢查。</p>
<hr>
<h2 id="2-標題規則">2. 標題規則</h2>
<h3 id="21-標題結構與格式規則">2.1 標題結構與格式規則</h3>
<ul>
<li><strong>正文禁止使用 H1</strong>。Hugo 的 front matter <code>title</code> 會自動產生 H1，若正文再寫 <code># ...</code> 會出現兩個 H1 並列，破壞語義階層與 SEO 訊號。正文一律從 H2 開始，最深到 H6。</li>
<li><strong>同一父標題（直接上層）底下，子標題文字必須唯一</strong>（MD024 siblings_only 模式）。</li>
<li>不同父標題底下，子標題允許重名。</li>
<li>標題前後需保留空行（MD022），<code>mdtools fmt --fix</code> 自動補。</li>
<li><strong>標題結尾禁止標點</strong>（MD026）— 禁用字元：<code>.</code>、<code>,</code>、<code>:</code>、<code>;</code>、<code>。</code>、<code>，</code>、<code>：</code>、<code>；</code>。允許 <code>?</code>、<code>！</code>、<code>？</code>、<code>!</code> 作為語氣結尾。<code>mdtools fmt --fix</code> 自動去除結尾禁用標點。</li>
<li><strong>禁止用粗體當標題</strong>（MD036）— 若段落整段只由 <code>**文字**</code> 或 <code>*文字*</code> 組成，視為視覺性標題濫用。<code>mdtools lint</code> 只報警、不自動修；作者需手動判斷正確的標題層級（通常是 H3 / H4）並改寫。</li>
</ul>
<h3 id="22-補充範例md026-與-md036-的典型誤用">2.2 補充範例：MD026 與 MD036 的典型誤用</h3>
<p>MD026（標題尾標點）常見誤用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">#### 字型選擇說明：        ← 違規（結尾 `：`）
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>#### 字型選擇說明          ← 合法</span></span></code></pre></div><p>中文寫作習慣用冒號引入後續內容，這個模式在「段首句」合理、在「標題」就不合理 — 標題本身的存在就暗示了後續有內容，冒號變成冗餘訊號。</p>
<p>MD036（粗體當標題）常見誤用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">**字型選擇說明**           ← 違規：整段只有粗體，視覺像標題但不是真標題
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl">這段內容...
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl">### 字型選擇說明           ← 合法：用正式的 H3 取代</span></span></code></pre></div><p>差異看起來微小，實際影響包含：Hugo TOC 不會抓到、卡片反向連結失效、screen reader 無法跳轉。這是「語義 vs 視覺」錯位的典型案例，AST linter 容易檢出（Paragraph 節點唯一子節點為 Strong/Emph）。</p>
<h3 id="23-為什麼採-siblings_only-而非全域唯一">2.3 為什麼採 siblings_only 而非全域唯一</h3>
<p>平行結構（多案例、多模板章節）的複用語義來自上層標題賦予的脈絡。例如：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 【案例一】Uber 2022
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu">### 弱點環節        ← 合法
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### 攻擊路徑
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="gu">## 【案例二】Okta 2023
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gu">### 弱點環節        ← 合法，因為在不同的父層下
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gu"></span>### 攻擊路徑</span></span></code></pre></div><p>重名只有在同層並列時才代表結構錯誤。強制全域唯一會逼作者寫 <code>### 【案例二】弱點環節</code>，破壞平行結構的視覺一致性，收益並不大。</p>
<hr>
<h2 id="3-url-與連結規則">3. URL 與連結規則</h2>
<h3 id="31-裸-url-轉換mdtools-fmt---fix-自動處理">3.1 裸 URL 轉換（<code>mdtools fmt --fix</code> 自動處理）</h3>
<p>段落或表格儲存格內的裸 URL 會自動包成 markdown 連結。顯示文字依路徑可識別性分級：</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>顯示文字</th>
          <th>範例（before → after）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>路徑含識別碼（例如 CVE）</td>
          <td><code>domain.com/識別碼</code></td>
          <td><code>https://nvd.nist.gov/vuln/detail/CVE-2023-34362</code> → <code>[nvd.nist.gov/CVE-2023-34362](https://nvd.nist.gov/vuln/detail/CVE-2023-34362)</code></td>
      </tr>
      <tr>
          <td>路徑冗長但無識別性</td>
          <td><code>domain.com</code></td>
          <td><code>https://www.cisa.gov/news-events/alerts/2024/06/03/snowflake-recommends-...</code> → <code>[cisa.gov](https://www.cisa.gov/news-events/alerts/2024/06/03/snowflake-recommends-...)</code></td>
      </tr>
      <tr>
          <td>已是 markdown 連結</td>
          <td>不動</td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<p>識別碼偵測用 regex 白名單，初始清單專注在高頻識別碼格式（例如 <code>CVE-YYYY-N</code>），其他格式以「遇到再加」原則擴充。清單維護在 <code>scripts/mdtools/internal/rules/identifiers.go</code>。</p>
<h3 id="32-反釣魚校驗mdtools-lint-強制檢查">3.2 反釣魚校驗（<code>mdtools lint</code> 強制檢查）</h3>
<p>Markdown 語法允許顯示文字與實際 href 完全不符，這是釣魚攻擊的結構基礎。本規則在 AST 層阻擋此模式。</p>
<ul>
<li><strong>R-URL-1（URL 樣顯示文字一致性）</strong>：若顯示文字含 <code>.com</code> / <code>.org</code> / <code>.gov</code> / <code>.net</code> / <code>.io</code> / <code>.dev</code> / <code>.tw</code> 等 TLD 字樣，則顯示文字的 domain 必須等於 href 的 domain（含子網域比對）。</li>
<li><strong>R-URL-2（描述型顯示文字自由）</strong>：顯示文字不含 TLD 字樣時，視為人類可讀描述，不做 domain 比對。</li>
</ul>
<p>違規範例（會被 lint 阻擋）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">[<span class="nt">nvd.nist.gov</span>](<span class="na">https://malicious.example.com/fake</span>)     ← 顯示文字暗示 NVD，href 卻不是
</span></span><span class="line"><span class="ln">2</span><span class="cl">[<span class="nt">cisa.gov/advisory</span>](<span class="na">https://cisa-gov.evil.example</span>)     ← 顯示文字抄 CISA 格式，domain 不符</span></span></code></pre></div><p>合法範例：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">[<span class="nt">Uber 事件公告</span>](<span class="na">https://www.uber.com/newsroom/security-update/</span>)
</span></span><span class="line"><span class="ln">2</span><span class="cl">[<span class="nt">nvd.nist.gov/CVE-2023-34362</span>](<span class="na">https://nvd.nist.gov/vuln/detail/CVE-2023-34362</span>)</span></span></code></pre></div><p>這條規則在紅隊 / 安全相關教材中特別重要：讀者本來就該對來源警戒，排版規則不該削弱這個警戒訊號。縮短顯示文字提升可讀性，反釣魚校驗守住安全底線，兩者互補。</p>
<h3 id="33-例外情境">3.3 例外情境</h3>
<ul>
<li><strong>程式碼區塊</strong>（fenced code block，<code>```</code> 包圍）內的 URL <strong>不做任何處理</strong>（不縮短、不校驗）。代碼範例經常需要展示完整 URL 給讀者複製執行。</li>
<li><strong>引用區塊</strong>（<code>&gt;</code> 開頭）內的 URL <strong>比照段落處理</strong>，會縮短也會做反釣魚校驗。</li>
</ul>
<hr>
<h2 id="4-表格規則">4. 表格規則</h2>
<ul>
<li>統一使用 <strong>aligned 風格</strong>：每欄內容用空白補齊到該欄的最大寬度，使 <code>|</code> 在 monospace 渲染下垂直對齊。</li>
<li>欄位分隔線使用 <code>| --- |</code> 形式，不含對齊冒號 <code>:</code>（分隔線內的 <code>-</code> 數量跟隨該欄寬度自動填足）。</li>
<li>寬度計算使用顯示寬度（display width）— CJK 字元佔 2 欄寬、ASCII 佔 1 欄寬，分隔列與資料列按同一套寬度對齊。</li>
<li><code>mdtools fmt --fix</code> 自動正規化：插入新行或改動欄寬時會全表重算，作者不需手工維持對齊。</li>
</ul>
<p>選 aligned 而非 compact 的理由是<strong>原始檔可讀性</strong>：技術教材的表格常需在 code review 裡對照，aligned 風格讓 reviewer 直接看出哪些欄位對應哪些內容，不用在腦中解析鋸齒狀的 pipes。手工對齊在長表格反覆編輯時確實會失效（新增一行就全表要重對齊），但這正是 <code>mdtools fmt --fix</code> 接手的地方。</p>
<hr>
<h2 id="5-基礎格式細節">5. 基礎格式細節</h2>
<p>這節整理容易被忽略、但會影響 parser 正確性或渲染品質的小規則。</p>
<h3 id="51-程式碼區塊必須註明語言md040">5.1 程式碼區塊必須註明語言（MD040）</h3>
<p>由 <code>mdtools lint</code> 檢查。未註明語言的 fenced code block 會被報警：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">``<span class="sb">`                   ← 違規：缺語言標示
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="sb">func main() {
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="sb">    fmt.Println(&#34;hi&#34;)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="sb">}
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sb">`</span>`<span class="sb">`
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sb">`</span>`<span class="sb">`go                 ← 合法
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="sb">func main() {
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sb">    fmt.Println(&#34;hi&#34;)
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="sb">}
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div><p>純文字輸出（例如 terminal output、log 片段）使用 <code>text</code> 或 <code>plain</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="s">```text
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s"></span>Error: permission denied
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s">```</span></span></span></code></pre></div><p>Shell 範例統一用 <code>bash</code>（即使是 zsh 語法，讓 syntax highlighter 有合理預設）；純設定檔依實際格式（<code>toml</code>、<code>yaml</code>、<code>json</code>、<code>ini</code>）。</p>
<h3 id="52-fenced-code-block-前後需空行md031">5.2 fenced code block 前後需空行（MD031）</h3>
<p>由 <code>mdtools fmt --fix</code> 自動處理。缺空行會讓前後段落被 parser 併入 code block 或反之。</p>
<h3 id="53-列表前後需空行md032">5.3 列表前後需空行（MD032）</h3>
<p>由 <code>mdtools fmt --fix</code> 自動處理。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">上一段結束。
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">-</span> 列表項一           ← 違規：列表前無空行，會被部分 parser 當段落延續
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> 列表項二
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">上一段結束。
</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 class="k">-</span> 列表項一           ← 合法
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 列表項二
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">下一段開始。</span></span></code></pre></div><h3 id="54-有序列表編號一致性md029">5.4 有序列表編號一致性（MD029）</h3>
<p>由 <code>mdtools fmt --fix</code> 正規化。本專案採 <code>ordered</code> 風格（全部遞增編號）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">1.</span> 第一步
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">2.</span> 第二步           ← 合法
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">3.</span> 第三步
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">1.</span> 第一步
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">1.</span> 第二步           ← 違規：混用風格（fmt --fix 會改成 1./2./3.）
</span></span><span class="line"><span class="ln">7</span><span class="cl">2. 第三步</span></span></code></pre></div><p>選擇 <code>ordered</code> 的理由：原始檔可讀性高，作者直接看到步驟數；插入新項目的對齊代價比全部重新渲染低。</p>
<h3 id="55-段落間空行">5.5 段落間空行</h3>
<p>段落之間、標題前後、列表與段落之間都需空行。<code>mdtools fmt --fix</code> 會自動規範化多餘 / 缺失的空行，作者不需手工維護。</p>
<h3 id="56-檔案結尾需有換行md047">5.6 檔案結尾需有換行（MD047）</h3>
<p>POSIX 文字檔規範；缺失時 git diff 會出現 <code>\ No newline at end of file</code>。<code>mdtools fmt --fix</code> 自動補。</p>
<h3 id="57-tab-字元md010-僅限-fenced-code-block">5.7 Tab 字元（MD010）— 僅限 fenced code block</h3>
<p>由 <code>mdtools lint</code> 檢查（warn 等級）。Prose / 列表 / 表格 / 引用等非 code-block 行內若出現 tab 字元，會被標記並建議改成空白；fenced code block 內的 tab 保留（Go 原始碼依 gofmt 慣例用 tab，文章要讓讀者能直接複製貼用）。</p>
<p>Repo 根目錄的 <code>.markdownlint.json</code> 用 <code>&quot;MD010&quot;: { &quot;code_blocks&quot;: false }</code> 告知 IDE 的 markdownlint extension 採用同一套 policy，讓編輯器跟 CI 的警告保持一致。</p>
<h3 id="58-行長度上限md013-預設關閉">5.8 行長度上限（MD013）— 預設關閉</h3>
<p>本規則<strong>預設關閉</strong>。中英混用的技術寫作不適用 80-char 慣例：</p>
<ul>
<li>中文每字元算 1 個寬度時，80-char ≈ 40 個中文字，寫到一半就要斷行，嚴重影響可讀性。</li>
<li>中文每字元算 2 個寬度時，80-char 相當於 20-30 個中文字，更離譜。</li>
<li>Markdown 編輯器普遍支援軟斷行與 IDE word wrap，實體行長度對閱讀體驗影響小。</li>
</ul>
<p>若未來需要打開（例如發現真的有人寫出 2000-char 單行段落），建議上限 <strong>400 字元</strong>（軟上限，warn 不阻擋）。設定在 <code>scripts/mdtools/internal/rules/config.go</code> 的 <code>LineLengthLimit</code> 欄位。</p>
<h3 id="59-裝飾符號禁用emoji--視覺記號">5.9 裝飾符號禁用（emoji / 視覺記號）</h3>
<blockquote>
<p><strong>本節本身豁免</strong>：規範要描述「哪些符號禁用」必然要列舉這些符號（use-mention distinction）。本節舉例的 emoji 屬 mention（指稱）、非 use（裝飾使用）、不違反規則。掃描指令會 hit 到本節、判讀時跳過。</p></blockquote>
<p><code>content/**</code> 正文不可使用 emoji（如 ✅ ❌ ⚠️ 🚨 🟡 🟢 ⭐ 📌 💡 ⚡ 🎯）與裝飾性 unicode 符號（✓ ✗ ✘）。<strong>表格、列表、行內標記都不行</strong>。</p>
<p><strong>替換策略</strong>（emoji 承載的語意要回到文字結構、不是純粹刪除符號）：</p>
<table>
  <thead>
      <tr>
          <th>原寫法</th>
          <th>改成</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>表格 status <code>| ✅ 解了 |</code></td>
          <td>純文字描述：「解了」/「是」/「適用」</td>
      </tr>
      <tr>
          <td>表格 status <code>| ❌ 漏 |</code></td>
          <td>純文字描述：「漏」/「否」/「不適用」</td>
      </tr>
      <tr>
          <td>列表優缺點 <code>- ✅ 簡單</code> / <code>- ❌ 慢</code></td>
          <td>拆成 <code>**優點**：簡單</code> / <code>**缺點**：慢</code> 段落或標題段</td>
      </tr>
      <tr>
          <td>列表錯誤示範 <code>- ❌ 把 key 寄 email</code> / <code>- ✅ 用 CSR</code></td>
          <td>拆成 <code>**錯誤做法**：</code> / <code>**正確做法**：</code> 標題段</td>
      </tr>
      <tr>
          <td>行內視覺強調 <code>🚨 critical</code></td>
          <td>markdown 粗體 <code>**critical**</code> 或引用塊 <code>&gt; **critical**：...</code></td>
      </tr>
  </tbody>
</table>
<p><strong>理由</strong>：</p>
<ul>
<li><strong>Grep-ability</strong>：emoji 無法用 plain text grep 命中；視覺結構容易掩蓋語意結構、reviewer 看不出「優 / 缺」是用 emoji 區分還是用標題段區分</li>
<li><strong>CLI parser 相容性</strong>：部分 multi-byte emoji 在 Rust-based CLI 工具（如某些 mdtools / pagefind / lint pipeline）觸發 char-boundary panic</li>
<li><strong>跨語境穩定</strong>：emoji 在不同字型 / 平台 / 終端機渲染差異大、容易斷行或顯示為框</li>
</ul>
<p><strong>掃描指令</strong>（提交前自己跑一次、有 hit 就替換）：</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">rg <span class="s2">&#34;✅|❌|⚠️|🚨|🟡|🟢|🔴|🟠|🔵|⭐|📌|💡|⚡|🎯|✨|📝|🔍|🛠|⛔|✓|✗|✘&#34;</span> content/</span></span></code></pre></div><blockquote>
<p>本規則目前<strong>未進 <code>mdtools lint</code> 自動掃描</strong>、靠人工 grep。未來會加進 lint pipeline。</p></blockquote>
<hr>
<h3 id="510-位置引用與數量命名候選掃描ref1--ref2警告層">5.10 位置引用與數量命名候選掃描（REF1 / REF2、警告層）</h3>
<p><code>mdtools lint</code> 對 <code>content/**</code> 跑兩個警告層掃描、來源是引用紀律卡（<a href="/blog/report/reference-by-semantic-title-not-number/" data-link-title="引用章節用語意標題、不用位置編號：編號是結構排列的 derivation、會隨版本漂移" data-link-desc="跨段落、跨檔引用結構單位（章節 / 階段 / 條列項）時、引用語意標題（副標題）、不引用位置編號（Stage 3、第 5 章、第 3 點）。編號是「目前結構排列」的 derivation、不是 fact；結構重排時編號全部位移、引用點不會報錯、而是 silent 指向錯的內容 — 比 broken link 更難偵測。標題的存在意義就是承載可被引用的語意。是 #44 SSoT 在結構引用維度的實例、#93 identifier-as-fact 家族的 sibling、#84 命名承載語意的引用面延伸。">#155 引用章節用語意標題</a>、<a href="/blog/report/name-collections-by-role-not-count/" data-link-title="集合命名用角色、不內嵌數量：「核心七問」的七是成員數的 derivation、加一問就全面失真" data-link-desc="「核心七問」「成長六階段」「四大支柱」這類名稱把成員數量烤進名字裡 — 數量是集合當前成員的 derivation、不是集合的語意身分；成員增減時名稱失真、且名稱是被複製最多次的字串、缺陷隨每次引用繁殖。修法：命名只承載角色與層級（核心問題 / 次要問題 / 撞牆階段）、數量讓清單自己呈現。本卡是 #155 的命名端 sibling（#155 修引用端、本卡讓「語意標題是穩定錨」的前提真正成立）、#44 SSoT 在名稱內容的實例、#84 命名檢驗的數量維度。">#156 集合命名用角色</a>）：</p>
<ul>
<li><strong>REF1-positional-anchor</strong>：正文中的位置式引用候選 —「見第 3 點」「詳見第五章」「§4」。位置編號是當下排列的衍生值、目標是活文件時、結構重排會讓引用 silent 指向錯的內容。</li>
<li><strong>REF2-count-in-name</strong>：標題與 front matter <code>title</code> 中內嵌成員數的集合命名 —「六大原則」「遷移五階段流程」。成員增減時名稱先失真、且名稱是被複製最多次的字串。</li>
</ul>
<p>兩個規則都停在警告層、<strong>命中是候選、不是判決</strong> — 回報前要做語意判定：</p>
<table>
  <thead>
      <tr>
          <th>命中情境</th>
          <th>判定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>引用發布方凍結的編號（RFC 段號、法條條號）</td>
          <td>合規、編號是 fact</td>
      </tr>
      <tr>
          <td>數字緊鄰它描述的清單（「確認三件事：」）</td>
          <td>合規、漂移在編輯當下可見</td>
      </tr>
      <tr>
          <td>外部凍結品牌名（SOLID 五原則）</td>
          <td>合規、數量由發布方凍結</td>
      </tr>
      <tr>
          <td>目標是內部活文件 / 內部活集合</td>
          <td>改語意標題引用 / 角色命名</td>
      </tr>
  </tbody>
</table>
<p>掃描器內建兩個自動豁免：被 <code>「」</code> 包住的命中視為反例引用、直接跳過；「第」開頭的序數（第三階段）不屬 REF2 的集合命名。既有內容的命中屬歷史基線、依「changed-set scoped lint」原則處理 — 新增與修改的檔案要判讀、存量警告分開回報。</p>
<h3 id="511-否定起手候選掃描pos-negation-lead警告層">5.11 否定起手候選掃描（POS-negation-lead、警告層）</h3>
<p><code>mdtools lint</code> 對 <code>content/**</code> 跑否定起手掃描、來源是 <a href="/blog/report/lead-with-the-point-cross-language/" data-link-title="重點優先陳述是跨語言的資訊結構原則、不是中文句型問題" data-link-desc="正向陳述優先的本質是資訊結構效率：讀者拿到核心概念的認知步驟越少越好。「不是 X、而是 Y」表達能力差、是因為它讓讀者先處理一個被否定的錯誤理解 X 才拿到正確的 Y、重點後置多繞一步。這個缺陷跨語言成立——英文 not X but Y、日文 X ではなく Y 同樣高頻、換語言不打破（證偽過的反例假設）。判別線是「核心概念在不在最前」、統一了 #94（重點先行合法）與 #149（重點後置違規）、且可操作。LLM 系統性放水的根因是高頻偏置（把語料高頻句型評為表達好、高頻不等於資訊結構優、跨語言）。主解是強制執行重點位置判準、#165 的異源視角降為補充。">#166 重點優先陳述是跨語言的資訊結構原則</a>（搭配 <a href="/blog/report/register-violation-needs-cross-style-eyes/" data-link-title="register 違規：偵測可機械化、判定要靠文體異源的眼睛" data-link-desc="寫作規範的違規分兩類：形式違規（emoji / 編號 / broken link）可完全機械判定、該進工具鏈；register / 品味違規（概念前置 / 否定起手 / 喊話 / 誇飾）的判定有不可消除的品味核心。「不是 X、而是 Y」的陷阱是偵測可機械化（grep 抓得到句型）偽裝成判定可機械化、誘導無限投入更精緻的判定方法（grep → 概念位置 → 行為測試）、但判定始終在品味側、始終放水。更深一層：產出這類違規的 LLM 跟審查它的 LLM 共享文體直覺、同源自審對 register 違規有結構上限、加再多輪次都跨不過。結構解是引入文體異源的視角（人類冷讀 / reader-simulation / 對抗文體 reviewer）、並接受 100% 自動 catch 不可能。">#165 register 違規要文體異源</a>）：</p>
<ul>
<li><strong>POS-negation-lead</strong>：正文中「不是 X、而是 Y」「不是 X — 是 Y」「與其 X、不如 Y」的重點後置候選。核心概念（Y）被擠到「而是 / 不如」之後、讀者要先處理一個被否定的 X 才拿到重點。這是資訊結構效率問題、跨語言成立（英文「not X but Y」、日文「X ではなく Y」）、不是中文特有句型 — 偵測可機械化、判定不可。pattern 涵蓋的連接詞（而是 / 「— 是」/ 不如）枚舉不完、判準是「核心概念在不在句首」而非哪個連接詞 — 漏掉的變體只是讓候選 silent 到有人讀到（規則第一版就漏了「不是 X — 是 Y」、靠人發現才補）。</li>
</ul>
<p>判定用「重點位置」、<strong>命中是候選、不是判決</strong>：</p>
<table>
  <thead>
      <tr>
          <th>命中情境</th>
          <th>判定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>核心概念第一次正面出現在句首（「有深度、不是非黑即白的二元」）</td>
          <td>合規、重點先行</td>
      </tr>
      <tr>
          <td>明示反例 / 對照段落內的否定（見 <a href="/blog/report/positive-rewrite-preserves-contrast/" data-link-title="正向改寫要保留對照論據、不能空降結論" data-link-desc="把「X、不是 Y」改成正向陳述時、若直接刪除「不是 Y」會讓結論失去推理依據、變成空降斷言。對照論據（Y 是讀者直覺會想到的替代方案）跟結論（X）是同一個推理單元、抽掉一半就不成立。正向改寫的合法做法是補解釋、改成對照表、或保留 Y 當對比錨點 — 直接拿掉是 worst。">#94</a>）</td>
          <td>合規、否定是對照本體</td>
      </tr>
      <tr>
          <td>核心概念被擠到「而是」之後（「不是二元、而是有深度」）</td>
          <td>改正向、把核心概念移到句首</td>
      </tr>
  </tbody>
</table>
<p>掃描器豁免兩類命中：被 <code>「」</code> 包住的引用（反例引用 / 句型佔位符）、以及 backtick 行內程式碼內的 pattern（grep regex / 技術識別碼）。講這個句型的 meta 卡（#165 / #166）與本規範段落會大量自我觸發 — 屬候選人判定豁免、跟 REF1 / REF2 對 #155 / #156 一致。既有內容的命中（全 <code>content/</code> 約數百個）屬歷史基線、依「changed-set scoped lint」原則處理 — 偵測把候選池曝光、判定漸進進行、存量警告分開回報。</p>
<h2 id="6-front-matter-schemamdtools-lint">6. Front matter schema（<code>mdtools lint</code>）</h2>
<p>Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位給 render pipeline。缺欄位會讓列表頁、排序、SEO 壞掉，但 Hugo 本身不會失敗（靜默接受不完整資料），所以必須由 linter 守住。</p>
<h3 id="61-通用層contentmd">6.1 通用層（<code>content/**/*.md</code>）</h3>
<p>所有內容文章必須有：</p>
<ul>
<li><code>title</code>：字串，不可空。</li>
<li><code>date</code>：<code>YYYY-MM-DD</code> 格式（ISO 8601 date）。</li>
</ul>
<p><strong>Hugo <code>_index.md</code> section 頁面例外</strong>：這類檔案是 Hugo 的 section 列表 landing page，不是內容文章，沒有語意上的「日期」。只要求 <code>title</code>，不強制 <code>date</code>。</p>
<h3 id="62-推薦層警告不阻擋">6.2 推薦層（警告，不阻擋）</h3>
<p>推薦填寫（<code>mdtools lint</code> warn level）：</p>
<ul>
<li><code>description</code>：字串，建議 30–150 字，影響 SEO 與列表頁預覽。</li>
<li><code>tags</code>：陣列，至少 1 個標籤。</li>
</ul>
<p>推薦層是歷史內容的緩衝區，不是新增內容的放行條件。新增文章必須同時填寫 <code>description</code> 與 <code>tags</code>；修改既有文章時，若同一檔案缺少推薦欄位，應在同次變更補齊，避免每次驗證都被舊 warning 淹沒。</p>
<p>驗證時先跑 changed-set scoped lint 判斷本次變更品質，再視需要跑 full lint 觀察整體基線。回報 full lint 結果時，要把歷史 warning、已知 warning 與本次新增問題分開描述。</p>
<h3 id="63-卡片嚴格層contentbackendknowledge-cards">6.3 卡片嚴格層（<code>content/backend/knowledge-cards/**</code>）</h3>
<p>知識卡片額外要求（對應 <code>.codex/briefs/knowledge-cards.md</code> K2）：</p>
<ul>
<li><code>title</code>、<code>date</code>、<code>description</code> 必填。</li>
<li><code>weight</code>：整數，決定在 <code>_index.md</code> 主題表格中的排序位置。</li>
</ul>
<h3 id="64-禁止欄位">6.4 禁止欄位</h3>
<p>以下欄位存在時 <code>mdtools lint</code> 警告（避免語義混淆）：</p>
<ul>
<li><code>author</code>：本專案為單作者 blog，統一於 Hugo 設定。</li>
<li><code>permalink</code>：使用 Hugo 預設路徑規則，避免手動覆蓋。</li>
</ul>
<p>若未來需要鬆綁，在 <code>scripts/mdtools/internal/rules/frontmatter.go</code> 的 <code>DisallowedFields</code> 清單調整。</p>
<h3 id="65-slug-必填跟檔名對齊">6.5 slug 必填、跟檔名對齊</h3>
<p>所有 content 文章 frontmatter 必須有 <code>slug</code> 欄位，值跟檔名（不含 <code>.md</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;視覺手段對齊錯誤層次&#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;visual-tool-error-layer-alignment&#34;</span><span class="w">   </span><span class="c"># 跟檔名對齊</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">2026-04-28</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><strong>為什麼必填</strong>：</p>
<p>slug 是 URL 的核心識別、跨多個工具共用（Hugo build、mdtools lint、跨檔 markdown link、search index）。若不顯式定義，slug 散落在三處推導鏈：</p>
<table>
  <thead>
      <tr>
          <th>來源</th>
          <th>推導值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hugo 預設（從 title 用 urlize）</td>
          <td>runtime 推導、隨 hugo 版本變化</td>
      </tr>
      <tr>
          <td>mdtools 字面比對</td>
          <td>檔名 stem</td>
      </tr>
      <tr>
          <td>跨檔連結時的引用</td>
          <td>寫作者手動算 / 複製</td>
      </tr>
  </tbody>
</table>
<p>三個推導鏈不一致時 = silent broken link（mdtools pass 但 hugo build 後 404、或反過來）。把 slug 升成 frontmatter 顯式 fact、所有工具基於同一 source、消除 derivation 鏈。</p>
<p>詳細論述見 <a href="/blog/report/url-slug-must-be-explicit-fact/" data-link-title="URL slug 必須顯式定義為 fact：跨工具 identifier 用單一定義源" data-link-desc="URL slug 在 Hugo 預設下從 title 自動推導、在 mdtools lint 下從檔名讀、在跨檔連結時又要寫第三個值 — 一個 identifier 散落在三個推導鏈、典型 SSoT 違反。當多個工具共用一個 identifier、推導不一致 = silent broken link。修法：把 slug 從 derivation（runtime 推導）升級成 fact（frontmatter 顯式定義）、檔名 / 連結都基於這個 fact。本卡是 #44 在 toolchain integration 情境的具體實例、是 #82 字面 vs 行為在 identifier 維度的展現。">report #93 URL slug 必須顯式定義為 fact</a>。</p>
<p><strong>檔名對齊規則</strong>：</p>
<ul>
<li>檔名命名建議：英文小寫、kebab-case 或 snake_case、不含中文（避免 hugo <code>urlize</code> 規則跨版本變動）</li>
<li>slug 值 == 檔名 stem（不含 <code>.md</code>）</li>
<li>修檔名時必須同步修 slug；修 slug 時必須同步 rename 檔案</li>
</ul>
<p><strong>Hugo <code>_index.md</code> 例外</strong>：section 列表頁已有 <code>slug:</code> 欄位指定資料夾路徑、不適用本規則。</p>
<hr>
<h2 id="7-卡片雙向完整性mdtools-cards">7. 卡片雙向完整性（<code>mdtools cards</code>）</h2>
<p>作用範圍：<code>content/**/*.md</code>，重點關注 <code>content/backend/knowledge-cards/</code>。</p>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>規則</th>
          <th>實作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>L1 連結有效性</strong></td>
          <td>所有相對連結 <code>[...](/posts/markdown-writing-spec/path)</code> / <code>[...](/posts/path)</code> 的目標檔案必須存在</td>
          <td>AST 抽 Link node → 解析相對路徑 → stat 檔案</td>
      </tr>
      <tr>
          <td><strong>L2 卡片 orphan 偵測</strong></td>
          <td>每張卡片至少被 <code>content/**</code> 中一篇非卡片正文引用</td>
          <td>建反向索引 → 找無 incoming edge 的卡片</td>
      </tr>
      <tr>
          <td><strong>L4 卡片 K4 結構合規</strong></td>
          <td>卡片首段與「概念位置」段各至少 1 個相鄰卡片連結</td>
          <td>AST 定位段落節點 → 統計子樹 Link 數</td>
      </tr>
  </tbody>
</table>
<p>L3（正文首次出現術語必須連結到卡片）暫不納入，待術語字典（<code>.codex/briefs/knowledge-web-expansion.md</code>）啟動後再開。</p>
<h3 id="為什麼要做跨文件檢查">為什麼要做跨文件檢查</h3>
<p>知識卡片是 blog 的核心知識資產。隨著卡片數量增加：</p>
<ul>
<li><strong>Orphan 卡片</strong>（沒有正文連結進來）會變成知識死角，讀者無法發現。</li>
<li><strong>斷掉的相對連結</strong>（檔案被改名或移動）肉眼難以發現，只有讀者點擊失敗才暴露。</li>
<li><strong>K4 合規</strong>（首段 + 概念位置段要有鄰卡連結）保證卡片間的知識網不會鬆散。</li>
</ul>
<p>這些檢查用 regex 做都卡在「段落歸屬怎麼判斷」。AST 天生知道節點的父子結構，做起來自然。</p>
<hr>
<h2 id="8-執行時機">8. 執行時機</h2>
<h3 id="pre-commit-hookgithookspre-commit">Pre-commit hook（<code>.githooks/pre-commit</code>）</h3>
<ol>
<li><code>mdtools fmt --fix</code> — 自動修格式；改動會 <code>git add</code> 回 staged，避免改完又沒進 commit。</li>
<li><code>mdtools lint</code> — 結構檢查；失敗阻擋 commit。</li>
<li><code>mdtools cards</code> — 完整性檢查；失敗阻擋 commit。</li>
</ol>
<h3 id="pre-push-hookgithookspre-push">Pre-push hook（<code>.githooks/pre-push</code>）</h3>
<p><code>pre-push</code> 的責任是把 CI 同款全量檢查提前到本機。<code>pre-commit</code> 為了速度只處理 staged markdown；<code>pre-push</code> 會跑 <code>make check</code>，也就是 <code>mdtools fmt --check content/</code>、<code>mdtools lint content/</code>、<code>mdtools cards content/</code>，讓整個 <code>content/</code> 的格式與連結 drift 在推送前被攔下。</p>
<p>啟用 hook：</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">git config core.hooksPath .githooks
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 或：make install-hooks</span></span></span></code></pre></div><h3 id="cigithubworkflowsmd-checkyml">CI（<code>.github/workflows/md-check.yml</code>）</h3>
<p>三個子命令都跑 <code>--check</code> / 嚴格模式，任何違規 fail CI。</p>
<hr>
<h2 id="9-寫作者使用指引">9. 寫作者使用指引</h2>
<ul>
<li>寫作時優先遵循本規範。pre-commit / pre-push 報錯時讀訊息修正；<strong>不可用 <code>git commit --no-verify</code> 或跳過 hook 的方式繞過檢查</strong>。</li>
<li>新增案例平行章節（例如多個「工具評測」「事件時序」）時不需登記到任何白名單 — siblings_only 自動判讀。</li>
<li>新增 URL 時優先採用裸 URL 轉換段的分級形式；若顯示文字含 TLD 字樣，確認 domain 與 href 完全一致。</li>
<li>新增卡片時確認首段與「概念位置」段各有至少一個相鄰卡片連結（L4 要求）；確認 front matter 含 <code>title</code> / <code>date</code> / <code>description</code> / <code>weight</code>（卡片嚴格層）。</li>
<li>程式碼區塊養成習慣先寫語言標示再填內容；純文字輸出用 <code>text</code>。</li>
</ul>
<hr>
<h2 id="10-規則擴充流程">10. 規則擴充流程</h2>
<p>新規則進入本文的路徑：</p>
<ol>
<li>先在 <code>scripts/mdtools/internal/rules/</code> 實作為可開關的 rule（預設關）。</li>
<li>在代表性檔案上測試誤判率。</li>
<li>誤判率 &lt; 1% 且有明確教材品質收益時，預設開啟並更新本文。</li>
<li>預設開啟後同步修正既有違規；若違規數量大，可分批 PR。</li>
</ol>
<hr>
<h2 id="11-為什麼自訂而不是用現成-markdownlint">11. 為什麼自訂而不是用現成 markdownlint</h2>
<p><code>markdownlint-cli2</code> 的 MD022 / MD024 / MD026 / MD029 / MD031 / MD032 / MD034 / MD036 / MD040 / MD047 / MD060 這些基礎規則都有（MD013 預設關閉、MD025 本規範嚴於原版），為什麼還要自寫？</p>
<p>關鍵差在<strong>卡片雙向完整性</strong>、<strong>反釣魚校驗</strong>、<strong>Front matter schema</strong> 這三類檢查，屬於跨文件 / AST 層 / 業務邏輯層的自訂邏輯，現成 linter 無法表達。這些檢查是 blog 品質的核心訊號，必須跟基礎格式檢查放在同一個工具鏈、同一次 AST parse 內處理，避免多個工具重複解析、重複維護。</p>
<p>另外 goldmark 是 Hugo 內建的 markdown parser。用同一個 parser 做 lint 保證「lint 通過 → Hugo render 一致」，杜絕兩套 parser 解讀不同的長尾 bug。</p>
<hr>
<p>本文為 blog 專案 Markdown 寫作規範的單一真實來源。repo 根目錄的 <code>AGENTS.md</code> 引用本文作為排版規範權威，規則與 <code>scripts/mdtools</code> 實作保持同步。</p>
]]></content:encoded></item><item><title>在部落格中設置文章資訊與tag</title><link>https://tarrragon.github.io/blog/posts/%E5%9C%A8%E9%83%A8%E8%90%BD%E6%A0%BC%E4%B8%AD%E8%A8%AD%E7%BD%AE%E6%96%87%E7%AB%A0%E8%B3%87%E8%A8%8A%E8%88%87tag/</link><pubDate>Tue, 09 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/%E5%9C%A8%E9%83%A8%E8%90%BD%E6%A0%BC%E4%B8%AD%E8%A8%AD%E7%BD%AE%E6%96%87%E7%AB%A0%E8%B3%87%E8%A8%8A%E8%88%87tag/</guid><description>&lt;p>使用YAML格式撰寫以下資訊放在文章開頭，title部分取代＃大標題&lt;/p></description><content:encoded><![CDATA[<p>使用YAML格式撰寫以下資訊放在文章開頭，title部分取代＃大標題</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;Flutter 可以使用的 ToggleButton 樣式&#34;</span><span class="w"> </span><span class="c">#設訂文章標題後不需再使用#大標題</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">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2025-09-09</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">draft</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="c">#是否設為草稿</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"></span><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;Markdown&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;blog心得&#34;</span><span class="p">,]</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w"></span><span class="nn">---</span></span></span></code></pre></div>]]></content:encoded></item><item><title>Hugo + Bear Cub 主題設定完整教學</title><link>https://tarrragon.github.io/blog/posts/hugo--bear-cub-%E4%B8%BB%E9%A1%8C%E8%A8%AD%E5%AE%9A%E5%AE%8C%E6%95%B4%E6%95%99%E5%AD%B8/</link><pubDate>Fri, 22 Aug 2025 20:41:50 +0800</pubDate><guid>https://tarrragon.github.io/blog/posts/hugo--bear-cub-%E4%B8%BB%E9%A1%8C%E8%A8%AD%E5%AE%9A%E5%AE%8C%E6%95%B4%E6%95%99%E5%AD%B8/</guid><description>&lt;h2 id="hugo--主題設定完整教學">Hugo + 主題設定完整教學&lt;/h2>
&lt;p>這篇文章記錄了我從零開始建立 Hugo 部落格並安裝 Bear Cub 主題的完整過程，包含常見的設定問題和解決方案。&lt;/p></description><content:encoded><![CDATA[<h2 id="hugo--主題設定完整教學">Hugo + 主題設定完整教學</h2>
<p>這篇文章記錄了我從零開始建立 Hugo 部落格並安裝 Bear Cub 主題的完整過程，包含常見的設定問題和解決方案。</p>
<h2 id="為什麼選擇-hugo-">為什麼選擇 Hugo ？</h2>
<h3 id="特點go-語言寫的速度極快">特點：Go 語言寫的，速度極快</h3>
<h3 id="優點">優點</h3>
<p>生成速度最快（</p>
<p>單一 binary → 不需要安裝 Node/Ruby 環境，跨平台好用</p>
<p>主題多</p>
<p>部署方便（build → 靜態檔 → push）</p>
<h3 id="缺點">缺點</h3>
<p>需要本地 build，再 push 結果到 GitHub Pages（不像 Jekyll 原生）</p>
<p>模板語法（Go Template）對新手稍有難度</p>
<p>Plugin 擴展性不如 Node.js 生態</p>
<h2 id="基本安裝與設定">基本安裝與設定</h2>
<h3 id="1-初始化-hugo-專案">1. 初始化 Hugo 專案</h3>





<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="c1"># 在現有的 GitHub 專案中初始化 Hugo</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hugo new site . --force</span></span></code></pre></div><h3 id="2-安裝-bear-cub-主題">2. 安裝 Bear Cub 主題</h3>





<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="c1"># 使用 git submodule 安裝（推薦）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">git submodule add https://github.com/clente/hugo-bearcub.git themes/hugo-bearcub</span></span></code></pre></div><h3 id="3-基本設定檔案-hugotoml">3. 基本設定檔案 (hugo.toml)</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 基本設定</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">baseURL</span> <span class="p">=</span> <span class="s1">&#39;https://你的用戶名.github.io/blog&#39;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">theme</span> <span class="p">=</span> <span class="s1">&#39;hugo-bearcub&#39;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">copyright</span> <span class="p">=</span> <span class="s1">&#39;你的名字 (CC BY 4.0)&#39;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">defaultContentLanguage</span> <span class="p">=</span> <span class="s1">&#39;zh-tw&#39;</span>
</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 class="c"># 產生 robots.txt 以利於 SEO</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">enableRobotsTXT</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c"># 設定語法高亮</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">markup</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">[</span><span class="nx">markup</span><span class="p">.</span><span class="nx">highlight</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nx">lineNos</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="nx">lineNumbersInTable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="nx">noClasses</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">[</span><span class="nx">markup</span><span class="p">.</span><span class="nx">goldmark</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">[</span><span class="nx">markup</span><span class="p">.</span><span class="nx">goldmark</span><span class="p">.</span><span class="nx">renderer</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">      <span class="nx">unsafe</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c"># 多語言模式設定</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">languages</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p">[</span><span class="nx">languages</span><span class="p">.</span><span class="nx">zh-tw</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="nx">title</span> <span class="p">=</span> <span class="s1">&#39;我的部落格&#39;</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nx">languageName</span> <span class="p">=</span> <span class="s1">&#39;zh-TW 🇹🇼&#39;</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="nx">LanguageCode</span> <span class="p">=</span> <span class="s1">&#39;zh-TW&#39;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="nx">contentDir</span> <span class="p">=</span> <span class="s1">&#39;content&#39;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="p">[</span><span class="nx">languages</span><span class="p">.</span><span class="nx">zh-tw</span><span class="p">.</span><span class="nx">params</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">      <span class="nx">madeWith</span> <span class="p">=</span> <span class="s1">&#39;使用 [Bear Cub](https://github.com/clente/hugo-bearcub) 製作&#39;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="c"># 網站描述</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="nx">description</span> <span class="p">=</span> <span class="s1">&#39;我的個人部落格，分享技術與生活&#39;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">  
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="c"># 網站標題</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="nx">title</span> <span class="p">=</span> <span class="s1">&#39;我的部落格&#39;</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="c"># 日期格式</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="nx">dateFormat</span> <span class="p">=</span> <span class="s1">&#39;2006-01-02&#39;</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">  
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="c"># 主題樣式 (original 或 herman)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="nx">themeStyle</span> <span class="p">=</span> <span class="s1">&#39;original&#39;</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">  
</span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="c"># 自動產生社群媒體卡片</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">  <span class="nx">generateSocialCard</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">  
</span></span><span class="line"><span class="ln">46</span><span class="cl">  <span class="c"># 作者資訊</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">  <span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">author</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="nx">name</span> <span class="p">=</span> <span class="s1">&#39;你的名字&#39;</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="nx">email</span> <span class="p">=</span> <span class="s1">&#39;your.email@example.com&#39;</span></span></span></code></pre></div><h2 id="首頁內容自訂">首頁內容自訂</h2>
<h3 id="建立首頁內容檔案">建立首頁內容檔案</h3>
<p>在 <code>content/_index.md</code> 建立檔案：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">title: &#34;首頁&#34;
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gh"># 歡迎來到我的個人部落格
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">嗨！我是 <span class="gs">**你的名字**</span>，歡迎來到我的數位天地。
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">## 關於這個部落格
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">這裡是我分享想法、學習心得和生活點滴的地方。你可以在這裡找到：
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> **技術文章**：程式開發經驗分享
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> **學習筆記**：新技術的學習過程
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> **專案記錄**：有趣的專案開發歷程
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> **生活感悟**：日常生活的思考與體驗
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">## 最新文章
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">歡迎查看我的[<span class="nt">最新文章</span>](<span class="na">/posts/</span>)，或者透過[<span class="nt">標籤</span>](<span class="na">/tags/</span>)來瀏覽特定主題的內容。
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">## 聯絡我
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">如果你想要與我交流或合作，歡迎透過以下方式聯絡：
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> Email: your.email<span class="ni">@example</span>.com
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 有任何問題或建議，也歡迎在文章下方留言
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">---
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">*感謝你的造訪，希望這些內容對你有所幫助！*</span></span></code></pre></div><h2 id="建立文章">建立文章</h2>
<h3 id="快速建立新文章">快速建立新文章</h3>





<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="c1"># 建立新文章</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hugo new content posts/文章標題.md</span></span></code></pre></div><h3 id="文章格式範例">文章格式範例</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">title: &#34;文章標題&#34;
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">date: 2025-08-22T20:41:50+08:00
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">draft: false
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">description: &#34;文章描述，用於 SEO 和社群分享&#34;
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">tags: [&#34;標籤1&#34;, &#34;標籤2&#34;]
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">文章內容寫在這裡...
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">## 小節標題
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 列表項目
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 另一個項目
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 程式碼範例
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span>\`\`\`bash
</span></span><span class="line"><span class="ln">18</span><span class="cl">echo &#34;Hello Hugo!&#34;
</span></span><span class="line"><span class="ln">19</span><span class="cl">\`\`\`</span></span></code></pre></div><h2 id="文章摘要與繼續閱讀設定">文章摘要與繼續閱讀設定</h2>
<h3 id="摘要顯示功能">摘要顯示功能</h3>
<p>為了避免文章列表頁面顯示完整內容，我們可以設定文章摘要功能，讓列表只顯示文章摘要並提供「繼續閱讀」按鈕。</p>
<h3 id="1-自動摘要設定">1. 自動摘要設定</h3>
<p>在 <code>hugo.toml</code> 中設定摘要長度：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># 摘要設定</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">summaryLength</span> <span class="p">=</span> <span class="mi">200</span>  <span class="c"># 摘要字數限制</span></span></span></code></pre></div><h4 id="說明">說明</h4>
<ul>
<li>當文章沒有手動設定摘要時，Hugo 會自動截取前 200 個字元</li>
<li>對於中文內容，200 個字元大約是 100-150 個中文字</li>
<li>對於英文內容，200 個字元大約是 30-40 個英文單詞</li>
</ul>
<h3 id="2-手動摘要設定">2. 手動摘要設定</h3>
<h4 id="方法一使用---more---標記">方法一：使用 <code>&lt;!--more--&gt;</code> 標記</h4>
<p>在文章中插入 <code>&lt;!--more--&gt;</code> 來精確控制摘要位置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">title: &#34;文章標題&#34;
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">date: 2025-01-XX
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">tags: [&#34;標籤1&#34;, &#34;標籤2&#34;]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">---
</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><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">這裡是更多內容...
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c">&lt;!--more--&gt;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">這裡是文章的其餘部分，只會在單篇文章頁面中顯示。</span></span></code></pre></div><h4 id="方法二使用-description-參數">方法二：使用 <code>description</code> 參數</h4>
<p>在文章的 front matter 中設定 <code>description</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">---
</span></span><span class="line"><span class="ln">2</span><span class="cl">title: &#34;文章標題&#34;
</span></span><span class="line"><span class="ln">3</span><span class="cl">date: 2025-01-XX
</span></span><span class="line"><span class="ln">4</span><span class="cl">description: &#34;這是文章的摘要，會顯示在列表中&#34;
</span></span><span class="line"><span class="ln">5</span><span class="cl">tags: [&#34;標籤1&#34;, &#34;標籤2&#34;]
</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><span class="line"><span class="ln">8</span><span class="cl">文章內容...</span></span></code></pre></div><h3 id="3-摘要優先順序">3. 摘要優先順序</h3>
<p>Hugo 的摘要顯示優先順序：</p>
<ol>
<li><strong><code>&lt;!--more--&gt;</code> 標記</strong> - 最高優先級</li>
<li><strong><code>description</code> 參數</strong> - 第二優先級</li>
<li><strong>自動截取</strong> - 使用 <code>summaryLength</code> 設定</li>
</ol>
<h3 id="4-列表頁面樣式">4. 列表頁面樣式</h3>
<p>設定完成後，文章列表會以卡片式設計顯示：</p>
<ul>
<li><strong>文章標題</strong>：可點擊進入完整文章</li>
<li><strong>發布日期</strong>：顯示在標題旁邊</li>
<li><strong>文章摘要</strong>：只顯示摘要內容</li>
<li><strong>繼續閱讀按鈕</strong>：當文章被截斷時顯示</li>
<li><strong>標籤</strong>：顯示文章相關標籤</li>
</ul>
<h2 id="github-actions-自動部署">GitHub Actions 自動部署</h2>
<h3 id="設定工作流程">設定工作流程</h3>
<p>在 <code>.github/workflows/hugo.yml</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="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy Hugo site to GitHub Pages</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</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">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;main&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">  </span><span class="nt">workflow_dispatch</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">read</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">pages</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">          </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="l">recursive</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Hugo</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v2</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">          </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;latest&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">          
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy to GitHub Pages</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v3</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">          </span><span class="nt">github_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">          </span><span class="nt">publish_dir</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span></span></span></code></pre></div><h2 id="常見問題與解決方案">常見問題與解決方案</h2>
<h3 id="社群媒體預覽卡片顯示亂碼問題">社群媒體預覽卡片顯示亂碼問題</h3>
<p>當你在 Discord、Facebook 等社群媒體分享文章時，如果預覽卡片中的中文顯示為問號（？），這是因為預設字型不支援中文字符。</p>
<h4 id="問題表現">問題表現</h4>
<ul>
<li>分享連結時，預覽卡片的標題顯示為問號</li>
<li>作者名稱顯示為 <code>map[name:作者名稱]</code> 而不是純文字</li>
</ul>
<h4 id="解決方案">解決方案</h4>
<h5 id="1-建立客製化的社群媒體卡片檔案">1. 建立客製化的社群媒體卡片檔案</h5>
<p>首先，在你的專案根目錄建立目錄並複製檔案：</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="c1"># 建立 layouts/partials 目錄</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">mkdir -p layouts/partials
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 複製主題的 social_card.html 到你的專案中</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">cp themes/hugo-bearcub/layouts/partials/social_card.html layouts/partials/</span></span></code></pre></div><h5 id="2-修改字型設定">2. 修改字型設定</h5>
<p>編輯 <code>layouts/partials/social_card.html</code> 檔案（注意這是你專案中的檔案，不是主題檔案）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">&lt;!-- 將第 2 行的字型 URL 替換為支援中文的字型 --&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">{{ $font := resources.GetRemote &#34;https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/TraditionalChinese/SourceHanSansTC-Bold.otf&#34; }}</span></span></code></pre></div><h5 id="3-修正作者名稱顯示">3. 修正作者名稱顯示</h5>
<p>在同一個檔案中，找到第 27 行並修改：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">&lt;!-- 原來的程式碼 --&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">{{ $author := (default $.Site.Params.author.name ($.Param &#34;author&#34;) ) }}
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c">&lt;!-- 修改為 --&gt;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">{{ $author := (or ($.Param &#34;author&#34;) $.Site.Params.author.name) }}</span></span></code></pre></div><blockquote>
<p><strong>重要說明</strong>：我們將檔案複製到專案的 <code>layouts/partials/</code> 目錄而不是直接修改主題檔案，這樣做的好處是：</p>
<ul>
<li>保持主題的 git submodule 乾淨</li>
<li>未來更新主題時不會遺失客製化設定</li>
<li>Hugo 會優先使用專案中的檔案覆蓋主題檔案</li>
</ul></blockquote>
<h5 id="4-確保社群媒體卡片功能開啟">4. 確保社群媒體卡片功能開啟</h5>
<p>在 <code>hugo.toml</code> 中確認設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="c"># 自動產生社群媒體卡片</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nx">generateSocialCard</span> <span class="p">=</span> <span class="kc">true</span></span></span></code></pre></div><h4 id="字型選擇說明">字型選擇說明</h4>
<ul>
<li><strong>Source Han Sans TC</strong>：Adobe 思源黑體繁體中文版本，支援完整中文字符集</li>
<li><strong>替代方案</strong>：也可以使用 Google 的 Noto Sans CJK 字型</li>
<li><strong>檔案大小</strong>：中文字型檔案較大，但現代瀏覽器會快取字型檔案</li>
</ul>
<p>修改完成後，重新建立並部署網站，社群媒體預覽就會正確顯示中文內容了。</p>
<h2 id="實用技巧與其他設定">實用技巧與其他設定</h2>
<h3 id="本地開發">本地開發</h3>





<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="c1"># 啟動開發伺服器</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hugo server
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 包含草稿</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">hugo server --buildDrafts
</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 class="c1"># 指定 IP 和 Port</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">hugo server --bind 0.0.0.0 --port <span class="m">1313</span></span></span></code></pre></div><h3 id="不同電腦上的工作流程">不同電腦上的工作流程</h3>
<h4 id="主要開發電腦">主要開發電腦</h4>
<ul>
<li>安裝 Hugo 進行完整開發</li>
<li>可以本地預覽和測試</li>
</ul>
<h4 id="其他電腦緊急更新">其他電腦/緊急更新</h4>
<ul>
<li>只需要 Git 和文字編輯器</li>
<li>直接編輯 Markdown 檔案推送即可</li>
<li>GitHub Actions 會自動編譯和部署</li>
</ul>
<h3 id="常見檔案位置">常見檔案位置</h3>
<ul>
<li><strong>設定檔</strong>：<code>hugo.toml</code></li>
<li><strong>首頁內容</strong>：<code>content/_index.md</code></li>
<li><strong>文章</strong>：<code>content/posts/</code></li>
<li><strong>主題</strong>：<code>themes/hugo-bearcub/</code></li>
<li><strong>靜態檔案</strong>：<code>static/</code></li>
</ul>
<h2 id="總結">總結</h2>
<p>透過這個設定流程，我們成功建立了：</p>
<ul>
<li>快速載入的靜態部落格</li>
<li>支援繁體中文的介面</li>
<li>自動化的 GitHub Pages 部署</li>
</ul>
<p>現在你可以專注於寫作，讓 Hugo 和 GitHub Actions 處理其他的技術細節！</p>
<hr>
<p>這篇教學記錄了我的實際設定過程，希望對其他想要建立 Hugo 部落格的朋友有所幫助。</p>]]></content:encoded></item></channel></rss>