その日のうちに1周、3日後にはマージボタン — cc-bridgeを回し始めて分かった「往復」の正体【2/4】

cc-bridge構築記の2本目です。前回は発案から設計、MVPのブリーフを出すまでを書きました。今回は動き始めてからの4日間(9/25夜〜9/28)。この4日でcc-bridge自身のジョブ番号は035まで進み、見た目も運用の形もほぼ今の姿になりました。
1. 初めての1周と、ボトルネックの引っ越し
初めてチャットから投げたジョブは「READMEの誤字を1つ直す」でした。Discordに完了通知が来て、チャットが report で報告を読み戻す。ここまでは設計どおり。
ところが同じ夜のうちに、別の往復が目立ち始めました。ジョブはgit worktree(作業用の別コピー)の中で動くので、結果を本体に取り込むには私がターミナルでマージする必要があります。つまり「チャットが出したマージ手順をコピーしてターミナルに貼る」が残っていた。私はこれを「ボトルネックがClaude Codeの出力のコピペから、ターミナルコマンドのコピペに移っただけ」と言い、チャットも設計の穴だと認めました。
そこで追加したのが merge ツールです。テストを流してからmainに取り込み、cc-bridge自身を取り込んだときはサービスを自動で再起動する。claude.ai側で「承認が必要」に設定した、唯一の人間のゲートです。
同じ夜に、動いているジョブを一覧する jobs と、止める abort も足しました。ツールは7つになり、権限は「merge だけ承認、残りは常時許可」に変えました。前回書いた「人間のゲートはマージの1点」が、この時点で実際の設定になっています。
正直に書いておくと、この夜の終わりに「往復はまだ減っていない」とも言いました。チャットの答えは「MVPの試運転中は当然で、本当の評価は実プロジェクトで回してから」。これはそのとおりで、減り始めたのは3日目からでした。
2. 途中経過が見えない
次の懸念は、ヘッドレスで動くClaude Codeの途中経過が見えないことでした。ターミナルなら何を考えて何を実行しているか流れていくのに、cc-bridgeでは終わるまで黙っている。失敗したときに何が起きたのか追えない。
対策として、Tailscale(自宅のVPN)の中だけで見られるWeb画面を作りました。/jobs に一覧、/jobs/<id> にそのジョブの経過がストリーミングで流れる。公開側のURLからは同じパスを叩いても404を返します。最初は素っ気ない画面でしたが、ダークモード、日本時間のタイムスタンプ、「最終更新 N秒前」、経過のMarkdown表示とツール呼び出しのカード化、スマホ幅ではカード表示、と数ジョブで整えました。


この一覧画面は、後で私の「主画面」になります。Discordの通知を見て判断するつもりで設計したのに、実際には一覧を開いて判断していた。これに気づいてからは、一覧の情報量を増やす方向に改修が寄っていきました。
3. 事故集(最初の3日分)
小さい事故がいくつも起きました。どれも仕組みの理解に役立ったので残します。
再起動後の確認が偽失敗する。cc-bridge自身を取り込むと自動再起動し、起き上がったか公開側のURLで確認します。これが403で「失敗」と出た。原因はCloudflareの保護機能が、Pythonの標準ライブラリの既定のUser-Agentを弾いていたことで、認証の失敗ではなかった。UAを明示し、応答ヘッダで「cc-bridgeが返した応答か」を見る形に直しました。
自動再起動がマージを途中で落とす。ジョブAを取り込んで再起動が走っている最中に、ジョブBの取り込みが始まると、再起動がBのマージを殺す。記録にも残らず、何が起きたか分からない状態になりました。原因確定後、再起動はマージのロックが空くのを待つように直しました。複数のジョブが同時に終わったときは、このロック修正を先に取り込んでから他を取り込む、という順番にも気を使うようになりました。
ブリーフの題名。一覧のタイトルはブリーフの最初の # 見出しから取ります。## 目的 で始めると、それが題名になる。ブリーフには必ず # 題名 の行を付ける、というルールはここで決まりました。
コミットせずに終わる。あるジョブでClaude Codeがコミットせずに終了し、merge が「既に取り込まれています」と断りました。嘘ではないが誤解を招く。断り文句の分岐を直し、コミット漏れは完了通知に出すようにしました(この問題は後でもう一度、もっと大きく起きます。第3回で)。
崩れたときの戻し方。Web画面の見た目はプレビュー機能を作らず、マージして本番で見る方針にしました。崩れたら直前のマージを戻す。
cd ~/cc-bridge && git revert -m 1 --no-edit HEAD && launchctl kickstart -k gui/$(id -u)/com.tada.cc-bridge
この1行はチャット側に「覚えておく」約束にしてあります。
4. モデルを選べるようにしたら費用が1/6になった
ジョブのモデルは当初、全部Claude Codeの既定(Opus)でした。READMEに追記するだけのジョブにOpusは贅沢なので、dispatch の引数でモデルを選べるようにしました。試しにSonnetでREADMEのジョブを流したら、費用は$0.18。Opusのジョブは$1〜1.7だったので、1/6〜1/9です。
運用は「既定はOpusのまま、READMEの追記や調査だけのような軽いジョブはdispatch時にSonnetを指定する」に落ち着きました。一覧にもモデル名を出すようにしています。
5. 「終わりました」「マージして」の往復
3日目に、一番大きな構造の問題を言葉にしました。ジョブが終わるたびに、私がチャットに「終わりました」と伝え、チャットが報告を読んで「マージ可」と言い、私が「マージして」と言う。この3往復が毎ジョブに挟まる。
チャットの診断は「私(チャット)は呼ばれないと動けない。ジョブが止まるたびに、あなたが私を起こす往復が必ず挟まる。これは構造的で、振る舞いでは消えない」。
そこで、チャットが1周でやっている仕事を3つに分けました。
- ブリーフを書く(私と相談して仕様に翻訳する)
- 報告を読んで「マージ可 / 判断が要る」を見立てる
- 承認を受けて
mergeを叩く、判断待ちならreplyを送る
1はチャットに残す。記憶と横断的な文脈が要るのはここだけだから。2と3をMac Studio側に移す。順番はこう決めました。
- A. 読み取りツール:
repos(登録済みリポジトリ一覧)とnotes(任意のリポジトリのSTATUS.md・DECISIONS.md・README・決裁メモ・git logを返す)。チャットがジョブの話を始めるとき、まずjobsと一緒にこれを読めば「前回何が決まったか」を毎回取り戻せる - B. Web画面にマージボタンと返信欄: Discordの通知からリンクを踏んで押すだけ。効果が確実で、AIの質に依存しない
- C. 完了時の自動レビュー: Claude Code(Sonnet)に報告と差分を読ませ、「推奨: マージ可 / 保留(理由)」を通知に載せる。数週間回して推奨が当たっていると感じたら、監督役による自動マージを考える
Cまで行くかは今決めない、で合意しました(そして今も行っていません。第4回で理由を書きます)。
6. 「覚えていてほしい」と言ったら
Aの話をしているとき、私はこう言いました。「記録がリポジトリに残るのは分かったけど、あなたに覚えていてほしいんだよね」。
返事は率直でした。「私が見ていないものは覚えられません。往復を減らせば減らすほど、私の目に触れない判断が増えるのは避けられない構造です」。
ただし、「覚えていてほしい」の中身を分けると、ほとんど失われない、と続きました。
- 方針レベルの判断(判断待ちは原文を出して待つ、軽いジョブはSonnet、cc-bridge自身は差分を見てから承認)は、私とチャットの会話で決まる。ボタン化しても、方針を変える話はチャットでするので、ここは残る
- ジョブ単位の履歴(023で何を入れたか、019のマージがなぜ記録を欠いたか)は、そもそもチャットの記憶に置くべきものではない。数十本たまれば薄れるし、正確さでリポジトリに勝てない。「覚えている」ではなく「見に行ける」にするのが正しい。それが
notesツール - 残る穴は、私がWeb画面のボタンや返信欄で下した判断のうち、方針に関わるもの。これはチャットに見えないので、そういう判断をしたときだけ、次にチャットを開いたときに一言言う
「覚えるのは方針、見に行くのは履歴」。この役割分担は、以降の設計全部の前提になっています。
7. 新しいツールが出てこない
AのツールとBのボタンは翌日(9/28)までに取り込みました。Bの実機確認は問題なく、以後は報告を読んで返信・マージまで画面で済むようになりました。

