人の手を減らしつつ、ゲートは残す — cc-bridge運用1か月、ツール16個・ジョブ104本の先で見えた本当のボトルネック【4/4】

cc-bridge構築記の最終回です。前回までで、コードのリポジトリはworktreeで、非コードの作業は直接モードで回る形になりました。今回は10月後半、「もっと人の手を減らせるところはどこか」を探して足したものと、足さなかったものの話です。

1. 数字から

発案から1か月(9/25〜10/26)の時点で、cc-bridge自身のジョブは104本、ツールは16個、決裁メモは78件、テストは567件になっていました。ツールの内訳を並べると、この1か月で何を足したかがそのまま分かります。

  • 最初の4つ: dispatch status report reply
  • 初日に足した3つ: jobs abort merge
  • 往復削減で足した2つ: repos notes(読む側)
  • 10月に足した7つ: decide push launch procs procs_stop close sync_commands

16個のうち、claude.ai側で確認のダイアログが出るのは launch procs procs_stop の3つだけです。最初は唯一の人間のゲートだった merge も、今は常時許可にしています。しばらく使ってみて、Claude Codeは慎重に実装してくれるので、戻したくなったことが一度もなかった。仮におかしくなっても、指示して直させる方が私には安い。毎回マージを承認する手間の方が、はるかに大きいと判断しました。人のゲートは、ダイアログではなく、私がチャットで「マージして」と言うか、Web画面のボタンを押す一言に残っています。

cc-bridgeのツールごとの許可設定(claude.ai)

2. 壁打ちの結論が、索引に乗らない

10月中旬、チャット側のモデル(Fable)のトークン消費が週の上限に当たるようになりました。相談の条件は「タスクの難しさでモデルを選ぶような、私の側の判断が増える対策は避けたい」。

一番効く策は「チャットの既定モデルをOpusにしてFableは壁打ちに限る」で、これは今も未決のままです。記事にしたいのはその次に出た話で、私が「全部のログと議論をチャットから検索して引き出せるようにするのが構築の目的だが、この観点で支障はあるか」と聞いたところから始まります。

答えは「支障はなく、むしろ良くなる」。保管経路で一番弱いのはclaude.aiの会話で、当時は月次のエクスポートでしか索引に乗らない。Claude Codeのログやリポジトリ内のSTATUS/DECISIONSは日次で索引化される。レビューと返信をMac Studio側へ移すほど、議論の記録が弱い経路から強い経路へ動く。

そのうえで指摘された穴が「壁打ちの結論」でした。チャットで「こう決めた」が出ても、それはチャット内とチャットの記憶にしか残らない。チャットの記憶は方針しか持たない約束(第2回)なので、2週間後に別のチャットから「あの件をどう決めたか」を探すと、どこにも索引が無い空白ができる。

対策は decide ツールです。Claude Codeを起動せず、cc-bridgeのサーバーが指定リポジトリの決裁メモ(docs/decisions/YYYYMMDD-題.md)を追記してコミットするだけ。数秒で終わり、ジョブの費用も発生しない。既にある notes(読む側)の書く版です。

実装のジョブは1つ判断待ちで止まりました。「置き場を decisions/(ブリーフどおり)のままにするか、既存の規約に合わせて docs/decisions/ にするか」。ブリーフで別の置き場を指定したのはチャット側の見落としで、推奨どおり既存の規約に揃えました。1件目の決裁メモは、この日の「Fable消費対策と壁打ち結論の即日索引化」そのものです。

運用上の区別も決めました。Todoistに書くのは「やること」、決裁メモに書くのは「決めたこと」、ジョブが残すDECISIONS.mdは「実装中の判断」、decideで書くのは「着手前の方針判断」。

3. 本番が10日止まっていた

10月17日、別の監視ツールのリポジトリで、マージしてもoriginにpushされておらず、本番機で git pull しても新しいコードが届かない状態が10日続いていたことが分かりました。「マージした」と「本番に出た」の間が私の記憶頼みで、配備が要る変更はたまにしか来ないので、毎回その手順を忘れる。

仕組みで塞ぐことにしました。リポジトリごとの設定で、マージ後にサーバーが git push origin main し、指定したものは配備コマンドまで流す。originが先に進んでいればforceせず「push失敗」として記録し、マージは取り消さない。ジョブ(worktree)からのpush禁止はそのまま残す。止めたいのは未レビューのpushで、承認を経たマージに続くpushを止める理由は無いからです。

棚卸しで分かったことが2つ。1つは、cc-bridge自身を含む4つのリポジトリにoriginが無く、Mac Studioのディスクにしか存在していなかったこと。仕組みの中核がバックアップなしでした。GitHubにprivateリポジトリを作って最初のpushは手で行い、以後はマージ後の自動pushに乗せました。もう1つは小話で、「マージ後にpushする」設定を足したジョブ自身のマージではpushされない。マージはconfigを取り込む前に読むからで、次のマージから効きます。

直接モードのリポジトリにはマージが無いので、別に push ツールを足しました。本体で git pull --rebase してからpushし、衝突したらabortして何もpushしない。

4. 長い処理をジョブの外に出す

第3回で「1コマンド60分まで」にしたので、音声処理の全長はジョブの中で済むようになりました。それでも足りないものがありました。動画のレンダリングのように1時間を超える処理と、「起動して結果を待つだけ」なのにClaude Codeのセッションを占有し続けるのがもったいない処理です。

launch は、config.toml に登録した処理だけを、Claude Codeのジョブの外で切り離して起動します。シェルは通さず、同じリポジトリで別の処理が動いていれば断る。終わったらDiscordに終了コードとログの末尾を通知し、様子は procs、止めるのは procs_stop。第3回の設計で後回しにした「C. 長時間処理」の答えは、結局「Claude Codeを待たせない」形になりました。

