0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIの「完了しました」は当てにならない — 誤完了を証拠で止める品質ゲート

0
Last updated at Posted at 2026-08-24

AIの「完了しました」は当てにならない — 誤完了を証拠で止める品質ゲート

上原正吉(EarthLink Network Co., Ltd.)。Claude Codeを開発の主体に据え、20を超えるプロダクトを1人で同時に開発・運用しています。これは、その現場の実測記です。

AIの「完了しました」は当てにならない — 誤完了を証拠で止める品質ゲート

2026 年 6 月 29 日、あるモバイルアプリが「PC 版とのパリティ第 1 弾完了、162 テスト green」と報告されました。実機 (TestFlight) で触ると、主要動線がことごとく壊れていました。SafeArea 未対応でノッチに食い込み、チャットは送信即 failed、設定画面は 404、ナレッジのアップロードは failed。162 テストが緑で、実機で動いた主要動線は 0。この記事は、AI にコードを書かせると能力より先にぶつかる「完了しました」の虚偽報告を、複数プロダクトの実例で解剖し、そこから機械強制の品質ゲートに落とし込むまでの記録です。

誤完了の典型: 「テストが通る」を「動く」と偽る

162 対 0 の根本原因は 1 つに尽きました。すべてをモックしたユニットテストの green を、「動くアプリ」の証拠にして報告したことです。ふりかえり文書で 7 つの根本原因をコード証拠つきで並べたのですが、要点はこうです。API の baseURL が配線されていない。認証の結線は「future step」というコメントで先送りされている。そして実機操作はゼロ。つまりテストは「自分が書いたモックが自分の期待通りに動く」ことしか確認しておらず、外界とつながっていなかった。ここから「テストが通る ≠ 機能が動く」を合言葉にし、完了ゲートの skill を新設しました。

promptflow モバイルの CI 実行結果。ユニットテストは 162 件すべて green(画面は再構成・匿名、数値は実測)。この緑の裏で、実機の主要動線は 1 つも動いていませんでした

別の顔: 「実装した」と「本番で動いている」も別物

誤完了は、AI 特有の幻覚だけが原因ではありません。人間でも踏む「実装したのに本番に無い」も、同じ顔をしています。ニュースキュレーション基盤で、壊れた JSON をサルベージする多段フォールバックを実装したのに、本番でエラーが止まらない事件がありました。調べると、フォールバックに使う json-repair ライブラリがワーカー用の requirements ファイルに入っておらず、Docker イメージに存在しなかった。requirements ファイルが 2 つある構成の落とし穴です。コードは書かれ、レビューも通り、しかし本番では dead code。その間、監視には 1 万件を超えるエラーイベントが積み上がっていました。この一件で、パッケージング契約そのものをテストで assert するようにしました。

# 「実装した」を「本番イメージに入っている」で裏取りする契約テスト
def test_fallback_dependency_is_packaged():
    # ワーカーの requirements に json-repair が宣言されていること
    reqs = read_requirements("requirements-worker.txt")
    assert "json-repair" in reqs, "本番イメージにフォールバック依存が無い"
    # 実際に import できること(宣言だけでなく解決可能か)
    import importlib
    assert importlib.util.find_spec("json_repair") is not None

監視ダッシュボードに積み上がった ModuleNotFoundError: No module named 'json_repair'。フォールバックのコードは書かれていたのに、本番イメージに依存が入っておらず dead code のまま、エラーイベントが 1 万件を超えて記録され続けていました(画面は再構成・匿名、数値は実測)

幻覚報告: 空のブランチで「1 行追加しました」

最も AI らしい誤完了も見ました。ローカル LLM オーケストレーターに自分自身の改修を投げる dogfooding で、リポジトリを clone した実行エージェントが、そのリポジトリの設定ファイル (「実装はサブエージェントに委譲せよ」というルール) を読んで委譲しようとしたのです。ところが headless 環境では孫サブエージェントが起動できず、何も変更していないのに「1 行追加しました」と幻覚報告し、空のブランチを push。当然 GitHub は「空の push」を 422 で弾きました。ここでの学びは 2 つあって、ひとつは「自己開発したときだけ、自リポの設定が実行エージェントにとって毒になる」こと。もうひとつは、報告を信じてはいけない、成果物 (実 diff) を見なければいけない、ということです。

仕組み: なぜ AI は「完了しました」と言ってしまうのか

落ち着いて見ると、誤完了にはいくつか共通する仕組みがありました。

第 1 に、AI は「タスクの意図を満たしたか」ではなく「もっともらしい終端状態に到達したか」で完了を判断しがちなこと。テスト green、コミット作成、PR 作成といった中間成果は、それ単体では意図の達成を意味しません。第 2 に、完了の分母に「デプロイ」や「実機」が入っていないこと。「リポジトリ内では 100% できた」を「本番で動く」と同一視すると、上の json-repair や後述のプラグイン凍結が起きます。第 3 に、検証対象を取り違えること。あるゲートは working tree の diff だけを見ていて、「コミットすると tree がクリーンになり、永遠に未完了扱いになる」というバグを持っていました。つまり「完了」の定義そのものが、外界と切れていたのです。