一方、Aの repos / notes は、取り込んだのにチャットのツール一覧に出てこない。新しいチャットを開いても7ツールのまま。チャットの調べでは、claude.aiがコネクタのツール一覧をURL文字列ごとにキャッシュする既知の不具合が濃厚で、回避策は「同じURLに ?v=日付 を付けて再登録(権限設定はやり直し)」か「数時間〜1日待つ」。サーバー側で本当にツールが載っているかを確かめるジョブも投げましたが、ジョブの権限ではログが読めず確認できないまま閉じました。権限を広げてまで確かめる価値はないと判断したからです。
私は再登録の手間を避けて待つ方を選び、翌朝には何もしていないのに9ツールが並んでいました。教訓は「cc-bridgeにツールを増やしたら、claude.ai側に出るまで半日ほど待てばよく、再登録は要らない」。この後ツールは16まで増えますが、毎回この待ち時間はありました。
8. 4日で決まった運用の形
9/28の夜の時点で、使い方はほぼ今の形になっていました。
- Web画面からの初めてのマージが成功し、「取り込み→再起動OK」まで画面で確認できた
- 一覧に「取り込み済み / マージ待ち」と最終更新時刻が出るようになり、Discordの通知は補助になった
- 判断待ちで止まったジョブが返信後に
doneに戻らない不具合、マージボタンの位置(上部から返信欄の横へ)など、使ってみて初めて分かる細部を小さいジョブで直した
この4日で分かったことを一言にすると、「往復を減らす」とは、チャットを賢くすることではなく、チャットを通らなくていい判断を外に出すことでした。
9. 運用ルールとして残すこと
- 人間のゲートはmergeだけ。dispatch / reply / abort / jobs / status / report は常時許可
- ブリーフには必ず
# 題名の行を付ける - 軽いジョブ(README追記・調査だけ)はdispatch時にSonnetを指定。既定はOpus
- 並行するジョブは触るファイルを分けて投げる。STATUS.md / DECISIONS.md は末尾追記のみ
- 1つのジョブは、投げたチャットが最後まで担当する。別のチャットから同じジョブにreplyしない
- 判断待ちで止まったら、チャットは要判断事項の原文と推奨を出して私の返事を待つ。勝手に決めてreplyしない
- cc-bridge自身と本番稼働中のリポジトリは、差分を見てからマージを承認する
- チャットが覚えるのは方針、ジョブ単位の履歴はリポジトリに残して
notesで見に行く。Web画面で方針に関わる判断をしたら、次にチャットを開いたときに一言伝える - ツールを増やしたらclaude.ai側に出るまで半日待つ。再登録はしない
- 見た目の崩れはマージして本番で見る。崩れたら直前のマージをrevertして再起動
次回は、コードではない仕事(楽曲制作・音声処理)をこの仕組みに通そうとして、worktreeという前提そのものを見直した話です。