もう1つ、close。判断待ちで止まったジョブに「もうこの判断は要らない、閉じる」と返すだけのために reply でClaude Codeを起動すると、時間も費用もかかる。close は報告の先頭に「PMが閉じた(日時): 理由」の1行を足してコミットし、doneにするだけです。コミットの無い読み取り専用のジョブ(調査など)が一覧で「マージ待ち」と出続ける問題も、同じ頃に「変更なし」と表示してマージボタンを出さない形で片付けました。

5. 足さなかったもの

足したものより、足さなかったものの方が、この仕組みの性格を決めていると思います。

完了時の自動レビュー(フェーズC)。第2回で「数週間回してから」とした、Sonnetに報告と差分を読ませて推奨を通知に載せる案は、1か月経っても作っていません。理由は、報告を読んで判断する往復が、Web画面のマージボタンと返信欄でほぼ消えたからです。推奨が通知に載っても、押すのは私で、押す前に報告は読む。読むならレビューの価値は薄い。「監督役による自動マージ」も作っていません。マージの判断は私の一言のままで、そこを機械に渡す理由は今のところありません。

ランナー側の自動コミット。第3回で書いたとおり、AIが「終わった」と言った状態をそのまま取り込む仕組みは作らず、取り込めない状態を取り込めないままにする方を選びました。

OSのサンドボックス。任意コード実行を受け入れた以上、境界を作る調査はやらない。

Hermes統合。第1回で「安定してから」とした話は、1か月経っても着手していません。Discordから /job で投げられると便利ですが、入口を増やすより、claude.ai側で文脈を持ったまま回す方が私には合っていました。通知はDiscordに来るし、判断はTailscale内のWeb画面でできるので、スマホからの操作はもう足りています。

画像をチャットに返すツール。ジョブが作った画像をチャット側のClaudeに見せたい場面がありますが、Web画面はTailscale内なのでチャットからは見えない。ジョブが画像をObsidianの保管庫に写し、それがNAS経由でMacBook Airに同期され、Airと連携したチャットから読む、という経路が既にあるので、cc-bridgeに画像を返すツールは作らない、と決めました。

6. 見張り

運用1か月で、壊れ方も分かってきました。Claude Codeの認証期限切れ(第3回)、コネクタのOAuthの状態ファイルが壊れる可能性、pushの取りこぼし。最後に足したのは、コネクタの認証の見張りです。claude.aiからのトークン更新が4xxで失敗したら即Discordに通知し、回復したら1回通知する。「最後の更新からN時間空いたら通知」という案は捨てました。claude.aiは使ったときにしかトークンを更新しないので、空白は利用の空白と区別できず、夜間や休日に誤報になる。同じ方式を先に自前の検索コネクタ(docs-search)で3か月動かして、誤報0・障害2回とも発生と同時に鳴った実績があったので、そのまま持ってきました。

7. 本当のボトルネックはどこへ行ったか

第1回で「ボトルネックはコピペではなく往復だった」と書きました。1か月経って、往復は確かに減りました。ブリーフを書くのはチャット、実装はジョブ、報告を読んで返信・マージするのはWeb画面、決めたことはdecideで即日リポジトリへ。私がやるのは相談と承認だけ、という最初の目標に近い形です。

そして次のボトルネックは、トークンでした。チャット側のモデルの消費が週の上限に当たる。これは往復が減った結果でもあります。コピペしていた頃は私の手が律速だったので、モデルの消費がそこまで行かなかった。手が外れたら、今度はモデルが律速になった。

対策の候補は2節に書いたとおりで、「既定のモデルを変える」のような一度だけ決めればいい形にするつもりです。毎回判断する形は、私の往復を増やすだけなので採りません。

1か月で分かったことを一つにまとめるなら、自動化で消えたのは「作業」で、残ったのは「判断」でした。判断の回数は減らせても、質を落とす形では減らさない。そのための人のゲートを、マージの1点に絞って残す。最初のブリーフにあった7番の課題のうち、この「位置」は1か月使っても変える必要を感じませんでした。変わったのは「形」で、承認ダイアログから私の一言へと、使いながら軽くしました。

8. 運用ルールとして残すこと

  • 確認ダイアログが出るのは launch procs procs_stop だけ。merge も常時許可にし、人のゲートは私の「マージして」の一言(またはWeb画面のボタン)に置く
  • 壁打ちの結論はその場で decide に書く。Todoistは「やること」、決裁メモは「決めたこと」、ジョブのDECISIONSは「実装中の判断」
  • マージ後のpushと配備はリポジトリごとに設定で有効にする。ジョブからのpushは禁止のまま。設定を足したジョブ自身のマージではまだ効かない
  • originの無いリポジトリを作らない。新しいリポジトリはprivateでもGitHubに置く
  • 長い処理は launch で切り離す。Claude Codeに待たせない
  • 判断が要らなくなった判断待ちは close で閉じる。replyで起こさない
  • 自動レビュー・自動マージ・自動コミットは作らない。おかしくなったら指示して直させる方が、毎回の承認より安い
  • Hermes統合はやらない(入口を増やさない)。画像をチャットに返すツールも作らない(Obsidian経由で足りる)
  • 見張りは「失敗を即通知」。「N時間音沙汰なし」で鳴らさない
  • 次のボトルネック(トークン)への対策は、一度だけ決める形にする。毎回判断する形は採らない

ここまでの4本が、cc-bridgeの1か月分です。この記事自体も、原稿はチャットで書き、下書きの入稿とアイキャッチの生成はcc-bridgeのジョブで行い、公開は私がボタンを押しました。