VANNAN GAMES.

← 制作ノート一覧

本番の画像43枚が全部404でも、誰も気づかなかった——AIへの指示より検査を書いた話

サイト全体

カードRPG「フルスタック!」に、集めたカードを並べる「カードずかん」という画面があります。ある日そこを機械に点検させたら、43枚あるカード画像が本番で1枚残らず404になっていました。いつからそうなっていたのかも、すぐには分かりませんでした。

人の目は「無いもの」を見ない

原因はすぐ分かりました。以前カード画像をまとめてWebPに変換したとき、参照側のファイルを1枚だけ直し忘れていたんです。実体は .webp が43枚、.png は0枚。ずかんの側は img/card_<id>.png を読みにいっていました。変換のコミットからこの発見まで、私は何度もこの画面を開いています。開いて、そのまま閉じていました。

言い訳をすると、404の画像は「割れた絵」ではなく、ただの空白として並びます。カードずかんはもともと余白の多いレイアウトなので、絵が無い状態でも画面として成立してしまう。あるべきものが無い、は見ようとしないと見えません。そして人は毎回見ようとはしないもので、私も見ていませんでした。

ここでやっと腹が決まりました。AIにうまく指示を書くことより、間違いが機械で見つかる形にするほうに時間を使おう、と。指示は毎回私が書くもので、疲れていれば雑になります。検査は一度書けば毎回同じ精度で走ります。

書いた検査が、何も検査していなかった

そう決めた直後に転んだので、その話を書きます。推理ノベルのシナリオを触っていて、選択肢の飛び先が実在するシーンかどうかを静的に調べる使い捨てスクリプトを書きました。走らせると「問題なし」。安心してブラウザで遊んだら、いきなり「unknown scene」で止まりました。飛び先の1つが古い名前のままだったんです。

スクリプトを読み直したら、シナリオのデータを取り出す部分が失敗していて、0件のシーンを走査して「全部OK」と言っていました。検査は嘘をついていません。0件のうち0件が正しい、と正直に答えただけです。

ここから、いま動いている検査には全部これを入れました。何件を検査したかを必ず出す。

ok: index対象49ページ・意図どおりのnoindex7件・チェックすべて通過
ok: 8ゲーム・チェックすべて通過
ok: ドキュメント69件・機械照合できる範囲でズレなし

「ok」だけなら、私は数字を見ません。49と出ていれば、ページを1つ足した日に50にならないのが目に入ります(数字は2026年8月26日時点。以下も同じです)。0件で通る検査は、無いより悪いです。無ければ不安なままですが、あると安心してしまうので。

1コマンドにまとめて、応答の直前に走らせる

始まりは3本でした。ページの体裁(h1・description・canonical・OGP・ファビコン・広告タグの位置と、sitemapに載っているか)、ゲームの掲載漏れ、リファレンスと実装のズレ。いまは16本です。増えたぶんは全部、実際に踏んだ事故から足しました。この束を1コマンドにまとめて、AIが応答を終える直前に自動で走るようにしてあります。落ちたら、報告してくる前に直す決まりです。GitHub Actions でも同じものを回していて、こちらは私が手で編集した場合の受け皿。

「リファレンスと実装のズレ」が、この中では変わり種かもしれません。開発メモが69ファイル・約16,000行あって、これがしょっちゅう実装より古くなります。全部を機械で照合するのは無理ですが、画像の拡張子・デバッグ用APIの名前・localStorageのキー・存在しないパスあたりは文字列として突き合わせられる。厄介だったのは、過去形で書いてある変更履歴を対象外にする線引きのほうです。日付が入っているだけで免除にしたら、無関係な日付を含む現役の説明まで見逃していました。

手で持っているものは、必ずずれる

検査を増やすのと並行して、手で書く場所そのものを減らしました。トップのカード・スライダー・404の作品リスト・共通メニュー・構造化データは、JSONを1つ直せば全部再生成されます。

ついこのあいだ、sitemap.xml も同じようにしました。39URLをずっと手で維持していて、点検のたびに更新日のズレが出ていたからです。生成に切り替えた瞬間、制作ノート一覧の更新日が8月4日のまま止まっていたのが出てきました。記事は8月19日まで足しているのに。載せる/載せないの判断も、これでページ側の noindex 一箇所に集まりました。

この手のスクリプトには「書き換えずに差分だけ見る」モードを付けています。検査から呼ぶのはそちら。手で sitemap を直しても、次の検査で落ちます。

禁止リストが、自分のコミットを止めた

許す操作は明示的に決めてあります。読む系とgitの日常操作は許可。機械が止めるのは3つで、強制push、rm -rf、グローバルgit設定の変更。PRのマージだけは許可にも禁止にも入れず、毎回私に聞きにくるようにしてあります。マージは自分で判断したいので。

この禁止リスト、一度だけ私自身の作業を止めました。強制pushを禁止した経緯をコミットメッセージに書こうとしたら、本文にそのコマンド文字列が入っていたせいで弾かれたんです。実行ではなく、ただの説明文なのに!仕方なく日本語に言い換えました。笑い話ですが、リストが効いている証拠ではあります。

これには続きがあって、同じことが翌週もう一度起きました。今度は言い換えでしのがず、止める側を直しています。止めたいのは実行であって言及ではないので、ヒアドキュメントの本文とクォートの中身を落としてから判定する条件を足しました。

入口は短く絞った

AIが最初に読むファイルは105行しかありません。16,000行あるメモのどこを読むかの案内と、踏むと事故る地雷だけを書いています。.js.css にキャッシュ用のクエリを付けない(画像は逆に必須)、ドット始まりのディレクトリは本番で配信されないので構造を壊さない、本文のないページに広告タグを置かない。全部、過去に実際に事故った項目です。

ここに「なぜそうなっているか」まで書きたくなるのを我慢しました。入口が長いと読まれません。いまは字数の上限も検査に入れてあって、超えると落ちます。書いた本人が2回とも自分では気づけなかったので、気づく側を機械に移しました。

まだ手で見ているもの

正直に書くと、機械に移せたのは全体のごく一部です。文章が生成AIっぽくないか、ゲームが面白いか、40秒という制限がちゃんと緊張になっているか。このへんは今も自分の目と手でやっています。移せる気配もありません。

白状すると、冒頭の43枚を見つけたのも検査ではありません。その日たまたま点検させただけです。ゲームの側が参照している画像が実在するかを見る検査は、まだ書けていません。

ただ、そこに使える時間は増えました。カード画像が404かどうかを毎回自分で確かめなくていいぶんだけ。検査は面白い仕事ではないのですが、面白い仕事のための時間を作ってくれます。