---
title: "Todoアプリの改修を運営者向け画面からAIに頼めるか 優先度追加と修復ループ編"
description: "第2回で確定した優先度追加の依頼を、固定した開始点から作る隔離環境へ渡しました。第1回の保存済みAI変更案はリポジトリ内テスト9件中3件と優先度絞り込みの受入条件検査に失敗しましたが、固定修復案を1回適用した後は4種類の検査に合格し、リモートGitHubを変えずにローカルcommitまで作成できました。"
lang: "ja"
canonical: "https://llm-lab.dev/posts/todo-cms-priority-repair-loop/"
source: "https://llm-lab.dev/posts/todo-cms-priority-repair-loop.md"
publishedAt: "2026-07-21"
updatedAt: "2026-07-21"
category: "AIDD"
tags:
  - "aidd"
  - "code-agent"
  - "human-in-the-loop"
  - "repair-loop"
  - "sandbox"
---

# Todoアプリの改修を運営者向け画面からAIに頼めるか 優先度追加と修復ループ編

> [!NOTE]
> この記事で確認したこと
>
> 第2回で人間が確定した「Todoへ優先度を追加する」という依頼から、隔離した実行環境でコード変更を始める処理を作りました。
>
> 第1回で実際のAI APIから得た7ファイルの変更案を再生すると、リポジトリ内テストは9件中6件成功、3件失敗でした。
> そのうえ、実行器の外に置いた受入条件検査が、優先度による絞り込みの未実装も検出しました。
>
> 固定した修復案を1回適用したあとは、リポジトリ内テスト12件、構文と画面部品の検査、既存データの移行検査、優先度機能の受入条件検査がすべて成功しました。
> 10ファイルの変更をローカルbranchへcommitしましたが、リモートGitHubと本番データは変更していません。
>
> 今回の修復案は再現用にあらかじめ用意したものです。
> 実AIがテスト失敗から適切な修復案を作れるかは、まだ確認していません。

## 要件確定からコード変更へ

このシリーズでは、Gitやターミナルを操作しないTodoアプリの運用担当者が、専用Web画面からAIへ改修を頼める仕組みを段階的に作っています。
最終目標は、自然文の依頼から確認質問、要件確定、隔離環境でのコード変更、テスト、GitHubのDraft PR（正式なレビュー前の下書き）までをつなぐことです。
最後に10種類の改修を実行し、既存のコーディングエージェントへ人間が手作業で依頼する方法と比べます。

```text
自然文で改修を依頼
  ↓
不足事項への質問と要件確定       第2回までに確認
  ↓
隔離環境でコード変更
  ↓
固定テストに失敗したら上限内で修復 第3回で確認
  ↓
合格した変更だけローカルcommit    第3回で確認
  ↓
GitHubのDraft PR                 第5回で検証予定
```

[第1回](/posts/todo-cms-safe-local-code-agent/)では、3社のAI APIを同じ形式へつなぎ、コード変更案が固定テストに失敗したとき、GitHubへ送る前に停止できることを確認しました。
ただし、コード変更案は7ファイルまで作れたものの、テストは9件中3件が失敗したままです。

[第2回](/posts/todo-cms-change-request-confirmation/)では、「Todoに優先度を付けたい。見やすくして」という依頼に4件の確認質問を返し、人間が回答、編集、確定したあとにだけ変更ジョブを発行しました。
発行したジョブは次の工程待ちで止まり、コード変更には接続していませんでした。

第3回は、この二つをつなぎます。
確定済みの変更ジョブから第1回の変更案を起動し、失敗結果を修復処理へ渡して、実際のTodoアプリで優先度を絞り込めるところまで進めました。

## 優先度追加を隔離環境へ渡す

検証計画では、優先度追加を「S01」と記録しています。
S01は「Scenario 01」を省略した管理用IDで、難易度や優先順位を示す番号ではありません。

第2回で確定した内容は次のとおりです。

- 優先度は`low`、`medium`、`high`の3段階にする
- 既存Todoと未指定時の初期値は`medium`にする
- 一覧では色と文字を併用したバッジで表示する
- 優先度と既存の状態を同時に絞り込めるようにする
- 許可していない優先度はAPIが400を返す
- 優先度は色だけに頼らず、文字でも判別できるようにする

