
エージェントが作ったブログは、なぜ公開されなかったのか
このブログのリポジトリは、最初の版を丸ごとコーディングエージェントが作った。 Astro のプロジェクトが用意され、記事のコレクションにスキーマが定義され、GitHub Actions のワークフローが置かれ、GitHub Pages へのデプロイまで書かれていた。 ファイルを眺めるかぎり、やることは終わっているように見える。
サイトは公開されていなかった。
何が壊れていたか
原因は一つではなかった。順に出てきたので、順に書く。
まず npm ci が落ちていた。
package.json が astro: ^5.2.4 を宣言しているのに、lock ファイルには 7.2.10 が入っていた。
npm ci は両者の整合を検査するので、依存のインストール段階で止まる。
CI の実行時間は18秒だった。ビルドまで到達していない。
これを直すと、次が顔を出した。
src/pages/blog/tags/[tag].astro の import が一階層浅い。
import BaseHead from '../../components/BaseHead.astro';
このファイルは src/pages/blog/tags/ にあるので、src/components/ に届かせるには ../../../ が要る。
ところが Astro はこれをビルド全体の失敗にはせず、そのルートだけを落として先に進む。
結果、ビルドは最後まで走るのに HTML が一枚も出ない。
dist/ に残っていたのはフォントの woff が2つだけだった。
3つ目は /about である。
TypeError: Cannot read properties of undefined (reading 'length')
レイアウトが tags.length を無条件に読んでいた。
about ページはブログ記事ではないのでタグを渡していない。
記事側からしか使われない前提で書かれたレイアウトを、記事でないページが借りていた。
4つ目が厄介だった。 ここまで全部直してビルドが緑になっても、サイトは公開されない。 GitHub Pages の Source が「Deploy from a branch」のままだったからである。 ワークフローがアーティファクトをアップロードしても、Pages 側はそれを見ない。 main ブランチのルートを Jekyll でビルドしようとし続ける。
読んでも出てこない不具合
4つのうち、1つ目と4つ目はファイルを読めば見つかる。
package.json と package-lock.json を突き合わせればバージョンの食い違いは見えるし、Pages の設定は gh api で一発で引ける。
2つ目と3つ目は違う。 どちらも、1つ目を直してビルドを実際に走らせるまで出てこなかった。 最初の故障が後ろの故障を隠していたからである。 CI は18秒で赤くなって止まっていたので、その先に何が待っているかは誰にも見えていない。
だから「コードをよく読む」では届かない。 届くのは、直して、走らせて、また落ちるのを見る、という手順のほうだった。
完了の定義
エージェントが無能だったわけではない。 content collections も zod のスキーマも RSS も、書かれているもの自体は妥当だった。 むしろ人手で組むより整っている。 足りなかったのは書く能力ではなく、どこで手を止めるかの基準である。
この作業には、終わりの候補が3つあった。
- ファイルが揃っている
- CI が緑になる
https://techblog.cro-co.co.jp/が 200 を返す
エージェントは1つ目で止まった。
git push して、ワークフローを起動して、そこで報告している。
起動した結果は見ていない。見ていれば18秒の赤にすぐ気づく。
そして3つ目は、CI が緑でも保証されない。
Pages の Source はリポジトリの中に書かれていない。
Actions のログにも出てこない。
ブラウザで開くか curl を投げるまで、公開されているかどうかは誰も知らない状態が続く。
1つ目と3つ目のあいだに、この件では4つの不具合が挟まっていた。
ここで問題なのは、その3つのうちどれを選ぶかがコードの中に書いていないことである。 リポジトリのどこを探しても出てこない。 依頼した側の頭の中にしかない。
AI の時代に人間に残るのは創造性だ、という言い方をよく耳にする。 この件に関するかぎり、残ったのはもっと退屈な仕事だった。 どこまでやったら終わりかを決めて、それを確かめること。 エージェントの出力に創造性が足りなかったのではなく、自分の出力を疑う理由が渡されていなかった。
完了の定義をエージェントに渡す
とはいえ、この基準は人間に固有のものではない。 書けば渡せる。
引き取ったあとのこのリポジトリでは、PR の時点でビルド検証が走るようにして、デプロイ後に主要なパスへ curl を投げて 200 を確認するところまで手順にした。
Dependabot が依存を上げてくる PR も、同じ検証を通らないとマージできない。
一度書いてしまえば、次からは人間が見ていなくても回る。
つまり、この記事で人間の仕事だと書いたものは、来週には自動化されているかもしれない。
実際そのほうがいい。
毎回 curl を手で叩きたい人はいない。
それでも、何を完了とみなすかを決める場所は残る。 残るというより、そこしか残らない、と言ったほうが近い。 判断を書き出せば渡せるが、書き出すには先に判断していなければならない。
次にエージェントへ何かを任せるときは、プロンプトに完了条件を一行入れることにしている。
このブログでは「https://techblog.cro-co.co.jp/ が 200 を返すこと」だった。
それだけ渡してあれば、4つのうち3つは自分で見つけて直していたはずである。