草稿堆積的連鎖起因
bobo-labs 的發布流程分兩層:/bobo-draft-pending 自動草擬草稿到 _pending/, /bobo-autopublish 的 QA gate 審過後才 mv 到 writing/ 正式上站。 兩層分工清楚,但有一個系統性缺口:草擬模板完全沒有 canonical 生成邏輯。
canonical tag 是 SEO 的基礎——告訴搜尋引擎「這個頁面的正式 URL 是什麼」, 防止重複內容問題。autopublish 的 QA gate 把它列為必要結構要素,缺了就擋。 結果每篇草稿從一生出來就缺 canonical,每篇都過不了 QA,六篇連環積壓。
矛盾:grep 說有,QA 說沒有
開工檢查時,對 boboweb-decision-rules.html 跑 grep canonical,有命中。 但 ops log 顯示「structure check: canonical missing (mentions canonical in text only)」。 看起來很矛盾——工具說有,log 說沒有。
逐行查證之後,真相很直白:boboweb-decision-rules.html 裡, 「canonical」這個字串全部出現在內文——h2 標題、code 範例、takeaway 段落。 head 區確實從來就沒有 link tag。 QA gate 的 structure check 看的是位置,不是關鍵字是否存在。 grep 命中關鍵字,但沒說明在哪裡命中。
治本 vs 治標:兩件事都要做
autopublish 的設計原則是「只 QA + promote,不修改草稿內容」。 這個設計選擇合理:如果 autopublish 自動修改草稿,那每次發布的內容就不再是人工確認過的版本。 但這也意味著,缺 canonical 的草稿即使每天 06:00 定時跑,也永遠過不了 QA、永遠不會自動發。
所以要雙管齊下。治未來:修 bobo-draft-pending 模板, 在 meta description 之後、link stylesheet 之前, 加 canonical 生成規則,URL 的檔名要和草稿檔名完全一致。 治積壓:五篇既有草稿逐一手動補 head 的 canonical,再順手查其他 head 要素。
補 canonical 時還發現 2026-06-01 那篇的 viewport meta 被誤植成 name="version"—— 這是模板偶發的生成瑕疵,補 canonical 時順手揪出來一起修。 對每篇補完之後做 QA 自檢,確認 canonical 指向自身且 viewport 存在,5/5 PASS,才進入發布流程。
ops log 裡的那句話值得細讀
「mentions canonical in text only」——這句 log 其實說得很精準。 canonical 有提到(mentions),但只在文字裡(in text)。 不是沒有,是放錯位置了。
如果只看「mentions」就以為「有」,就會誤診。 ops log 是給機器和人看的,設計好的 log 會盡量把診斷說清楚, 但需要讀者有足夠的 context 才能正確解讀「在哪裡有」這層語意。
關鍵教訓
grep 命中 ≠ 結構正確:關鍵字存在是一回事,出現在正確的位置是另一回事。診斷 HTML 結構問題時要問「在哪裡命中」,不只是「有沒有命中」。
系統性缺口要治根、不只治症:每篇草稿補 canonical 是治症;修模板讓未來的草稿自帶 canonical 才是治根。兩件事都要做,先後順序可以商量,但不能只做一件。
QA gate 不該修改被審的檔案:如果 QA 自動修復草稿,那確認對象就不再是人工寫的那個版本。QA 只擋不修,是設計原則而非限制。
補結構時順手做完整性掃描:補 canonical 的時候順手掃了其他 head 要素,找到一個 viewport 誤植。補丁時機是做完整檢查最自然的節點。
log 裡的括號說明往往是關鍵:「canonical missing (mentions canonical in text only)」——括號裡的備註才是真正的診斷,主句是結論。讀 log 要讀完整,不能只看第一個結論字。