「変更ジョブを開始」を押すと、サーバーは人間の確定状態をもう一度検査します。
条件を満たした依頼だけが、固定した開始時点から複製した隔離環境へ渡ります。

モデルに渡すのは、許可したソースと確定済み要件です。
モデルはファイルを直接書き換えず、変更案だけを返します。
書き込みを行う実行器は、マイグレーション、アプリ本体、画面、テストの許可範囲から外れるパスを拒否します。

今回、新しい外部AI API呼び出しは行っていません。
第1回でさくらのAI Engineから得た変更案を保存し、同じ失敗を再現できる入力として使いました。
変更案を作ったときのAPI呼び出しは1回ですが、第3回の再実行におけるAPI呼び出しは0回です。

## 最初の変更案で落ちた三つのテスト

保存済みの変更案は、優先度用のマイグレーション、入力検査、保存処理、作成フォーム、一覧のバッジ、テストを含む7ファイルでした。
許可パスの検査を通したあと、隔離環境へ適用してテストを実行しました。

結果は、第1回と同じ9件中6件成功、3件失敗です。

| 失敗したテスト | 変更案との食い違い |
|---|---|
| マイグレーション後の列一覧 | 優先度列を追加したのに、追加前の列だけを期待していた |
| 既存Todoの初期優先度 | 検査するTodoを作らず、存在しない行を読んでいた |
| 入力値の整形 | 初期優先度`medium`が返るようになったのに、古い期待値のままだった |

いずれも、優先度を追加するコードそのものではなく、変更後の契約と同時生成されたテストが食い違った失敗です。
テストを生成したからといって、そのテストが変更後の仕様を正しく表しているとは限りません。

ここで3件の期待値だけを直せば、リポジトリ内テストは通せます。
しかし、それでは「優先度と状態を同時に絞り込める」という確定要件を満たしたことにはなりませんでした。

## 生成テストが見逃した優先度の絞り込み

最初の変更案は、優先度を保存し、作成フォームから選び、一覧へ文字で表示するところまで実装していました。
一方、APIと画面には優先度の絞り込みがありません。

リポジトリ内テストは、この不足を検出していませんでした。
生成されたコードとテストが、そろって同じ要件を落としていたからです。

そこで、実行器が書き換えない場所へ**受入条件検査**を置きました。
これは記事用に用意した検証プログラムで、隔離環境のTodo APIへ優先度の異なる3件を登録し、`status=open`と`priority=high`を同時に指定したとき、該当する1件だけが返るかを確認します。
許可していない優先度の拒否、既存Todoの`medium`への移行、画面上の文字付きバッジも同じ検査に含めました。

最初の変更案に対する結果は不合格です。
`priority=high`をAPIへ渡しても無視され、状態だけで絞り込まれた複数件が返りました。

生成されたテストだけを合否判定に使うと、生成側の見落としまで評価側が引き継ぎます。
確定要件を別の検査へ固定したことで、「テストは直せたが、機能は足りない」という成功扱いを避けられました。

## 修復後の四つの検査

初回の失敗後、実行器は失敗した検査名と終了コードを修復処理へ渡しました。
今回の修復処理は外部AIではなく、同じ結果を繰り返し検証するための固定案です。

修復案では、生成テストの期待値を変更後の契約へ合わせるだけでなく、次の不足も補いました。

- APIで優先度を検査し、状態と同時に絞り込む
- 画面へ優先度の絞り込み欄を追加する
- `低`、`中`、`高`の文字を持つバッジを追加する
- 優先度と状態を同時指定するAPIテストを追加する
- 許可していない優先度の絞り込みを拒否するテストを追加する

修復後は、変更ファイルが7件から10件になりました。
再実行した検査は次の4種類です。

| 検査 | 結果 |
|---|---:|
| リポジトリ内テスト | 12件中12件成功 |
| JavaScriptの構文と画面部品の検査 | 成功 |
| 既存データを`medium`へ移す検査 | 成功 |
| 優先度機能の受入条件検査 | 成功 |

