白色卡片上的赭紅色 Claude 星形標誌與黑色字樣

用 Claude Code 的 routine 從頭到尾發布這個部落格 — 不用碰手,連電腦都不用開著

Claude Code 的 routine 把一篇草稿文章翻譯、檢查、合併,全部在這個部落格上一貫完成,過程中不需要任何一台機器保持開機。

本頁目錄

引言

Claude Code 的雲端 routine 沒有 AskUserQuestion 這個工具。也就是說,它沒辦法在執行到一半時停下來問一句確認。上一輪針對 Claude Code 雲端 routine 的震測把這件事實際驗證了一遍:交給 routine 的任何東西,都得以一份完整、執行中沒有任何東西需要再問的規格出現。順理成章的下一步,就是繼續往前推進:把一篇草稿文章從本機檔案一路帶到上線、合併完成的文章,開始之後自己什麼都不做,而且整個過程都不需要有一台機器一直開著。現在這確實可行了——/blog cloud-publish 把草稿交給一個 routine,由它翻譯、驗證、開 pull request,並在 CI 綠燈之後合併,全程都在雲端完成。 打造這套流程的過程中,先冒出來三個真實的問題。一篇早就躺在預設分支上的草稿。一篇已經不符合這個部落格現行規則的舊文章。一個擁有的寫入權限超過工作實際需要的 routine。這篇文章依序談這幾件事、第一次完整跑通的正式執行,以及在把放棄的代價講清楚之後,才做出的把僅存的手動步驟拿掉這個決定。

整條流程,從頭到尾

cloud-publish 的流程擁有者自己機器上的草稿交給 cloud-publish 後,接下來全部在雲端執行的區域:routine 進行翻譯與驗證,開出 pull request,並確認 CI 是否綠燈。綠燈就自動合併並上線,否則就停下來回報,而不是用猜的。在雲端執行 — 不需要機器保持開機草稿(自己的機器)cloud-publishroutinepull requestCI 是否綠燈yes合併(自動)上線no停止並回報
「cloud-publish」左邊的一切都在本機執行。從那之後的一切都在雲端執行,最後不是自動合併,就是停下來回報。

/blog cloud-publish <slug> 會把草稿 push 到自己的分支,並開一個已經貼上 cloud-publish 標籤的 GitHub issue。 這個標籤會觸發一個事先在 claude.ai/code/routines 設定好一次的 routine。從那之後,routine checkout 那個分支、翻譯每個語系、跑本機流程同一套審查關卡。它會開 pull request,等 CI 通過就合併。routine 本身的指示保持通用:讀觸發這次執行的 issue,照著寫的內容做,別的都不做。實際的任務,包括「不要呼叫 /release,git 和 gh 的操作要直接做」,是寫在 issue 內文裡,而不是 routine 的設定裡。這代表同一個 routine 不用重新編輯,就能服務未來任何一次發布請求。

issue 內文本身,就是 routine 讀進去、實際執行的任務清單。這裡只留下決定走向的關鍵步驟:

cloud-publish-issue.md
## Task
1. Checkout branch `draft/<slug>`, not the default branch.
2. Run `/blog publish <slug>` for every locale: translate, run the Section B
review gate, flip `status` to `published`, verify with `pnpm check` and
`pnpm build`.
// … commit and push to the same branch
5. Open a PR against `main` from `draft/<slug>` with `gh pr create` directly.
Do **not** invoke the `/release` skill; it requires interactive
confirmation this session has no way to give.
// … wait for CI, then run a code review against the PR
8. If CI is green **and** the review reported no `CONFIRMED` correctness
finding, merge immediately: `gh pr merge --squash --delete-branch`.
9. If the review reported a `CONFIRMED` correctness finding, fix it yourself
and keep going. Two repair rounds is the whole budget for this PR.
10. If a `CONFIRMED` finding still stands after the repair budget is spent,
and CI is green, merge anyway, then open a follow-up issue naming what
still stands.
// … close the tracking issue once merged
12. If CI is red at any point, repair it, and never merge it red. This is the
one gate that actually stops the run.

一篇早就到了 main 的草稿

第一次正式測試用的是一篇舊的、沒寫完的草稿:python-yield-async-generator-agent-streaming-inputstatus: 'draft' 還沒改。結果發現它早就被提交進 main 了,是先前某次不相關的發布,順手改了一批混雜的檔案時留下的。這在這裡其實是完全安全的狀態status: 'draft' 不管檔案躺在哪個分支上,都會把文章排除在正式建置之外),但 cloud-publish 分支步驟的第一版沒有考慮到這種情況。它只檢查有沒有未提交的本機變更,發現沒有,就會回報「沒有東西可以交出去」。這是錯的:明明有一整篇文章準備好要發布,只是不在腳本原本找的地方而已。

修正的方式是把這一步拆成兩種情況。 有未提交的變更,就照原本的路徑 staging、commit、push 到新分支,這是剛寫完草稿後最常見的路徑。沒有未提交的東西,但文章確實存在,就直接從 main 切出一個分支,不需要新建任何 commit:終點一樣,只是不用寫一個其實什麼都沒改動的 commit 就到得了那裡。

一篇沒通過今天自己規則的舊草稿

過了那一關,不代表草稿就能發布了。用現行的審查關卡跑一遍,浮現出兩個跟雲端流程本身完全無關的實際缺陷。標題長達 105 個字元,超過 104 字元的硬上限。而且 Introduction 的開頭,幾乎完全符合這個部落格自己的風格指南點名禁止的那種模式:用第二人稱的假設情境,取代一個真實發生過的事件。原句是「打造 AI agent 的時候,你有沒有遇過這種問題?」規則要求的是,要嘛是一個真實的第一人稱事件,要嘛是完全不帶代名詞的一般陳述。

