柵の抜け道は、柵を作った本人には見えない
7月7日(現地時間)、Anthropicが「Claude Fable 5」の無料枠期間を5日間延長すると発表した。新しい期限は日本時間13日午後3時59分。以前書いた記事で、期限付きの無料枠が終わる前のFable 5に、記事やコードでなく外部脳の運用ルール自体を作らせた話を書いた。まだ使えるうちにと思い、今回はその続きになる。もう一段本格的に、Claude Code環境そのものを段階的に再設計させた。作り終えた後、Ultracode(複数のエージェントを並列で走らせる機能)で「本当に効いているか」を疑わせたら、2週間以上前から使い続けている安全装置と、その日新設したばかりの安全装置の、作られた時期が全然違う両方に、同じ種類の穴が開いていた。ただし、この疑わせ方はタダでは済まなかった。
Fable 5(High)に、環境をPhase分けで作らせた
read-only診断から始めた。今の環境のどこが無駄でどこが手薄か、Fable 5に読ませるだけ読ませて、直接手は入れさせない。使ったのはHigh、Claude Codeで1体のモデルの思考の深さを最大まで上げるモードだ。Ultracodeが複数体を同時に動かすのに対し、Highは1体がじっくり考える。診断が出たところでPlan Modeの承認を挟み、4つのPhaseに分けて段階実行させた。プラグインの整理やCLAUDE.mdの分割など、常時コンテキストを削る作業が中心で、ここまでを配布可能な形にまとめたSkill Pack(19ファイル)も、Phaseの1つとして新設した。
その中の1つに、gh pr mergeを確認制にするconfirm-merge.shの新設があった。少し前に確認なしでPRをmainへマージしてしまい、それが再発だったというミスを、機構(hook)として埋め込む対応だ。4つのPhaseを終えたところで、設定の整合性を見る側とPack構造を見る側、2体のSubagentを並列で当ててレビューさせ、計14件の指摘を全部反映した。その日のうちに3つのリポジトリへcommit・pushまで終えた。ここまでは、Fable 5が一体でHighモードのまま、順番に考えて進めた作業だった。
Ultracodeで、動いている環境そのものを疑わせた
ここで終えてもよかった。レビューは通っている。指摘も全部反映済みだ。でも、それで運用に入れる前に、もう一段挟むことにした。今度はUltracodeを使い、改善そのものを疑う側に回る4体のエージェントを並列で走らせ、ガードレール迂回・ローカル整合・外部脳整合・導入シミュレーションという4つの観点で、今回触った範囲だけでなく、動いている環境全体を監査させた。
一番効いたのが、ガードレール迂回の観点だった。2週間以上前から稼働しているconfirm-master-push.shと、その日新設したconfirm-merge.shの両方が、実在するコマンドの書き方で無言のまま素通りしていると分かった。作った時期は2週間以上離れているのに、開いていた穴の性質は同じだった。
confirm-master-push.shは、コマンド文字列にgit pushという並びがあるかどうかをgrep -q 'git push'で見ていた。これはgit pushが単独で現れる素直なコマンドにしか反応しない。git -C ~/repos/nobu666.com push origin masterのように、gitとpushの間にオプションが挟まるだけで、この並びは消える。confirm-merge.shにも似た穴があった。gh[[:space:]]+pr[[:space:]]+mergeという、ghの直後にprが続くことを前提にした正規表現で、gh -R owner/repo pr mergeのように-Rオプションが割り込むと同じく素通りした。どちらも、確認を求めるべき操作が確認なしに通ってしまう向きの迂回だった。
検出ロジックを書き直して、実測で確かめる
見つかった穴は、単純な部分文字列一致という検出方式そのものの限界だった。直したのは、コマンド区切り(;・&・|)を跨がない範囲でトークンの出現順序を見る正規表現への書き直しだ。gitの後に何らかのオプション列を挟んでもpushが同じコマンド内に現れれば拾う、ghとprの間にオプションが挟まっても拾う、という形にした。
書き直しただけでは安心できない。確認を出す(ASK)べきコマンドと、無言で通す(SILENT)べきコマンドを20ケース分並べた実測マトリクスを組んで、1件ずつ期待どおりに動くかを確認した。confirm-master-push.shを2週間以上前に作った自分にも、confirm-merge.shをその日作った自分にも見えなかった迂回パターンを、監査に回った側が見つけて、直した側がもう一度実測で確かめる。作る・疑う・確かめるを別の役に分けた。
この2つのhookの迂回穴を含め、振り返り監査からは計13件の指摘が出た。README手順に書いた/output-styleコマンドが実は既に廃止されていたこと、persona.mdがその日の再設計を反映しないまま古くなっていたこと、topology地図の記述が別セッションで完了していたPRのマージ後の状態に追随していなかったことなど、性質はさまざまだった。全部反映し、反映後の差分を再度構造レビューに通す過程で、新しい食い違いが2件見つかって直した。残った成果物は、hook2本の書き直し、CLAUDE.mdの分割、配布用にまとめたSkill Pack(19ファイル)、そしてこの13件の指摘一覧そのものだ。
Ultracodeは、軽くはない
この記事自体も、書き上げた後にUltracodeでレビューさせた。多観点レビューと構造レビューを2体並列で1周、そこで見つかった事実誤認(confirm-master-push.shの作成時期を「当日」と誤って書いていた)を直してから、もう1周かけ直した。2周分で消費したトークンは合計17万近く、実行に9分近くかかっている。記事1本、しかもレビューだけでこの量だ。4体が実環境を丸ごと読み込んで監査した振り返り監査の本体は、対象の範囲がこの記事より広い。正確なトークン数は記録していないが、これより軽かったとは考えにくい。
Ultracodeは強い。1体では気づけない角度の指摘を、同時に複数持ち込んでくる。今回も、自分では気づけなかった時期のズレを一発で拾った。ただし、その分だけ重い。今回の環境再設計でも、Phase1-4の実作業自体はFable 5(High)の単体思考で足りていて、Ultracodeを持ち出したのは仕上がった後の監査だけだった。全部の工程をUltracodeにする必要はない。判断や実装が積み上がる工程は1体の深い思考に任せ、「本当に大丈夫か」を疑う工程にだけUltracodeを差し込む。それくらいの使い分けが、今のところちょうどいい。
整理
| 対象 | 時期 | 見つかったこと |
|---|---|---|
| confirm-master-push.sh | 2週間以上前(6/22時点で既に稼働) | git -C dir push等、オプションでgitとpushが分断され素通り |
| confirm-merge.sh | 当日新設 | gh -R owner/repo pr merge等、オプションでghとprが分断され素通り |
| 振り返り監査4観点全体 | 当日実施 | 上記2件を含め計13件、うち2件は反映後の再レビューで追加検出 |
結び
このやり方の価値は、作業が速く終わることではなかった。自分で作った安全装置の穴は、作ってから2週間経っていようが、その日作ったばかりであろうが、作った本人には一番見えにくい。時間が経てば見え方が変わるということもなかった。confirm-master-push.shを作ったときの自分も、confirm-merge.shを作ったときの自分も、「pushとmergeを確認制にした」という達成感の方が先に立っていて、その検出ロジックの粗さまでは目が向いていなかった。だから監査の対象を「今回触った差分」に絞らず、今動いているものすべてに広げた。範囲を絞っていたら、2週間以上前からある方の穴には出会えなかった。
1人(1エージェント)で作って自分でレビューするだけなら、この迂回穴にはたぶん次にpushを打つ瞬間まで気づけなかった。作った本人が気づけない場所を、視点の違う複数のエージェントに、作られた時期を問わず疑わせる。それができる体制を持てたことの方が、今回作ったどの機能単体よりも大きい。ただし、その体制はタダでは動かない。どこにUltracodeを使うかを選ぶところまで込みで、この体制だと思っている。