作ったもの
画面に道具が14個並んでいます。Instagramのキャプション、カルーセルの構成、ストーリーズ、リールの台本、ブログ記事、ホームページの文言、LINE、Googleビジネスプロフィール、画像生成。
使いたい道具を選んで題材を書くと、7〜10枚の構成とキャプションが出ます。文言はそこで直せます。「この文言で8枚を画像にする」を押すと、そのまま投稿できる画像になります。
押す場所を減らすほど、本人が回せるようになる。設計はそれだけです。ここから先が、実際に詰まったところ。

宣言だけでは、機械は動かなかった
「投稿には必ず画像を付ける」を恒久ルールとして規約のファイルに書きました。そのファイルは生成のプロンプトへ丸ごと注入されています。書いてあるし、読ませている。それで足りているつもりでした。
足りていませんでした。出力のスキーマにも、型にも、検査にも image の欄が無かったからです。生成されたJSONに画像が入ることは一度も無く、投稿側の if (image) が常に偽になり、例外もログも出さないまま、テキストだけを投稿して「3/3 成功」を返していました。
直近35件、すべて画像ゼロ。手で書いたときだけ画像が付いていたのは、そのJSONを人間が書いていたからでした。
直した順番。まず出力の型に画像の欄を足し、次に検査で「欄が空なら通さない」を立て、最後に規約の文章を直しました。ルールは、機械が読む場所に置かないと効きません。書いてあった場所は、人間が読む場所でした。
文字が画像からはみ出した
項目数に上限を置いていませんでした。行が増えた分だけ下へ伸びて、画像の外へ出ます。文字帯が狭い型では、本文が枠とロゴに重なりました。
切れて消えるのは、いつも下に置いた導線です。読ませたいものから順に消えていく。
直した順番。描く前に総高さを下見して、入るまで見出し→本文の順に一段ずつ縮めるループを入れました。行が少ないときは逆に行間を広げて台を使い切ります。縮めても入らないときだけ中ほどを間引き、先頭と末尾は必ず残す。末尾を守るのは、そこに導線があるからです。
「15,000」が「15,00 / 0人」で折れた
1文字ずつ折り返していました。日本語だけを見ていると気づきません。数字が入った瞬間に、桁の途中で改行が入ります。
直した順番。半角の英数字が続いている間は1つのまとまりとして扱う折り返しに変えました。

繰り返しの検出が、一度も発火していなかった
同じ言い回しの重なりを測る関数を書きました。返すのは0〜1の割合です。それを呼ぶ側で、件数のつもりで「3以上なら重複」と比較していました。
割合が3を超えることはありません。だから検出は永遠に発火しない。エラーは出ません。テストも通ります。何も起きないだけです。
直した順番。しきい値を 0.35 にしました。ここで学んだのは、通ったテストは「動いた」の証明ではないということです。何も起きないまま緑になる経路があります。
自分のページからの読み取りが、403で弾かれた
書き込みを守るために、どこから来た要求かを見て弾く関門を入れました。読み取りにも同じ関門を通していました。
ブラウザは、同じ場所へのGETに送り元の印を付けません。だから自分のページからの読み取りが「送り元が無い」として弾かれます。攻撃を防ぐために書いた行が、自分だけを閉め出していました。
直した順番。読み取り用の判定を別に立てました。書き込みは厳しいまま、読み取りは「送り元が無い」を許す。守る対象が違うものに、同じ関門を使い回してはいけませんでした。
鍵を入れたのに、効かなかった
公開先へ秘密の鍵を入れるコマンドは、成功を返します。ですが、すでに出ている公開物には反映されません。動いているものが読む値は、それが組み上がった時点で固定されているからです。
症状は「一部だけ壊れている」に見えます。鍵が要らない読み取りは通り、鍵が要る書き込みだけが落ちる。原因が読みにくい形で出ます。
直した順番。中身を変えずに、公開を1本出し直しました。ただしこれは、その時点の手元をそのまま公開に押し出します。検証していない変更が同じ場所にあると、鍵のためだけの操作で、見ていないものまで一緒に出ていきます。先に差分を数えました。

残ったもの
6つのうち4つは、エラーが出ないまま黙って壊れていました。落ちてくれるバグより、そちらのほうが長く生きます。
それと、この文章を書いているときにも1つやりました。詰まったところを説明するのに、記録に残っていない数字を2つ書いていた。測る前に書いた数字です。消しました。
作画:画像生成AI