兩個都沒辦法用猜的來修。 這篇舊草稿背後沒有一個可以回頭讀的 session,而且再確認過後,發現這篇文章本來就是通用知識,不是一段建置經歷,這一點是確認過的,不是假設的。所以就照規則自己提出的替代方案來修:把開頭改寫成不帶代名詞的一般陳述,而不是編造一段根本沒發生過的第一人稱事件。同樣「你可以……」這種語氣在本文好幾個段落裡反覆出現,也一併做了同樣的處理,而不是只修機械檢查剛好抓到的那一句,為的是整篇一致。

權限超過工作實際需要的 connector

cloud-publish 的「Edit routine」畫面,顯示 Gmail、Google Calendar、Google Drive,以及一個「visualize」connector 全部都已連接,並附有警告:Claude 在執行期間可以使用這些 connector 的所有工具,包括寫入,而不需要詢問許可

這個 routine 從來用不到的四個 connector,預設就是啟用的,而且每一個都有無人看管下的寫入權限。

實際設定這個 routine 的過程,又浮現出另一種缺陷。新的 routine 預設會納入當下所有已連接的 connector,這一個就這樣連上了 Gmail、Google Calendar、Google Drive,還有一個繪圖用的 connector,而一個只負責讀 GitHub issue、跑 shell 指令的 routine,完全沒有正當理由碰其中任何一個。routine 自己的編輯畫面把這句話講得很直白:Claude 可以使用任一個已連接 connector 的所有工具,包括寫入,執行期間不會跳出任何許可提示。

這份工作四個都用不到。 跟只把權限授予一個 skill 實際會下達的指令一樣的邏輯,它們在儲存前就被拿掉了。無人值守的流程應該只能碰到工作真正需要的東西,別的都不行,尤其是這種唯一觸發條件只是 GitHub issue 上一個標籤的流程。

第一次完整跑通的執行

一個已合併 GitHub pull request 的 Conversation 分頁,顯示雲端 routine 自己生成的摘要:以三個語系發布一篇文章、從零寫出 zh-TW 翻譯、重新翻譯日文以配合修訂過的原文、把 status 從 draft 改成 published,以及 Section A 審查關卡本身的通過/失敗細節

routine 自己對做了什麼的說明,留在 pull request 的摘要留言裡。

把上面兩個問題解決之後,實際執行順順利利地跑完了。 routine checkout 草稿的分支、把它翻譯成日文和 zh-TW、對三個語系都跑了審查關卡,並開出一個 pull request,它的摘要讀起來就像一份稱職的交接筆記:發布了什麼、zh-TW 翻譯是從零寫的、日文翻譯是重新翻譯以配合修訂過的原文而不是留著舊的、以及在任何東西發布之前,來源關卡確認過的具體通過條件。

來自 cloud-publish 的手機推播通知,內容為「PR #189 opened: python-yield-async-generator-agent-streaming-input published in all 3 locales (en/ja/zh-tw)」

第一則通知,確認翻譯與發布工作已經完成,而且完全沒開過筆電。

幾分鐘後的第二則手機推播通知,內容為「PR #189 CI is green and mergeable (checks: Check & build check mark, smoke test check mark)」

第二則,幾分鐘後送達:CI 綠燈,pull request 已經可以合併。

兩則通知都不需要開著機器才能送達,也不需要有人在旁邊盯著才有用:第一則說翻譯工作做完了,第二則說可以安全合併了。這次執行是在自動合併之前跑的,所以照設計在那裡停下來。文章之後是手動合併的。它現在已經上線,出現在 sitemap 裡,三種語言都有,由一個從頭到尾沒碰過本機機器的 routine 發布完成。

拿掉最後一個手動步驟

pull request 開出來,不等於文章已經上線,還留著的那一個手動步驟就是合併本身。要拿掉它,得先把一個真實的取捨想清楚:這條流程裡沒有任何一步能機械式地驗證翻譯真的「正確」,能驗證的只有結構上說不說得通。pnpm checkpnpm build 檢查的是形狀,不是意義。這個部落格自己的通用發布流程 /release,正是因為這個理由,才把合併這一步保留成互動式的。這是刻意的決定,是整條流程裡唯一沒拿掉的一點。

即使如此,決定要自動化,也是在先把這一點講清楚之後才做的,不是預設就滑進去的。我判斷這份便利值得,而且直接把話說出來的人是我。 而拿掉這個關卡的理由,也沒有留在心裡不說,而是記在 routine 自己的指示裡,好讓之後的讀者不會把自動合併誤會成一時疏忽。

總結

把一篇草稿文章交給 routine,現在就能走到一篇合併完成、上線的文章,過程中不需要任何機器保持開機。 有一次真實的執行證實了這一點:翻譯、審查、開成 pull request,這次 session 之後也設定成只要 CI 通過就自動合併。走到這一步,需要三個跟自動化本身無關的真實修正。一篇早就在 main 上的草稿需要自己的分支處理方式。一篇舊文章需要真正重寫而不是修補。routine 預設的 connector 範圍,得在真正跑之前先收窄。還沒經過完整驗證的,是自動合併本身。 驗證過這條流程的那一次正式執行,是在這個改動之前跑的。下一次真正的發布,同時也是自動合併第一次的正式測試。

分享這篇文章