画面を持つアプリケーションの見た目をエージェントに作らせるとき、ビルドが通ってテストが全部緑でも壊れたものが残るので、数値ゲート・オフスクリーンのPNG・実際の導線という三段で確認しています。撮影までは自動化できましたが、撮った絵を見て正しいか判断する行為だけは自動化できていません。
テストが緑でも壊れているものが残る
すり抜けたものを並べると、種類が見えてきます。
「値が変わった」は「正しくなった」を意味しません。変換の前後で値が違うことを確認するテストは、結果が破綻していても通ります。差がゼロでないことしか証明していないためです。
エラーも警告も出ないまま止まった絵が出ることがあります。内部の状態は正しく更新されていて、個別に取り出せば期待どおりの値が返る。それでも画面に出る絵だけが更新されない、という壊れ方があります。数値をどれだけ精密に検証しても、壊れているのが数値の先だと捕まりません。
個々の値が正しくても、合成結果が破綻します。ある例では2つの角度がそれぞれ84度と89度で、数値としてはどちらも正しい範囲でした。ところが2つが連なった結果、見た目は何も曲がっていない状態になっていました。
そして、8ラウンドのレビューと1019件の緑のテストを通過して出荷された欠陥があります。誰もレンダリング結果を見ていませんでした。レビュアーには画面を開かないよう指示が出ていて、指示した側も見ていなかったためです。コードとデータを読んでいる限り、描画の欠陥は原理的に見つかりません。
三段に分ける
| 段 | 何をするか | 前の段の盲点 |
|---|---|---|
| 1 | 数値ゲート | ここだけでは、数値として妥当で見た目が破綻したものが通る |
| 2 | オフスクリーンでPNGを書き出す | 単体で組んだ画面は、出荷される絵と違うことがある |
| 3 | 実際の導線を起動して撮る | 画面に写らない挙動は撮れない |
各段が存在するのは、前の段に実証済みの盲点があるからです。どれかを飛ばした回は、飛ばした段の種類の破綻がすり抜けています。
第1段で効いたのは、大きさの検査と向きの検査を必ず対にするというルールでした。大きさだけを見るゲートは「関節が90度曲がっている」を通しますが、それが横に曲がっていることは分かりません。この抽象は分野をまたいで移植できて、経路の生成なら連結性、勾配なら降下方向の実在、直列化なら往復して同じものが返るか、という形に翻訳されています。
第2段には罠があります。オフスクリーンで描画するとき、それらしい名前のフラグを付けると描画そのものが無効になり、真っ黒なPNGが書き出されたうえで終了コード0が返り、「保存しました」と表示されます。クラッシュより厄介です。成功に見えるためです。
第3段が要る理由は、単体で組み立てた画面が出荷される絵と違ったことが2回あったからです。一方は展開されていないプレースホルダーをそのまま描いていて、もう一方は指定に関係なく常に同じ画面を撮っていました。1か月生き延びた欠陥は、実際の導線を通したときにしか再現しませんでした。
ゲートは事故のたびにしか増やさない
検査項目は、実際にすり抜けた事故が1件起きるごとに1つ足すという決め方にしています。起こりうる失敗を想像して先回りで網羅すると、当たらない検査ばかりが増えて信号が薄まります。
結果として項目数は事故のたびに増えました。ある対象では84→104→110、別の対象ではレイアウトの検査ルールが19個あり、1ルールが過去に1回出荷された欠陥に対応しています。
撮る断面も同じ理屈で増えています。変更した箇所だけを撮ると、レイアウトの破綻は永久に見つかりません。内容が最小のとき、最大のとき、空のとき、ウィンドウが最小のとき、言語を切り替えたとき。9つの画面に対してこれらを掛け合わせ、撮る断面は64から74まで増えました。増えたきっかけは、内容が最小の断面を撮らなかったために、画面下に300〜430pxの空白と背景の抜けた帯を見逃した事故です。どちらも「変更箇所は正しい」PNGだけを見ていたので通っていました。
「保存した」で終わらせた回は全部すり抜けた
撮影は引数で角度も時刻も指定できるところまで自動化しました。それでも、書き出したPNGを実際に開いて見ない限り、検証したことになりません。
ここが自動化の境界です。スクリプトが終了コード0で終わり、ファイルを1枚書いた。それだけでは何も確かめていません。手元の記録では、この工程を「保存した」で終わらせた回はすべて、その回の破綻をそのまま通しています。
同じことは、この技術ブログの図版でも起きています。エージェントに書かせたSVGは、ソースを読むかぎり座標も属性も正しく見えて、ラスタライズして初めて矢頭の消失や文字のはみ出しが分かります。対象が違うだけで、境界は同じ場所にあります。
ブログClaude Codeに書かせたSVGをsipsでラスタライズして自分で目視させるClaude CodeにSVGで図を描かせる手法は既にありますが、書かせた図が実際にどう見えるかは書いた本人が確認していません。macOS同梱のsipsでPNGに変換し、そのPNGをエージェント自身に読ませて確認させる運用と、sipsが黙って落とす属性の実例をまとめます。サブエージェントに任せた場合も同じです。「視覚的に確認しました」という報告は、報告に書かれた画像のパスを親が自分で開くまで受理しないことにしています。報告と実際の画像が食い違ったことが2回あったためです。
1つだけ機械化しなかった
見る観点は6つあります。背景の連続性、境界の明示、余白の意図、はみ出し、重なり、整列。このうち5つは数値化して自動判定に移しました。
余白の意図だけは、意図的に人間に残しています。規約にもそう書いてあります。「この観点に対応する機械検査は無い。人が画を見て判断する」と。
一度は余白を測るルールを実装しました。ただしそのルールは削除されています。余白が広いこと自体は欠陥ではなく、そこに意図があるかどうかが問題で、意図は測れないためです。
この件には後日談があります。規約に「6観点すべてを数値化した」と書いた時期があり、それを事実に合わせて訂正するコミットが残っています。自動検証の射程を、書いた本人が実際より広く見積もっていました。
検証できないものは作らない、という判断
自動検証の射程は、設計の選択そのものを左右します。
ある機能は「目視でしか確認できない」という理由で、2回にわたって実装を見送られています。効果が正しく出ているかを確かめる手段が目視しかなく、その状態で入れると壊れても気づけないためです。
3回目に採用されました。変わったのは機能の側ではありません。レンダリングする前に数値で判定できる形が用意できたので、「目視でしか確認できない」という反対理由が成立しなくなったからです。数値で先に確かめ、描画がそれを追認する順序になりました。
検証の射程が伸びた瞬間に、それまで却下されていた設計が採用可能になる。この順序は覚えておく価値があります。
残っているところ
撮影の自動化が進んだぶん、確認のたびに画像を読み込む量が増えました。1つの変更で断面を何枚も撮ると、それを全部開くのは人間の側の作業として残ります。
もう1つ、この運用は「見れば分かる崩れ」にしか効きません。図や画面に書かれている内容そのものが間違っている場合は、何枚撮っても見つかりません。そちらは記載されている値や識別子を1つずつ根拠に当てる別の工程で、目視のループとは分けて回しています。
