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.pybuild_pages() 把 prev/next 在 build 時算好寫死進每一頁——新增一支 md,它前後兩頁的 pager 就過期。側欄若照同一做法會更嚴重:每一頁都嵌一份完整檔案樹,新增一支 md 等於整站全部 stale。

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

<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.csssite.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