# Claude Codeのルーチンでこのブログを最初から最後まで公開する — PCの電源を切っていても手放しで完了する

> Claude Codeのルーチンが下書き記事を翻訳し、確認し、マージするところまでこのブログ上で一貫して行います。マシンを起動したままにする必要は一切ありません。

- Source: https://oharu121.com/ja/blog/claude-code-routine-cloud-publish-hands-free-merge/
- Published: 2026-08-24T22:00:00+09:00
- Tags: Claude Code, Claude Routines, 自動化, AIエージェント

---
**要点**

- 下書き記事をClaude Codeのルーチンに任せるだけで、翻訳、検証、プルリクエストの作成、マージまでを含む「公開済みの記事」に至るようになりました。どの工程でもマシンを起動したままにしておく必要はありません。
- ルーチンの既定のコネクタにはGmail、カレンダー、Driveへの無制限の書き込みアクセスが含まれていましたが、どれもこの作業には不要でした。修正は既定値を信用することではなく、それらを外すことでした。
- ルーチンは特に指定しない限りリポジトリの既定ブランチをクローンします。そのため、すでに`main`に載っている下書き(ここでは実害のない、ふつうにあり得る状態)にも専用の扱いが要り、「公開するものがない」という誤った判定を避ける必要がありました。
- コードが何も変わっていなくても、古い下書きは今日のスタイルルールに落ちることがあります。今回は機械的な修正では済まず、実際に書き直す必要がありました。
- 自動マージは、この工程がどんな翻訳でも「正しいこと」までは検証できず、「構造として妥当なこと」しか検証できないというトレードオフを明示したうえで下した意図的な選択です。

## はじめに

Claude Codeのクラウドルーチンには`AskUserQuestion`ツールがありません。実行の途中で止まって確認を取ることができないということです。[前回のシェイクダウンテスト](/blog/claude-code-routines-github-issue-pr-sandbox-askuserquestion/)は、それを身をもって確かめました。ルーチンに任せるものは何であれ、実行中に尋ねる余地を一切残さない完全な仕様として渡す必要があります。当然の次の一手は、その先へ進むことでした。下書き記事をローカルのファイルから公開済みでマージ済みの記事まで、開始した後は自分では何もせず、マシンをその間ずっと起動したままにする必要もなく、一気に運ぶことです。**それが今、実際に動くようになりました——`/blog cloud-publish`は下書きをルーチンに渡し、ルーチンがそれを翻訳し、検証し、プルリクエストを開き、CIがgreenになれば完全にクラウド上でマージします。** それを作る過程では、まず三つの現実の問題が表面化しました。下書きがすでに既定ブランチに載っていたこと。古い記事の文体がこのブログの現行のルールにもう合わなくなっていたこと。ルーチンが仕事に必要な以上の書き込み権限を持っていたことです。この記事では、その一つ一つと、最初にエンドツーエンドで動いた本番の実行、そして手放す代償を先に明示したうえで下した、最後に残っていた手動の一手を外すという判断を順に扱います。

## 一連のパイプライン、最初から最後まで

*Figure — Pipeline: 「cloud-publish」より左はすべてローカルで実行されます。そこから先はすべてクラウド上で実行され、自動マージか、停止して報告するかのどちらかで終わります。*

**`/blog cloud-publish <slug>`は下書きを専用のブランチにpushし、あらかじめ`cloud-publish`ラベルが付いたGitHub Issueを開きます。** このラベルが、`claude.ai/code/routines`で一度だけ設定しておいたルーチンを起動します。そこから先、ルーチンはそのブランチをチェックアウトし、すべてのロケールを翻訳し、ローカルのパイプラインと同じレビューゲートを実行します。プルリクエストを開き、CIが通ればマージします。**ルーチン自体の指示は汎用的なままにしてあります。**「起動の原因となったIssueを読み、書かれている通りにそれだけを行う」というものです。「`/release`は呼び出さず、gitと`gh`の操作は直接行う」といった実際のタスクは、ルーチンの設定ではなくIssue本文の側にあります。だからこそ同じルーチンは、設定を変えずに、この先どんな公開依頼にも使い回せます。

Issue本文そのものが、ルーチンが読んで実行する実際のタスクリストです。ここでは判断の要になる手順だけに絞って載せています。

