# 2026-08-03 文件產出機制泛化 arc 收官 — SUMMARY

| 項目 | 內容 |
|------|------|
| Arc | 文件產出機制泛化（CM-1049 母案 + 四張子卡），2026-08-03 單日開局收官 |
| 產出 | `render_doc.py` / `render_index.py` 兩支 script、`docs/specs/_shared/` 四份共用資產、`doc-site-build` skill |
| 已建站 | FR-060 / FR-011.2 / FR-011.3 / FR-026 四站共 117 頁 |
| Notion | CM-1049（母）+ CM-1050 / 1051 / 1053 / 1067，全 Done |
| 狀態 | 七個 commit **全部未 push** |

## 1. 為何做（原始動機）

**不是「想做文件站」，是產一份文件的成本已經高到會讓 agent 死在半路。**

FR-060 討論稿手刻 1,003 行 HTML。第一支撰寫 agent 因單次輸出過長 **stalled mid-stream 陣亡——連檔案都沒開始寫**；重派改成「分 7 次寫入」才完成，約 11 分鐘。問題不在 agent 能力，在於**每產一份文件都要把版面、CSS、章節編號、mermaid 主題連同內容重刻一次**：內容是新的，那 800 行樣板是重複的。

決策者的指示是把重複的部分抽出去：**寫 md → 共用轉換 → 產 HTML**。寫稿者只負責內容，版面歸機制管。

## 2. 七個 commit

| commit | 內容 | 卡號 |
|--------|------|------|
| `c032b045` | ① mermaid 雲端相容性驗證 + CSS 共用設計（analysis 文件） | CM-1049 |
| `77b2c306` | ② 抽共用 CSS + `render_doc.py`，討論稿改 md | CM-1050 |
| `a3e7625b` | SPEC 站重 build，補齊 7 頁落後產物 | — |
| `56d7fcd9` | ③ FR 索引頁掃資料夾自動生成 | CM-1051 |
| `820d90a2` | ③.1 每個需求成為靜態網站 | CM-1053 |
| `df747570` | ③.2 共用側欄導覽（nav-data 驅動） | CM-1067 |
| `dc48f767` | ④ `doc-site-build` skill + 清 Artifact 舊流程 | CM-1067 |

## 3. 行為差異（以前 → 現在）

| | 以前 | 現在 |
|---|------|------|
| 寫一份討論稿 | 手刻 ~1,000 行 HTML，CSS 內嵌每頁一份 | 寫 md + 一段 front matter |
| 產出方式 | agent 逐段吐 HTML（曾因單次輸出過長陣亡） | 一行指令 build 整個資料夾 |
| 索引頁 | 手維護文件清單（必然 stale） | build 掃資料夾生成，含類型／標題／git 最後更新日 |
| 導覽 | 沒有；各頁互不相連 | 左側常駐檔案樹（分組 + current 高亮 + 收合）＋ 頁尾上下篇 |
| 新增一支 md | 無所謂（本來也沒導覽） | 只有 `nav-data.json` + 新頁 + `README.html` 變動，**既有頁零 diff** |
| 開檔 | 雙擊 html | 要跑 HTTP server（`file://` 被 CORS 擋，已拍板接受） |
| 分享 | 發 Artifact | **Artifact 停用**；repo 本地看，未來可部署靜態站 |

## 4. 三個關鍵決策的脈絡

### ① Artifact 停用引發的連鎖

原本每頁 CSS 內嵌、單檔自足，**唯一理由是 Artifact 的 CSP 擋外部 host**——不是設計偏好，是平台限制。實測索引頁 **64% 體積是那 13KB 重複 CSS**（20,835 bytes 中 13,383 是 CSS）。

決策者拍板停用 Artifact，原話：「既然已經把產生方式以及規則訂好了，就不需要它了」。停用後 CSS 改 `<link>` 外連，**連帶讓「重 build 逐位元組相同」這個原本的驗收條件也不再適用**——CSS 從內嵌變外連必然有 diff。驗收條件改成「渲染結果一致」。

這是本 arc 唯一一次驗收標準本身被改寫的地方，記在這裡是因為未來若有人拿舊條件回頭驗會對不上。

### ② 「模板要共用」是本 arc 的技術核心

決策者原話：**「模板要共用喔，不要加一個檔案就每一個 html 都要改，這樣很笨」**。

當時 `render_index.py` 的 `build_pages()` 把 prev/next 在 **build 時算好寫死進每一頁**——新增一支 md，它前後兩頁的 pager 就過期。側欄若照同一做法會更嚴重：每一頁都嵌一份完整檔案樹，新增一支 md 等於整站全部 stale。

正解是比照 SPEC 站的 nav-data 驅動：**build 只產一份 `nav-data.json`，每頁只放空殼**——

```html
<nav class="docnav" data-current="handoff/xxx.html" data-navdata="../nav-data.json"></nav>
```

內容由 `doc-nav.js` 在瀏覽器端填。實測驗證：新增一支 md 後，變動的只有 `nav-data.json`、新頁本身、`README.html`（它本來就是檔案清單）；**既有頁 html 零 diff**，刪掉測試 md 重 build 完全復原。

判準留給未來：**「新增一個項目時，有幾個檔會變動？」超過「資料檔 + 新項目本身」就是寫死了。**

代價是要跑 HTTP server（`file://` 的 `fetch()` 被 CORS 擋）。與 SPEC 站同一限制，決策者已拍板接受；被擋時側欄會顯示明確指引、pager 空殼自動移除，**不會靜默裸奔**。

