コードを書かないSE日誌

SE歴26年。コードを書かずにClaude Codeで8体のAIエージェントチームを動かしています。AIエージェント・自動化・GTDの実験日誌。

Claude Code 憲法続編:16日後、私が書いた法律に、私が殴られていた

3週間前——正確には16日前——、私は「Claude Code の憲法を書いたら、1日で法律になった話」という記事を公開した。AIエージェント組織に4つの原則(提案権・整合性チェック・承認権・上申経路)を書いたら、その日のうちに5つの trigger と是正措置が加わって「法律」として動き始めた、という内容だ。

この記事はその続編だ。 16日の間に trigger リストは5項目から7項目に拡張され、確認形式はほぼ全項目が改訂された。logs/governance-incidents/ には元記事公開(4月29日)以降だけで16件の事故記録が積み上がった。4つの権利(憲法本文)は不変だが、運用を規定する「法律」——trigger リスト——は現実によって何度も書き換えられた。書き換えたのは、その法律を書いた本人だ。

私はそれを少し誇らしく書いた。「法律を書いたら、書いた人の主張どおりに組織が動く」という物語として。

その16日後、私は同じ法律を何度も改正させられていた。

念のため補足しておく。4つの権利(提案権・整合性チェック・承認権・上申経路)という「憲法本文」は変わっていない。改正されたのは trigger リスト——元記事でいう「施行規則」——のほうだ。法律の骨格は保たれたまま、運用を規定する部分が現実によって何度も書き換えられた。

logs/governance-incidents/ というディレクトリには、4月23日以降の事故記録が18ファイル並んでいる。元記事公開(4月29日)以降に限っても16件だ。そのうち何件かは、私が書いた法律の trigger リスト自体を改正させた事故だった。

3週間前の私は、法律を「書く側」だと思っていた。だが実際には、書いた直後から私は「改正される側」になっていた。


Claude Code trigger リストの定義(元記事の振り返り)

元記事の核は、こうだった。

「原則だけでは止まらない。trigger がないと auto mode では発火しない」

そして、5つの trigger を CLAUDE.md(AIエージェントへの指示書)に書き加えた。

  • デフォルト値変更で挙動を on/off する
  • 不特定多数を触る一括操作
  • 自動実行の登録
  • 安全装置バイパス
  • 連続5コミット超え

これで「auto mode でも法律が機能する」と書いた。

書いた直後の私は、5項目で十分カバーできていると思っていた。法律の構造(憲法・施行規則・是正措置・前文)に当てはめれば、骨格は揃っているように見えたからだ。

その認識は、半月で覆された。


改正1: 「pull は安全」と思っていた

元記事公開から5日後、5月4日。

私は、過去に書いたエージェント組織論シリーズ9本にカテゴリを一括追加しようとして、blogsync push を直接実行した。blogsync は、はてなブログのローカルファイルを本番に同期するためのCLIだ。既存スクリプト hatena-bulk-update.sh が本文diff前提で使えなかったので、blogsync を直接叩くことにした。

結果から書く。4記事のカスタムURLが書き換わり、元URLが404になった

blogsync.yaml の設定で local_root: entry という相対パスが指定されていて、blogsync は実行時のカレントディレクトリ(CWD)に依存する挙動をする。私はそれを知らずに、引数のパス解釈が二重化することに気づかないまま entry/saitoko.hatenablog.com/entry/... という二重パスを本番に PUT してしまった。

はてなブックマーク、Google検索結果、SNSシェアリンク——元URLでアクセスしていたすべてが404になった。

このとき、私は trigger リストを改正した。「既存スクリプトを経由しない外部CLI直接実行」を追加した。元記事に書いた5項目に、6項目目が増えた。

ここまでは、まだ「想定外の事故型を一つ追加した」だけで済む話だった。

問題は、5月14日に起きた。

私は今度は blogsync pull——つまり本番からローカルに引っ張ってくる読み取り操作を、独断で実行した。「pull は読み取りだから副作用なし」と判断したのだ。それも、CLAUDE.md に明記された「Bash ツールでは cd を原則禁止」というルールを、cd hatena-content && ./bin/blogsync pull という1行で破った。

cd を Bash 内で使うと、その作業ディレクトリが次のコマンドにも継続する。それと相対パス依存の blogsync が組み合わさって、ローカルファイルが二重パスに renamed された。さらに夕方、別の記事を公開するために post.sh を実行したとき、その二重パスがそのまま本番URLを書き換えた。朝公開したばかりの記事のURLが、夕方に404になった

復旧には、はてなの管理画面を私が手動で操作する必要があった。ローカルのCLIからは戻せない領域だった。

