デプロイ成功なのに、画面が変わらない

「デプロイ成功なのに画面が変わらない」という人間の小さな違和感をきっかけに、15 ヶ月前から誰も触れずにいた古い設計の沼を AI が一日で遡って直した出来事を、前提知識ゼロの読者向けにストーリー化したもの。LLM Wiki つき Coding Agent がどのような働き方をできるのかをエピソードとして残す。

1. 小さな違和感

ある日、ユーザから「グラフが出ない」という報告がきた。原因を直す修正 PR が出され、人間がそれを merge する。デプロイ完了の通知が緑で点く。画面をリロードする。

直っていない。

人間が Agent にひと言投げる。

プロダクションデプロイ完了したっぽいけど直ってなくないですか?

これがこの一日の出発点だった。

2. 「成功」のほうが嘘をついていた

Agent はその問いを受けて、9 分作業して返事を返した。

はい、直ってないです。本番には旧バージョンの内容しか出ていなくて、新しいバージョンはまだ立ち上がりきっていません。デプロイ判定は false positive で、「サイトが何か応答した」だけで成功扱いにしていて、新しいバージョンが立ち上がったかは確認していません。

ここで見立てが切り替わる。「今回の修正 PR の不具合」ではなく、デプロイ成功の判定そのものが嘘をついていた、だ。

新しいバージョンと古いバージョンが両方存在していて、新しいほうはまだ立ち上がりきっていない。ユーザのアクセスは古いほうに行っている。古いほうは動いているから「サイトは応答した」というチェックは通る。だから「成功」になる。

判定が見ているのは「新しいバージョンがちゃんと立ち上がったか」ではなく、「サイトが何か返したか」だ。これでは、新しい変更が反映されようがされまいが、見分けがつかない。

人間の短い問い 1 つから、9 分で「直っていない理由」と「成功と表示された理由」の両方が、事実とログ付きで返ってきたことになる。

3. これは今日初めて壊れたのか?

人間がもうひとつ問いを足した。「この壊れ方は、今回初めて起きたのか?」

Agent は過去のデプロイ履歴を順に遡って、「サイトは 200 だが新しいバージョンは Ready ではない」という同じ状態で成功と言われていた回を探した。出てきた。少なくとも 2 ヶ月前のデプロイにも、同じ偽の成功の実例が混じっていた。それより前は Actions の logs が保存期間切れで失効していて、同じ粒度では確認できない。

つまり、今回のバグは今日壊れたのではなく、少なくとも 2 ヶ月前から同じ壊れ方が記録に残っていた。今日は人間がたまたま「直ってない」と気づいたから、ようやく見えた、というだけだ。

この切り分けが効くのは、「最近の変更が悪い」から「もとから壊れていた」に話の主語が動くことで、犯人探しから設計の見直しに議論を移せるからだ。

そのうえで、もうひとつの問いが残る。新しいバージョンには、いったい何が起きていたのか。それが次の節で見つかる。

4. 新しいバージョンは「殺されていた」

新しいバージョンが立ち上がりきれない理由を見にいくのにも、また人間のひと言で十分だった。クラウド側のログを見るには権限が必要で、人間が az login した直後にこう投げる。

az login したけど見れるようになった?

Agent が 3 分作業して返してきた。

はい、見れるようになりました。新しいバージョンは Ready になっていません。起動中の build が Running TypeScript ... のあとで Killed、最後は exit code '137' ── SIGKILL でした。

新しいバージョンは起動の途中で OS にメモリ不足で強制終了されていた。

このサービスは、起動するたびに、配信する web アプリを一から build してから動き始める作りになっていた。build はメモリを多く食う作業で、サーバに割り当てられたメモリでは足りずに OS に殺されていた、ということだ。

これでようやく、午前中の「成功なのに変わらない」が一本の線でつながった。デプロイ判定は新しいバージョンを見ていない、だから「成功」と出る。しかし新しいバージョンは実際には殺されていて、だから画面は古いまま変わらない。

止血策はすぐに思いつく。サーバのメモリを増やせばいい。今 1Gi なのを 2Gi にすれば build は通るはずだ。だがメモリは、build のときだけ必要なものを、24 時間動いているサーバに常に割り当てることになる。固定費がそのぶん上がる。

そもそも、なぜこのサービスは起動するたびに build しているのか。固定費を上げる前に、その問いを片付けたほうが筋がいい。次は歴史を見にいった。

5. 1 年前の判断が、ずっと延命されていた

その答えは、サービスが Docker 化された一番最初の commit に書いてあった。

「ビルド時に API サーバを参照するため、API サーバの起動を待ってからビルドを行う」

つまり、「アプリを作るときには API サーバが動いている必要があるから、ローカルや CI で先に作っておくのではなく、本番で API が起動したあとに作ろう」という判断だった。

問題は、その後 1 年以上のあいだ、誰もその判断を見直さなかったことだ。

その間に何度か似たようなトラブルが起きている。本番で作り直しに失敗する。タイムアウトする。必要なファイルが入っていなくて落ちる。そのたびに、「作り直しがうまくいくように」必要なファイルを足したり、待ち時間を延ばしたり、エラーを無視したりした。一度も、「そもそも本番で作り直すのをやめる」とは言われなかった。

延命策が積み重なって、誰もこの構成全体を 1 枚の絵で説明できない状態になっていた。「沼」と呼んでいい。

6. 沼を 1 ページに固定する

人間が「ここまで掘った内容を wiki に残しておこう」と指示し、Agent はこの 考古学的調査 の結果を、wiki の 1 ページにまとめた。