```markdown title="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-input`で、`status: 'draft'`のままでした。調べてみると、無関係な別のリリースが複数のファイルをまとめて触った際に、すでに`main`へコミットされていたことがわかりました。**それ自体はここでは実害のない状態です**(`status: 'draft'`は、どのブランチに載っていようと本番ビルドから記事を除外します)。しかし`cloud-publish`のブランチ作成ステップの最初のバージョンは、この状態を想定していませんでした。未コミットのローカル変更の有無だけを確認し、何もなければ「渡すものはない」と報告するようになっていました。それは誤りでした。渡すべき記事は丸ごと存在していました。ただスクリプトが探していた場所にいなかっただけです。

**修正はこのステップを二つのケースに分けることでした。** 未コミットの変更があれば、それをステージし、コミットし、新しいブランチにpushします。下書きしたばかりのふつうの経路です。未コミットのものは何もないが記事は存在する場合は、新規のコミットを作らずに`main`から直接ブランチを切ります。行き先は同じで、実際には何も変わっていないことを説明するコミットを作らずに、そこへたどり着きます。

## 今日のルール自体に落ちた古い下書き

そのハードルを越えても、下書きが公開可能になったわけではありませんでした。現行のレビューゲートを通すと、**クラウドのパイプラインとはまったく関係のない、実在する二つの欠陥**が表面化しました。タイトルは104文字の上限に対して105文字ありました。そしてIntroductionの冒頭は、このブログ自身のスタイルガイドが禁止パターンとして名指ししているものに、ほとんどそのまま合致していました。実在のインシデントの代わりに二人称の仮定を置く書き方です。実際の一文は「AIエージェントを作っていて、こんな疑問にぶつかったことはないだろうか?」というものでした。ルールが求めているのは、実在の一人称のインシデントか、代名詞を一切使わない一般論のどちらかです。

**どちらも推測では直せませんでした。** この古い下書きの背後には遡って読み込めるセッションがなく、しかも調べ直した結果、この記事はビルドの体験談というより一般的な知識であることが確認されました。想像ではなく確認できたことです。そこでルールが明示している代替案に従い、冒頭を代名詞のない一般論として書き直しました。起きてもいない一人称のインシデントをでっち上げることはしません。同じ「あなたは……」という枠組みが本文の複数の節にも繰り返し現れており、機械的なチェックが一文だけを検出したからといってその一文だけを直すのではなく、一貫性のために同じ処置をひと通り施しました。

## 仕事に必要な以上のアクセス権を持っていたコネクタ

*Image: cloud-publishの「Edit routine」画面。Gmail、Google Calendar、Google Drive、そして「visualize」コネクタがすべて接続されており、これらのコネクタからのすべてのツールを、書き込みも含めて、実行中に承認を求めることなくClaudeが使用できるという警告が表示されている*

*このルーチンが一度も使わない四つのコネクタが、既定で有効になっており、それぞれが無人のまま書き込みできる状態でした。*

実際にルーチンを設定してみると、別種の欠陥が表面化しました。**新しいルーチンは、その時点で接続済みのコネクタをすべて既定で含みます。** 今回のルーチンには、Gmail、Google Calendar、Google Drive、そして図解生成用のコネクタが紐づいていましたが、GitHub Issueを読んでシェルコマンドを実行するだけのルーチンにとって、そのどれ一つとして正当に触る理由がありませんでした。ルーチン自身の編集画面はそれが何を意味するかを率直に述べています。含まれているコネクタのすべてのツールを、書き込みも含めて、実行中に承認を求めることなくClaudeは使えるということです。

**この仕事にはその四つのどれも必要ありませんでした。** スキルが実際に発行するコマンドだけに権限の付与を絞るのと同じ理屈で、それらは保存前に取り除きました。無人プロセスは、その仕事が必要とするものだけに届くべきであり、それ以外には届くべきではありません。まして、GitHub Issueのラベルだけが唯一の起動条件になっているようなプロセスならなおさらです。

## 最初にエンドツーエンドで動いた実行

*Image: マージ済みGitHubプルリクエストのConversationタブ。クラウドルーチン自身が生成したまとめが表示されており、三つのロケールで記事を公開したこと、zh-TW訳をゼロから書いたこと、改訂されたソースに合わせて日本語訳を再翻訳したこと、statusをdraftからpublishedに切り替えたこと、そしてSection Aレビューゲート自体の合否の詳細が書かれている*

*何をしたかについてのルーチン自身の説明。プルリクエストのまとめコメントとして残された。*