trigger リストはこのとき、また改正された。「blogsync 直接実行(pull/push 問わず)」が独立項目として追加された。元記事公開時点では「pull は安全」と書きすらしなかったものに、明示的な禁則がついた。

5月4日に書き換えた trigger リストは、10日後にもう一度書き換えなければいけなかった。私は事故型を見落としていたのではない。「pull は読み取り操作だから副作用なし」という認識自体が、間違っていた。

書いた trigger リストを、書いた本人が書き直すことになっていた。


改正2: 「dry-run で件数が合った」では足りなかった

5月11日。今度はネタ帳の整合性を直す内部スクリプトの話だ。

update-spawned-slugs.sh という、ネタ帳ファイルの YAML ヘッダに「派生した記事のslug」を一括追記するスクリプトを skill-dev エージェントが書いた。私はそれを「不特定多数を触る一括操作」trigger として正しく承認手続きにかけた。dry-run(実書き込みなしの試行)で件数を確認し、対象26ファイルが想定どおりだった。

本実行を承認した。

26ファイルが、空ファイルになった。

スクリプトの awk -v content="$yaml_block" という1行が原因だった。$yaml_block には改行が含まれていて、macOS 標準の BSD awk はそれを "newline in string" 警告で処理中断する。dry-run では実書き込みをしないので、件数は合っていた。書き込み内容が壊れていることは、件数からは見えなかった

幸い未コミット状態だったので git checkout で復旧できた。被害は実質ゼロで済んだ。

だがこの事故が暴いたのは、私が元記事で書いた「不特定多数を触る一括操作」trigger の確認形式が、不十分だったということだ。元記事ではこう書いていた。

確認形式: 対象件数・差分サマリー・rollback手段を明示する。

「対象件数」を確認するだけでは、awk のバグも sed のバグも検出できなかった。

trigger リストはまた改正された。「書き込み系スクリプトはさらに『1ファイル実書き込みサンドボックステスト結果(before/after diff サンプル)』の提示を必須とする」という一文が追加された。

これは元記事の「対象件数を明示」の精度を一段上げる改正だ。元記事を書いた時点の私は、「件数で確認」で十分守れると思っていた。実際にはそれでは守れなかった。


改正3: article-status.md(集計ビュー)を書き込みの根拠にしてはいけない

5月14日、同じ日のもう一つの事故。

私はユーザーから「Qiita にクロスポスト1本」の依頼を受けて、content/article-status.md という集計ビューを開いた。そこには「Qiita: 未」と表示されていた。よし、これをクロスポストしようと、crosspost.sh --qiita --draft を実行した。

スクリプトのログには「Qiita: 更新」と出た。更新。新規投稿ではなかった。

その記事は、2日前にすでに Qiita に投稿済みだった。私が集計ビューを開いた時点で、ビューの再生成が追いついていなかった。crosspost.sh--draft オプションは「新規投稿を下書き状態で送る」設計だが、投稿済み記事に対して実行すると Qiita 上の記事を private: true(非公開)で上書きしてしまう。つまり --draft が非公開化を意味する。--draft 指定で実行したことで、Qiita 上の公開記事が一瞬 private(非公開)状態に切り替わった。

すぐに --publish で復旧したが、その数秒〜数分のあいだ、Qiita の検索インデックスからその記事は消えていた可能性がある。

このとき私が最も怖かったのは、被害の大きさではなかった。私が設計の前提として書いたことを、私が運用フェーズで自分で壊していた——そのことへの恐怖だった。SSoT は frontmatter だと自分で書いた。集計ビューは二次情報だと自分で書いた。それを運用初日から無視していた。

このとき trigger リストに追加されたのは「集計ビューの表示を根拠とした書き込み・更新操作」だった。

そして、これがいちばん効いた。

元記事を書いた時点の私は、「集計ビューを信じる」という発想自体を疑っていなかった。SSoT(Single Source of Truth)は frontmatter であり、集計ビューはそこから生成される二次情報だ。それは元記事で書いた article-status.md 再設計の前提だった。

なのに、私自身がその前提を破った。frontmatter を直接見ずに、集計ビューだけで判断した。私が書いた設計を、私が運用フェーズで破った

trigger リストには、これに加えて「書き込み系スクリプトを実行する前に、対象記事の frontmatter (platforms.*.status) を grep / Read で直接確認する」という具体的な手順が追記された。集計ビューを禁止するのではなく、集計ビューを信じない手順を書いた。

これも、私が法律(trigger リスト)に改正を迫られた一発だった。


16日で trigger が5項目から7項目に増えた経緯

元記事を書いた4月29日時点で、CLAUDE.md の trigger リストは5項目だった。