すべて成功したあとにだけ、実行器は`ai/s01-priority`というローカルbranchへcommitしました。
リモートGitHubへのpushとDraft PR作成は行っていません。

![最初の変更案はテスト6/9件と受入条件検査に失敗し、固定修復案を1回適用したあとに4種類の検査へ合格した変更ジョブ画面](/images/posts/todo-cms-priority-repair-loop/01-code-job-completed.webp)

変更ジョブ画面には、固定した開始点、変更した10ファイル、修復後の検査、ローカルcommitを表示しました。
利用者はターミナルを開かず、初回に何が失敗し、何回修復し、最終的にどの検査へ合格したかを確認できます。

![変更した10ファイルと、修復後に4種類の検査がすべて成功したことを表示するコード変更ジョブの結果欄](/images/posts/todo-cms-priority-repair-loop/02-repair-checks.webp)

## 改修後のTodo画面

検査が通っただけでは、運用担当者が使える画面になったかは分かりません。
修復後のTodoアプリを起動し、Playwrightで優先度`高`を選びました。

一覧には、高い優先度を持つ1件だけが残りました。
同じ画面に状態の絞り込みもあり、APIでは`status`と`priority`の同時指定を検査済みです。
バッジは色だけでなく「優先度 高」という文字を持っています。

![優先度の絞り込みで「高」を選び、文字付きの「優先度 高」バッジを持つTodoが1件だけ表示された改修後のTodoアプリ](/images/posts/todo-cms-priority-repair-loop/03-priority-filter.webp)

第2回で人間が追記した「色に頼らず文字でも判別できる」という受入条件が、ここで初めて実際の画面へ現れました。
要件画面で確定できるだけでは足りず、コード変更後の検査がその文言と結び付いている必要があります。

## 修復ループをどこで止めるか

修復を無制限に続けると、API利用量と実行時間が増えるだけでなく、同じ原因へ似た修正を重ねる可能性があります。
実行器には、修復を最大3回で止める条件を入れました。

もう一つの停止条件は、同じ検査失敗が2回続くことです。
失敗した検査名と終了コードから識別値を作り、修復前後で同じ値になった場合は、次の修復を始めません。

今回は1回目の修復で全検査が成功したため、停止条件には到達しませんでした。
最大回数と同一失敗の停止については、実行器の単体テストで確認しています。

この挙動を再現する`npm run verify:code-job`は、この記事用に作った検証スクリプトです。
固定した優先度追加の依頼を変更ジョブへ入力し、隔離環境の作成、保存済み変更案の適用、初回検査、固定修復案、再検査、ローカルcommitまでを確認します。

```bash
npm run verify:code-job
```

```text
first pass: 6/9 repository tests, acceptance=failed
repair attempts: 1
final checks: 4/4
changed files: 10
remote GitHub changed: false
```

## 固定修復案で残った課題

第3回で、確定済みの依頼から隔離環境を作り、保存済みのAI変更案を適用し、失敗後の修復を経て、動作するTodoアプリのローカルcommitまで到達できました。
第2回まで別々だった要件確定とコード変更が、専用Web画面の一つの操作としてつながっています。

一方、「修復ループが動いた」を「AIが自力で不具合を直せた」と読むことはできません。
修復案は固定しており、失敗ログから修正内容を考えたのは実AIではないからです。
自由な変更依頼に対する修復品質、同じ失敗が続いたときの人間への返し方、別種類の改修でも受入条件検査を用意できるかは未検証です。

したがって、第3回の判断も**Conditional Go（条件付きで継続）**です。
チャットからローカルコード変更までの経路と停止条件は成立しましたが、実AIによる修復と、複数種類の変更に対する再現性はまだ評価できません。

次の判断材料は、優先度追加だけに合わせた固定処理を外したときにも、同じ安全境界を保てるかどうかです。
期限日、タグ、CSV出力、要件不足、危険な依頼を同じ実行器へ通すと、今回の4/4が一例にすぎないのか、再利用できる仕組みなのかが見えてきます。