**その二つを片づけたところで、実際の実行は問題なく動きました。** ルーチンは下書きのブランチをチェックアウトし、日本語とzh-TWに翻訳し、三つのロケールすべてに対してレビューゲートを実行し、有能な引き継ぎメモのように読めるまとめを添えてプルリクエストを開きました。何を公開したか、zh-TW訳をゼロから書いたこと、古いままにせず改訂されたソースに合わせて日本語訳を再翻訳したこと、そして何であれ公開する前にソースゲートが確認した具体的な合格条件——すべてが書かれていました。

*Image: cloud-publishからのスマートフォンのプッシュ通知。「PR #189 opened: python-yield-async-generator-agent-streaming-input published in all 3 locales (en/ja/zh-tw)」と表示されている*

*最初の通知。ノートPCをどこにも開かないまま、翻訳と公開の作業が終わったことを伝える。*

*Image: cloud-publishからの二つ目のスマートフォンのプッシュ通知。「PR #189 CI is green and mergeable (checks: Check & build check mark, smoke test check mark)」と表示されている*

*数分後に届いた二つ目の通知。CIがgreenになり、プルリクエストがマージ可能になったことを伝える。*

どちらの通知も、届くのにマシンを起動しておく必要も、誰かが見張っている必要もありませんでした。一つ目は翻訳作業が終わったことを、二つ目はマージしても安全であることを伝えていました。この実行は自動マージより前のものだったので、設計通りそこで止まりました。記事はそのあと手動でマージされました。**今はもう公開済みで、サイトマップにも載り、三言語すべてで**、ローカルマシンに一度も触れることなくルーチンによって公開されています。

## 最後に残った手動の一手を外す

**プルリクエストが開かれることと記事が公開されることは同じではありません**。まだ手動で残っていた一手はマージそのものでした。それを外すには、まず現実のトレードオフを検討する必要がありました。このパイプラインのどこにも、翻訳が*正しい*ことを機械的に検証する仕組みはありません。検証できるのは構造として妥当かどうかだけです。`pnpm check`と`pnpm build`が確認するのは形であって、意味ではありません。このブログ自身の汎用リリースフローである`/release`が、まさにその理由でマージのステップだけを対話的なまま残しています。それは意図的なことで、フロー全体の中で唯一外さなかった一点です。

それでも自動化するという判断は、その点を先に明示したうえで下されました。何かにデフォルトで流されたわけではありません。**利便性の方に価値があると判断し、それをはっきりそう述べたのは私です。** そして、このゲートを外す理由は、暗黙のままにせず、ルーチン自身の指示の中に記録してあります。あとから読む人が自動マージを見落としや事故だと誤解しないように。

## まとめ

**下書き記事をルーチンに渡すだけで、マシンを起動したままにすることなく、マージ済みで公開済みの記事に至るようになりました。** それは実際の実行によって確かめられています。翻訳され、レビューされ、プルリクエストとして開かれ、このセッションのあとはCIが通れば自動的にマージされる状態になりました。そこに至るまでに、自動化そのものとは無関係な、実在する三つの修正が必要でした。すでに`main`に載っていた下書きには専用のブランチ分岐ケースが要りました。古い記事には機械的な修正ではなく実際の書き直しが要りました。そしてルーチンの既定のコネクタの範囲は、本番で一度でも動かす前に絞り込む必要がありました。**まだエンドツーエンドで実証されていないのは自動マージそのものです。** このパイプラインを検証した唯一の本番実行は、その変更より前のものでした。次の実際の公開が、自動マージにとっても最初の本番テストになります。

> **Warning**
>
> **更新、2026-08-29:** 次の実際の公開で自動マージは実際に試され、すんなりとは通りませんでした。[Issue #219](https://github.com/oharu121/oharu-tech-blog/issues/219)で`CONFIRMED`の正確性に関する指摘が見つかりました。手作業で解いたSQLの例に含まれていた、二つの誤った数値です。当時のパイプラインはそこで止まり、追跡用のIssueを人間向けに開いたままにしました。設計どおりの動きです。それはその時点では正しい判断でしたが、同時に、上で述べた主張が実際の指摘を通してはまだ検証されていなかったことも意味していました。ルーチンはその後変更されています。`CONFIRMED`の指摘は今では自動的に修正され、再検証されます。修正が二回のラウンドを経ても残っていれば、ルーチンはそれでもマージし、まだ残っている点を書いたフォローアップのIssueを開きます。不完全さを抱えた記事が`main`に届くことは、今では受け入れられた代償であり、人待ちで止まったままの実行ではありません。