「いつ入ったか」「なぜ入ったか」「以降のどの修正がそれを延命したか」「どこを誤読しやすいか」を、関連する変更と一緒に並べた。次に同じところを誰かが触ったとき、もう 1 から掘り直さなくていい。

これは Agent 自身のためでもある。明日になって同じ話題が出たとき、Agent は前日にどこまで分かったかを覚えていない。覚えていないので、wiki を読み返す。読み返すと、昨日の自分が見つけたことから続けられる。

「Wiki つき Coding Agent」という言い方の核心はここにある。記憶は会話の中で消えても、wiki には残る。

7. 計画してから手を動かす

人間が「実装に入る前にまず計画を書いて」と指示する。Agent はいきなりリファクタリングに突っ込まず、まず計画ページを書いた。「いまの状態を観測してから直す」「何段階に分ける」「各段階で何が通れば合格とするか」「どの順番で PR を出すと巻き戻しが楽か」。リスクと未解決の問いも書いた。

このとき Agent は、同じ日にあった定例ミーティングの議事録も読んだ。人間側も同じ問題を「判定がおかしい」「ときどき殺される」の二層に分けて整理していた。独立に出した結論が、人間の議論と同じ方向に収束していることを確認できた。これは安心材料になる。違っていたら、どちらが見落としているかを再検討する材料になる。

8. 実装、レビュー、本物の成功

計画に沿って、本番で作り直していたのをやめ、ビルド済みのものを起動するだけの構成に変えた。実装してみると、計画段階では気づけなかった制約に当たった節もあった。ある変更を入れたら別のページが壊れる、というような。そのたびに「この案は採らない、なぜなら〜」を計画ページに追記して、別の手段に切り替えた。

PR を出し、自動レビューが「ここはもっと良くできる」と指摘した。指摘の妥当性を独立に検証して、直せるところは直し、直せないところは理由を書いて据え置いた。

人間が merge する。デプロイ完了の通知が緑で点く。今度は、画面をリロードすると、ちゃんと変わっている。

このときの「デプロイ完了の緑」は、午前中の「デプロイ完了の緑」とは意味が違う。同じ色だが、片方は嘘で、片方は本物だった。両方を見比べた人間しか、その違いを実感できない。

9. 何が起きていたのか、振り返って

この一日にあったことを並べると、こうなる。

  • 人間が「成功と言っているのに直っていない」という違和感を掴む
  • 人間が Agent に調査を頼み、判定の設計の問題だと切り分ける
  • 人間が「今回初めて起きたのか?」と問い、Agent が過去のログを遡って「少なくとも 2 ヶ月前から同じ壊れ方が記録に残っている」と確認する
  • 新しいバージョンが立ち上がりきれない理由を掘ったら、起動時に SIGKILL で殺されていた、というもうひとつの発見が出てくる
  • 「メモリを増やせば直る」「だが固定費が上がる」という分岐を踏んで、固定費を上げる前にそもそも論を見直す方向へ進む
  • なぜ「本番で作り直す」構成になっていたのか、1 年以上前の判断と、その後の延命策の系譜を 1 ページにまとめる
  • 段階的なリファクタ計画を書く
  • 人間側の定例議論と Agent の独立調査が同じ結論に収束していたことを確認する
  • 実装し、自動レビューを反映し、merge し、本物のデプロイ成功を観測する

これを、ひとつの会話セッションの集中力でやり切ったわけではない。途中で何度か、別の話題で会話が切れたり、同じことを別の角度から問い直したりしている。それでも続けられたのは、毎回 wiki に「ここまで分かった」が残っていたからだ。

役割分担も、はっきり書いておく。「次にどこを疑うか」を選んでいるのはほぼ常に人間で、「そこを実際に掘って、観測した事実を並べて、wiki に残す」のが Agent だった。Agent が独走したわけではない。だがそのぶん、人間が「次の問いを思いつくだけ」で深いところまで掘れる、という関係になっていた。

10. Wiki なしだとどこが詰まるか

似た構図を、wiki なしでやってみるとどこで詰まるかを並べておく。

  • 1 年前の commit のコメントから始まる archeology は、毎回 1 から掘り直すと数時間かかる。固定化しないと、明日には同じ調査を誰かがまた始める
  • 「2 ヶ月前の同じ偽成功」のような遡及確認は、生のログを順に読んで初めて言える。1 回目の調査で結論まで書いておかないと、2 回目の調査者は「これって今回だけの問題かもしれない」とまた疑い直す
  • 自分の独立調査と、人間の議論の結論が一致したかどうかは、両方の文章が同じ場所に並んでいないと突き合わせられない

これらの「あったほうがいい」を 1 か所に集めたのが wiki であり、その wiki を読みながら次の手を選ぶのが Wiki つき Coding Agent である、というのがこのエピソードのまとめになる。

次に読む

仕組みそのものに興味があれば

このエピソードで動いている「Wiki つき Coding Agent」の働き方自体を解説しているのは wiki-driven-workflow。この repo がなぜ本体 repo と別に存在し、raw/ / wiki/ / work/ の三層がどう連携しているか、ingest と filing-back がどう回るか、を概念ページとしてまとめている。

このエピソードの技術詳細を追いたければ

ストーリーは技術詳細を意図的に省いている。PR 番号・検証コマンド・revision 名込みの資料版は pr-887-pr-888-runtime-build-removal-episode-2026-06-01 に置いた。さらに掘り下げると、以下に分かれている。