### ③ ④ 為什麼開新 skill 而不併進 `big-feature-workflow`

判準是**使用者不只一種**：討論稿走 `big-feature-workflow`、handoff / STATE / LOG 走 `closing-and-handoff`、實作計畫走一般開發流程。併進 `big-feature-workflow` 等於把一個通用機制鎖在「只有大型新功能才載入」的 skill 裡——寫 handoff 的 session 根本讀不到。

**skill 的 `description` 就是載入開關**，這是決定落點的實際依據，不是文件分類美感問題。

## 5. 落地的檔案結構

```
scripts/deliverables/
  render_html.py    ← SPEC 手冊站（既有，全程未動一字）
  render_doc.py     ← 單頁 md → HTML（渲染核心）
  render_index.py   ← 吃資料夾，整站 build + nav-data.json

docs/specs/_shared/
  site.css / site.js          ← SPEC 手冊專用（全程未動）
  doc-base.css                ← 排版底：token／字級／表格／code／callout／引用
  doc-artifact.css            ← 版型：側欄／頂欄／首屏／章節／statgrid／grid2／pager
  doc-nav.js                  ← 側欄渲染 + 收合 + pager 填值
  mermaid.min.js              ← 圖渲染，3.5MB 入版控不走 CDN

.claude/skills/doc-site-build/SKILL.md   ← 機制規範（197 行）
```

`doc-artifact.css` 的檔名是歷史沿革（原為 Artifact 專屬），Artifact 停用後就是文件站版型檔，未改名以免整批產物重生。

**產物不是單檔自足**——CSS/JS 走相對路徑外連，搬走時要連同 `docs/specs/_shared/` 一起帶。

### 拍板紀錄

| 題目 | 決定 |
|------|------|
| 既有 26 份手刻 html | 不回頭轉，新機制只約束新產出 |
| 索引頁設定格式 | `README.md` front matter，不開 `_meta.yaml`（一個 repo 不要兩套設定慣例）|
| 站的使用情境 | repo 本地／未來部署靜態站 |
| Artifact | 停用 |
| `mermaid.min.js` 入版控 | 維持，不走 CDN（離線與內網部署要看得到圖）|
| 側欄配色 | 複製一份改青灰放 `doc-artifact.css`，`site.css` 一字不動 |
| `file://` 被 CORS 擋 | 接受，與 SPEC 站一致 |
| ④ 落點 | 新開 `doc-site-build` skill |

## 6. 驗收結果（協調者實跑，不採信自報）

- **紅線守住**：`render_html.py` / `site.css` / `site.js` 完全未出現在任何 diff
- **無任何 `.md` 內容被改**（產物 html 可重生，md 是源）
- **共用模板**：自行加測試 md 重 build → 既有 5 份文件頁零 diff，刪除後完全復原
- **側欄實看**：252px 青灰系；分組「需求與討論／設計／交接與收口」；current 高亮正確；`handoff/` 深層頁 `data-navdata="../nav-data.json"` 相對路徑正確
- **pager 已成空殼**，`next → design.html` 由 JS 填入（非寫死）
- **build 冪等**；**SPEC 站零 diff**；**死連結未新增**（5 條 pre-existing）
- **④ 走查**：照新 skill 從零建 FR-999 假需求走完整條流程（README + 討論稿 + 無 front matter 的 handoff → build → HTTP server 實看），驗完刪除

## 7. 教訓

### 教訓一：跨頁共用的東西不可在 build 時寫死

導覽、上下篇、目錄這類**跨頁共用**的內容，在 build 時算好寫死進每一頁，等於把「一份資料」複製成 N 份副本——新增一個項目，N 份全部過期。正解是 build 只產一份資料檔，每頁放空殼由前端填。

判準：**問「新增一個項目時，有幾個檔會變動？」超過「資料檔 + 新項目本身」就是寫死了。**

代價（要跑 HTTP server）本專案已接受。詳見 memory `feedback_shared_template_not_buildtime_hardcode`。

### 教訓二：流程型 skill 寫完要照它從零走一遍，不能只審稿

④ 的走查用新 skill 從零建了一個 FR-999 假需求跑完整條流程，**callout 那項先失敗**：`render_doc.py` docstring 的範例沒寫空行，照抄會讓 pandoc 把標籤、內文、建議併成同一個 `<p>`，`.co-label` 小標不生成、頁面擠成一行。

**這個 bug 一直存在，只是從沒人照著抄過。** 純審稿看不出來——範例「看起來對」，錯的是肉眼不顯眼的空行。已在三處補警語，FR-999 走查完即刪。

詳見 memory `feedback_skill_walkthrough_from_scratch`。

## 8. 已知 follow-up（皆非 blocker）

1. **七個 commit 全部未 push**——等決策者明示
2. **handoff 頁頂欄章節多時末端裁切**（最後一項只剩半截）。有側欄接手導覽職能後不影響使用
3. **FR-026 的 `README.md` 沒有 front matter**，正文原樣排在自動生成的文件清單之前（標題已改善，不再顯示資料夾路徑名）

## 9. 座標

- 機制規範（single source of truth）：`.claude/skills/doc-site-build/SKILL.md`
- 設計與實測全文：`docs/analysis/2026-08-03-doc-html-pipeline.md`
- 本 arc 索引頁：`docs/features/docs-site-generalization/README.md`
- memory：`project_doc_site_build_mechanism` / `feedback_shared_template_not_buildtime_hardcode` / `feedback_skill_walkthrough_from_scratch`
