個人で運営しているブログで、AIエージェントを10体並べて記事の企画・執筆・校閲・デプロイを回している。うまく回っている前提で紹介したい話ではない。校閲を通っていない記事が、そのまま本番に出た。しかも1回の事故で終わらず、直すのに50分かかった。この記事はその再現と、なぜ起きたかと、何を直したかを書く。
この構成に至るまでの考え方はClaude Code のコンテキスト設計に書いた。自分で運営しているブログなので、あらかじめ断っておく。
何をやっているか
ブログ運営のタスクを役割ごとに10体のエージェントに分けている。
-
interviewer— 一次情報を聞き出す -
writer— 記事を書く -
editor— 校閲する -
pdm— デプロイの意思決定をする -
analyst/designer/distributor/monetizer/sns/techseo— それぞれ分析・デザイン・配信・収益化・SNS運用・SEOを担当
標準のフローは interviewer(素材収集)→ writer(執筆)→ editor(校閲)→ pdm(デプロイ)の順で、校閲を通ってから公開する建てつけにしていた。
問題は、これらのエージェントが同じ Git ワーキングツリーを共有していること。あるセッションが記事Aを校閲待ちで置いている最中に、別のセッションが記事Bのちょっとした修正をコミットしにいく、ということが普通に起きる。
起きたこと
git の履歴から再構成すると、事故は次の順で起きている。
| 時刻 | コミット | 何が起きたか |
|---|---|---|
| 23:14 | fix: Xserver VPS の36ヶ月価格が1ヶ月契約の価格になっていた |
価格表記の修正のつもりで git add . を実行し、別セッションが校閲中だった手取りシミュレーション記事の全面改稿を巻き込んで公開
|
| 23:30 | fix: 手取りシミュレーションの数字が記事内で矛盾していた(本番に出ていた) |
巻き込まれた記事の数字が矛盾していると発覚 |
| 23:35 | fix: 記事内の「70万円」と「57万円」の違いを冒頭で明示する |
修正後も残る2つの数字(手取り70万円/積立を引いた生活費57万円)を、読者が混同しないよう冒頭で説明 |
| 23:54 | fix: 社会保険料を取材で確認した実額に更新し、全表を再計算 |
社会保険料を実額に更新し、全表を再計算 |
| 00:04 | fix: make p の git add . を廃止し... |
git add . を廃止。ファイルを明示しないとコミットできないようにした |
最初のコミットメッセージは Xserver VPS の価格修正について書いているだけだ。実際の差分を見ると、フリーランスの手取りシミュレーション記事が65行にわたって全面的に書き換えられて同梱されていた。税別表記から税込表記への変更、インボイス未登録の経緯の追記、2割特例の説明の追加。Xserver の価格とはまったく関係のない変更だ。
これは「Aの作業をコミットしたつもりが、Bの作業もステージに乗っていて、そのまま git commit してしまった」という、Git を使う人なら一度は踏む古典的な事故のパターンでしかない。ただし今回は、Bを書いていたのが自分ではなく別のAIエージェントのセッションだったという点が違う。人間なら「あれ、これ自分の変更じゃないな」と気づく余地があるが、git add . を実行するエージェントは自分がコミットしている差分の中身を毎回精査しているわけではない。
本番に出た中身がどう壊れていたか
厄介なのは、公開されてしまった記事の数字が、ぱっと見では気づきにくい壊れ方をしていたことだ。
同じ記事の中に、次の2つの数字が併存していた。
- 年間表:「実際に使えるお金 約562万円/年(月約47万円)」
- 月次まとめ:「月約57万円」
同じ「実際に使えるお金」が、同じ記事の中で月47万円と月57万円の2通りになっていた。
さらに、同じ年間表にある「手取り概算(積立前)約886万円/年」は、その表に印字されている行を上から順に引き算しても出てこない数字だった。実際に引くと736万円にしかならない。表に載っている数字だけを追っても、表の結論にたどり着けない状態になっていた。
原因は2つ重なっていた。
1つ目は、契約形態を税別から税込に直した際の更新漏れ。「消費税収入 +144万円」という行を削除したのは正しい判断だったが、その削除が手取りの数字に反映されていなかった。本来はこの行を消すことで調整幅が -98 になるはずが、他の項目の差分だけを足してしまい +46 になっていた。
2つ目は、表の作りそのものの構造的な欠陥。iDeCo・小規模企業共済(合計144万円)を、課税所得を計算する過程で一度控除として引いていた。それに加えて、表の下の方で「うち積立 ▲144万円」ともう一度引いていた。同じ144万円が二重に引かれていた。
根本原因は、1つの表の中で「税額を計算するための控除」と「実際に口座から出ていくお金」を、同じ縦の引き算として扱っていたことだ。基礎控除や青色申告特別控除は税額を減らすだけの数字で、実際に現金が出ていくわけではない。一方iDeCoや共済は税務上も控除だが、実際にお金も出ていく。この2つの性質が違う項目を1本の表に混ぜて積み上げていたので、どちらかを更新するたびに全体の整合性が崩れる構造になっていた。
検算はこうなる。
1440(税込売上) - 88(経費) - 106(国保) - 20(国民年金)
- 100(所得税) - 95(住民税) - 26(消費税) - 144(iDeCo等) - 180(NISA等) = 681
681 / 12 ≒ 56.75万円 → 月次表の「57万円」と一致
修正後は、税額を出すための表(表A)と、実際のキャッシュフローを積み上げる表(表B)を分けた。控除の性質が違うものを同じ引き算に混ぜないようにしただけで、更新のたびにズレる構造そのものをなくした。
「置き場所で守る」をやめた
事故のあと、まず考えたのは「校閲が終わるまで未コミットのまま置いておけばいい」という対策だった。しかしこれは対策になっていない。複数のセッションが同じワーキングツリーを共有している以上、未コミットのファイルも、別ディレクトリに退避したファイルも、他のセッションから触れる。今回の事故も、まさに「校閲中のファイルが未コミットで置かれていた」状態で起きている。置き場所という前提そのものが、この構成では安全を保証しない。
そこで打った手は2つ。
1. git add . を禁止し、ファイルを明示しないとコミットできなくする
.PHONY: p deploy
# 変更をコミットしてプッシュする。
# **git add . は使わない。** 複数セッションが同じワーキングツリーを共有しており、
# 他セッションが校閲待ちで置いているファイルを巻き込んで公開してしまう事故が起きた。
# ファイルを明示して渡すこと。
#
# make p F="content/posts/money/foo.mdx util/affiliate.ts" M="記事を追加"
p:
@test -n "$(F)" || (echo "F= にファイルを明示してください(例: make p F=\"content/posts/money/foo.mdx\" M=\"メッセージ\")"; exit 1)
git add $(F)
git commit -m "$(or $(M),記事を更新)"
git push origin main
deploy:
npx vercel --prod --yes
F= が空ならコマンドそのものが失敗する。これで「ついでに . を付けて全部拾う」という選択肢自体をなくした。
2. 記事自身に鍵をかける
ただ、これだけでは足りないと考えた。F= を強制しても、明示的に指定したファイルの中にたまたま未校閲の記事が混ざっていれば結局同じ事故になる。運用ルールで縛るのは、結局「守る人がミスをしないこと」に依存している。
そこで、置き場所ではなくファイル自身が公開を拒否する形にした。記事のfrontmatterに draft フラグを持たせ、ビルド側の2箇所でこれを弾く。
// 一覧から除外
if (data.draft) continue
// ...
// ビルドから除外
if (data.draft) return null
運用ルールはこうなる。
-
writerが書き始めた時点でdraft: trueを入れる -
editorの校閲を通った時点で、初めてdraft: falseにする -
draft: falseにすることそのものが「校閲を通過した」という合図になる
これなら、誰かが誤って git add してコミット・デプロイしてしまっても、記事自体が draft: true である限り一覧にもビルドにも出てこない。守る主体を「コミットする人の注意力」から「ファイルのフラグ」に移した、というだけの話だが、複数セッションが同じ状態を共有する環境では、こちらの方が壊れにくい。
学んだこと
-
AIエージェントに
git add .のような「全部拾う」操作を渡すと、他のセッションの未完了作業を巻き込むリスクがある。 人間が1人で使っていれば起きにくい事故だが、複数セッションが並走する構成では現実的に起きる - 「未完成のものを一時的にどこかへ置いておく」という前提は、共有ワーキングツリーでは安全ではない。 場所ではなく、そのファイル自身に「これはまだ公開してはいけない」という状態を持たせる方が確実
- 1つの表・1つの計算に、性質の違う概念(税額計算用の控除と実際の支出)を混ぜると、更新のたびに整合性が壊れる。 これはAIが書いたかどうかに関係なく、表を設計する時点の問題
念のため書いておくと、最初の「Xserver VPS の価格を直すつもりが手取り記事を巻き込んだ」というコミットが意図的だったかどうかまでは分からない。あくまで git の履歴から再構成した事実として書いている。ただ、原因が意図的な誤操作であれ単純な巻き込みであれ、「同じ事故が起きない構造にする」という対策の方向性は変わらない。
同じような構成でAIエージェントに個人開発やブログ運営を任せている人の参考になれば。
Claude Code を既存システムに導入したときにハマった別の問題は、Claude Codeを既存システムに導入すると詰まる「コンテキスト問題」とハーネスエンジニアリングの話にまとめた。コンテキストの分散・Hooks での自動検証・段階的PRフローあたりを扱っている。