対策: 完了を「主張」ではなく「証拠」で定義する

対処は一貫して「完了の定義を、AI の自己申告から機械検証可能な証拠へ移す」ことでした。段階的にゲートを重ねました。

ひとつめは、証拠なき完了主張をブロックするゲート。境界をまたぐ変更 (外部 API・決済・DB 書き込み・SaaS 間連携) を「ビルドが通った」「ユニットが緑」といった弱い代理証拠だけで完了と呼ぶことを禁じ、実機相当の E2E 証拠を要求します。稼働監視 SaaS では、この実機ゲート (社内で MUST 25 と呼んでいる原則) に一度不合格を出したことで、ユニット全 green の裏で 5 件のバグ (DB create が落ちる、スケジューラがペイロードに必要データを含めない、Dockerfile のインストール順ズレ等) が一気に見つかりました。

ふたつめは、モバイルのような境界の多い領域に専用の完了チェックリストを skill として持たせること。イメージは次のようなものです。

# feature-done ゲート(実機で満たされるまで「完了」と呼ばない)
- [ ] API baseURL が実環境に配線され、モック無しで 1 往復した証拠がある
- [ ] 認証フローを実機で通過した("future step" コメントが残っていない)
- [ ] 主要動線(作成・送信・アップロード)を実機で 1 回ずつ手で通した
- [ ] SafeArea / ナビゲーションが実機スクリーンショットで確認済み

みっつめは、clean 環境でゲートを回すこと。ローカルの手元では通るのに、clean にチェックアウトした環境ではテストが落ちる——これは実ディレクトリに依存した非 hermetic なテストを炙り出します。実際、あるゲートは clean worktree でテストを回して「ローカルの npm run check では永遠に気づかない欠陥」を検出しました。これはゲートの狙い通りの価値でした。

配布のズレ(デプロイドリフト): push 型フックでは見えない凍結

最後に、完了の分母から「デプロイ」が抜けると何が起きるかの、はっきりした例があります。自動運転中の本番アカウント群で、配布しているプラグインが 12 日間も古い版のまま凍結していたのに、品質ゲートは全 green で、誰も気づかなかったのです。5 つの構造穴のうち核心は、「リポジトリ内 100%」と「本番反映済み」を同一視していたこと、そして検出層が push 型のフックしか無かったことでした。git のコミット時に走るフックは、git の外の状態 (どの版が実際に配布先で動いているか) を構造的に観測できません。対策は、配布先で cron が「インストール済みの版」と「配布すべき版」を突き合わせるpull 型のパリティチェックを持つこと。「配布のズレの検出は pull 型の定期突合だけ」という原則を、ここで文書に昇格させました。

配布先で走らせる pull 型のパリティチェック。インストール済みの版と配布すべき版を突き合わせ、12 日分のズレを検出しています。この間、品質ゲートはずっと全部 green のままでした(画面は再構成・匿名、期間は実測)

まとめ — 転用できる教訓

  • 「テストが通る ≠ 機能が動く」。全モックのユニット green は「自分の期待が自分の期待通り」を確認しているだけ。境界は実機 E2E で完了判定する
  • 「実装した ≠ 本番で動いている」。パッケージング・デプロイまで含めて契約テストで裏取りする。dead code は緑のまま本番で沈黙する
  • 完了は主張でなく証拠で定義する。ビルド OK・ユニット緑・デプロイ SUCCEED という弱い代理証拠だけで「完了」と言わせない
  • デプロイドリフトは pull 型で見る。push 型フックは git の外を観測できない。「リポジトリ内 100%」は「本番反映済み」ではない

筆者について

上原正吉。EarthLink Network Co., Ltd. でAI開発をしています。2025年からClaude Codeを開発の主体に据え、今は20を超えるプロダクトを1人で同時に開発・運用しています。この連載では、その現場で実際に起きたこと(うまくいったことも、失敗も)を、数字と一緒に書いていきます。

また、AIで業務や開発を組み替えたい会社・チーム向けに、AI活用のコンサルティングも受け付けています。ご相談は www.eln.ne.jp からどうぞ。


EarthLink Network は、会社の全業務を AI で回すために、必要になったものを自社で作っています。いま作っているプロダクトの一覧と概要は、こちらにまとめています。

EarthLink Network が自社でつくっている18のプロダクト

会社と各プロダクトの詳細は、公式サイト www.eln.ne.jp をご覧ください。


🔗 この記事は Zenn に掲載した記事の再掲です。正規版(canonical)はこちら: https://zenn.dev/chooser/articles/series-004-ai-dev-process

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?