今、同じファイルを開くと、項目は7つに増えている。確認形式の追記まで含めると、ほぼ全項目が改訂されている。

元記事時点(5項目) 現在(7項目+確認形式の精緻化)
デフォルト値変更 (維持)
不特定多数を触る一括操作 「書き込み系スクリプト本実行のサンドボックステスト」を追加
自動実行の登録 (維持)
安全装置バイパス (維持)
連続5コミット超え (維持)
既存スクリプトを経由しない外部CLI直接実行(5/4 blogsync push 事故で追加)
blogsync 直接実行(pull/push 問わず)(5/14 pull 事故で追加)

それから coo-rules.md(COOエージェント専用の行動規範)にもチェックポイントが3つ増えた。「集計ビューを信じる前に SSoT を直接確認する」も、そこに書かれた。

これだけ追記されると、もう「元記事の主張」と現在の運用は別物だ。

元記事は「憲法を書いたら法律になった」と書いた。それは1日の話としては正しい。だが16日のスケールで見ると、その法律は16件の事故によって何度も書き換えられた。書き換えたのは、その法律を書いた本人だ。


Claude Code auto mode で発火しなかった事故型とは何か

元記事の主張を、もう一度引用する。

「原則だけでは止まらない。trigger がないと auto mode では発火しない」

この主張は、半分は正しかった。trigger がないと止まらない、というのはその通りだった。

ただし、片面的だった。

trigger を書いた瞬間に、その trigger は不完全だった。書いた時点の私が想定していた事故型しか書けないからだ。私は5月4日の blogsync push 事故を、4月23日には想像できていなかった。「pull は安全」と無自覚に信じていた5月13日の私は、5月14日の事故を予見できなかった。

元記事は「法律が誕生した瞬間」を書いた。今書くとすれば、こうなる。

「法律は誕生した瞬間から、その法律で守れない事故に殴られ続ける」

これは敗北宣言ではない。むしろ、法律を書く側として真っ当な認識だ。

trigger リストは完成品ではなく、私が現実に踏まれて改正される生き物だった。改正の頻度は、運用の精度の証拠ではなく、運用してみないと見えなかった事故型の証拠だ。


3週間前の私への、反論ではない応答

私は元記事を取り下げたいわけではない。あの記事は、その日に起きたことについては正確に書いた。

ただ、あの記事には書き残したことがあった。「翌日: 法律は最初から完成しない」という節を書いてはいた。だが、そこに書いたのは「翌日にも事故が起きた」という一例だけだった。私はその時点で「事故型は今後も増える」とは予感していたが、それが16日で trigger リストの新規追加2件・確認形式の記述強化、さらに coo-rules.md の各チェックポイントへ箇条書きが計7件追加——10件規模の改正になるとは想像していなかった。

法律(trigger リスト)に改正を迫られる回数は、書いた瞬間にはわからない。

そして改正を迫られたあと、私は毎回、私が書いた法律(trigger リスト)に項目を足してきた。減らしてはいない。

これが、3週間前の私への、反論ではない応答だ。法律を書いた人間は、法律を改正する仕事を放棄できない。書いた瞬間に「これで終わり」と思った気持ちごと、改正される。


結末を書かない

ここに気の利いた教訓を置きたい誘惑がある。「だから AIガバナンスは継続的に……」とか、「改正サイクルを設計すれば……」とか。

書かない。

書いてしまうと、また半月後の私が、その教訓に追い越されて改正することになる。それは確実に起きる。元記事で書いた5項目が16日で7項目になったのと同じ理由で、ここに今書ける教訓も、たぶん半月持たない。

書ける範囲で正直に言うと、3週間前の私の主張は片面的だった。「憲法を書いたら法律になる」は1日の話。「法律は書いた本人が改正させられる」が16日の話。どちらも本当のことで、どちらか片方では足りない。

私は今もエージェント組織を運用している。CLAUDE.md は今日もまた1行増える可能性がある。logs/governance-incidents/ ディレクトリにも、近いうちに19件目のファイルが追加される予感がある。

それを「成熟」と呼ぶか「未完成」と呼ぶか、まだ決めかねている。


この記事の続きを本で読む

本記事で触れた法律改正サイクル(trigger リストの更新)・事故対応・ガバナンスの育て方は、Zenn Book に体系的に収録している。

コードを書けない私がClaude Codeで「AIチーム」を作るまで(¥900 / 序章・第1部無料)

SE歴26年、Claude Code で「AIエージェント編集部」を作った実践記録。マルチエージェント組織の設計・ガバナンス・失敗リカバリを体系的に知りたい人向け。


関連書籍

AIエージェントのガバナンス設計を考えるうえで、以下も参考になる。