はじめに
AI が「実装しました」と報告する。テストもすべて通っている。それでも、実際に使うと画面が崩れ、本来見えてはいけない情報が見えてしまう。
Redmine を Laravel / Livewire で再実装する個人開発プロジェクト「artisan-pm」では、そんな問題に直面しました。
目指しているのは、Redmine 7.0 と機能的に同等のアプリケーションです。AI に手伝ってもらえば実装は速く進みます。しかし、「実装した」という報告と、「元の製品と同じように動く」という事実の間には、大きな隔たりがありました。
この記事では、その隔たりを埋めるために試したことを紹介します。ソースコードの比較、独立した監査、実際の利用に近いテスト、画面の自動検査。そして、それらを AI と継続するための工夫です。
なぜ Redmine を Laravel で作り直すのか
きっかけは、AI による大規模な再実装がどこまで通用するのか、試してみたかったことです。
長年使われてきた複雑な製品を題材に、画面や機能だけでなく、権限や初期設定まで再現する。そのとき、AI とどこまで正確に作れるのかに関心がありました。
Redmine を選んだのには、実用的な理由もあります。Rails 製の Redmine は安価な共用レンタルサーバーでは動かしにくい一方、PHP に対応したサービスは広くあります。Laravel で再実装すれば、動作要件を満たすレンタルサーバーでも使えるようになると考えました。
開発用の PostgreSQL に加え、MySQL / MariaDB に対応しているのも、このためです。
| 項目 | 内容 |
|---|---|
| 主な技術 | Laravel 13 / Livewire 4 / Volt / Pest 4 / PHP 8.3 |
| データベース | PostgreSQL、MySQL / MariaDB |
| 同等性の管理 | 機能別のチェックリストと、差分を管理するバックログ |
| 作業規模 | 100件を超える差分項目を管理し、1件ずつテスト付きで実装 |
ソースコードは GitHub の artisan-pm リポジトリ で公開しています。
完了の条件を決めても、それだけでは足りなかった
done には、両方のソースを比較した根拠を残す
早い段階で決めてよかったのは、チェックリストに done と書くための条件です。
「実装した」という報告だけで完了にせず、Redmine 側と artisan-pm 側のソースを読み比べ、両方のファイル名と行番号を残すルールにしました。この記事では、この参照を file:line と呼びます。
大切なのは、参照先を並べることではありません。両方の処理を読み、その挙動が同等だと判断した理由を残すことです。
記録には、2つのドキュメントを使っています。
| ドキュメント | 役割 |
|---|---|
docs/parity-checklist.md |
機能ごとの同等性を done / partial / missing で記録する |
docs/redmine-gap-backlog.md |
差分の対応順序、実装内容、判断の根拠を残す |
ただし、このルールは徹底できていませんでした。後の監査では、十分に比較されないまま done になっていた箇所が見つかります。
過去の報告は、調査の手がかりとして扱う
開発は多数のセッションにまたがります。前のセッションが done と書いていても、それはそのときの評価であり、独立した裏付けではありません。
そこで途中から、チェックリストの扱いを変えました。記述を正しさの証拠にせず、「どこを調べるか」の手がかりとして使い、判定時には Redmine の実ソースに当たり直すようにしたのです。
独立監査では、コードのバグが6件、ドキュメントの誤りが1件、追加の対応候補が22件見つかりました。クローズ中のプロジェクトで、本来禁止すべき操作が許可される問題も含まれていました。
過去の報告を前提に読み進めていたら、見落としたままだったかもしれません。
done の先に残っていた、3種類の問題
1. 完了済みの機能に、閲覧権限の穴があった
その後の監査で、工数エントリの閲覧範囲に関する不一致が見つかりました。この機能は、チェックリストでは done(2026-07-21) になっていました。
実際には、次の問題が残っていました。
- 「自分の工数だけ閲覧できる」ユーザーが、閲覧権限のない別ロールを併せ持つと、閲覧範囲の制限が外れる
- 非メンバー・匿名ユーザー向けのロール設定が無視され、全件閲覧できる扱いになる
- 課題詳細画面で閲覧範囲のチェックが行われず、全員分の工数明細が表示される
単なる表示の違いではなく、権限のないユーザーに情報を見せてしまう問題です。
見つかったのは、done と記録してから2か月あまり後でした。その間、この機能を触る変更がなく、再検証するきっかけがありませんでした。
done は「その時点で確認した」という記録です。確認に抜けがあれば、問題はそのまま残ります。特に権限や閲覧範囲は、変更時のテストに加え、定期的に独立監査する必要があると感じました。
2. 3,900件以上のテストが通っていても、画面は崩れていた
サーバー側のテストがすべて通っている状態でログインすると、画面にいくつも問題がありました。
入力欄の枠線を描く前提だった @tailwindcss/forms が入っておらず、394個の入力欄のうち384個が枠線なしで表示されていました。ヘッダーのメニューは半分以上がスクロールの先に隠れ、プロジェクトを切り替えるドロップダウンも、開いているのに見えません。
3,900件を超えるテストが確認していたのは、サーバー側の処理です。画面の見た目を検査する仕組みは、一つもありませんでした。
テストが多くても、検査していない種類の問題は見つかりません。 ブラウザー上で正しく表示され、操作できるかは、別に確かめる必要がありました。
3. データが少なく、管理者だけで触っていた
当初の開発用データには、課題が2件しかありませんでした。その状態では、一覧やカレンダーの崩れも、データが増えたときの性能問題も目立ちません。
そこで、ある程度使い込んだ状態を想定したデータを用意しました。
- 課題:約430件
- 変更履歴:約1,500件
- 工数:約1,500件
すると、長い件名がカレンダーの枠からはみ出す問題や、ユーザー詳細画面でプロジェクトごとに問い合わせが走り、SQL が257件に膨らむ問題が見つかりました。
操作するユーザーも重要でした。
一般の開発メンバーとして操作するシナリオテストを作ると、工数の記録も Wiki の編集もできません。初期データの「開発者」ロールに、権限が6つしか設定されていなかったためです。Redmine の初期データでは、同じロールに31個の権限があります。
「デフォルト設定のロード」も、チェックリストでは done でした。
少量のデータで表示できることや、管理者で操作できることだけでは、実際に使えるかどうかは分かりませんでした。
AI と進める手順にも、検証が必要だった
アプリケーションだけでなく、AI に仕事を任せる手順でも失敗しました。
委任を重ねたら、監査が数時間止まった
ある監査では、コーディネーター役のエージェントから、さらに5体の監査エージェントを起動しました。コーディネーターはその完了を待つ設計で、何も出力しないまま数時間止まってしまいました。
当時の指示には、並行作業はフラットに起動し、サブエージェントからさらに委任させない、という趣旨の注意がありました。それでも、入れ子の委任が選ばれたのです。
5体が応答したころには、コーディネーターは別の作業に移っていました。監査結果は後から手動で回収できましたが、時間を失い、結果をバックログへ反映する手間も増えました。
この経験から、注意書きがあるだけでは再発を防げないと感じました。次は、主担当が各エージェントを直接起動し、結果を回収するところまでを、毎回の監査手順に組み込むつもりです。
監査スキルを作っても、効果は分からなかった
監査で得た知見は、redmine-parity-audit という Claude Code のスキルにまとめました。入れ子委任を避けること、検索で見落としやすいパターン、判定基準などを記載しています。
ところが、作成手順を見直すと、「スキルあり・なし」の比較評価が省かれていました。スキルを作ったことで満足し、効果を確かめないまま完成扱いにしていたのです。
そこで、3件の同じ評価タスクを、スキルあり・なしの両方で実行しました。決めておいた判定条件で採点したところ、結果は両方とも100%通過。差は出ませんでした。
この3件では、スキルがなくても Redmine の実ソースを読んで確認できていました。スキルが無意味だとは言えませんが、この評価では効果を示せませんでした。
AI の done を疑おうと言いながら、AI が作った検証手順については、その完成報告を信じていたわけです。
スキルも、作ったことと役立つことは別です。 期待する効果があるなら、そこまで確かめる必要がありました。
問題を見つける仕組みを、普段の開発に組み込む
こうした経験を受けて、繰り返し確認できることは、自動検査へ移していきました。
翻訳の抜けを CI で検出する
これは、早い段階から導入してよかった仕組みです。
翻訳には、日本語の原文をキーにする __('原文') という方式を使っています。画面の文言を追加・変更すると、対応する翻訳キーが不足することがあります。
そこで、TranslationCoverageTest.php で翻訳キーの不足を検出し、CI を失敗させるようにしました。機械的に判定できる抜けを、レビューで見つけることに頼らずに済みます。
画面の巡回と、一般メンバーの操作をテストする
画面については、2種類のブラウザーテストを用意しました。
| テスト | 確認すること |
|---|---|
| 画面の巡回検査 | アクセシビリティ、JavaScript エラー、はみ出しや文字切れ、メニューの切り取り、SQL 件数など |
| シナリオテスト | 一般メンバーとして課題・Wiki・フォーラムを操作し、画面とデータベースの状態を確認する |
それぞれ tests/Browser/PageAuditTest.php と tests/Browser/ScenarioTest.php で管理しています。
既存の指摘は基準ファイルに記録し、新しい指摘が増えたら失敗させます。修正した指摘は基準から減らすことで、同じ問題の再発も検出できるようにしました。
検査そのものも、本物のバグで確かめる
新しく作った検査が通るだけでは、問題を見つけられるかどうかは分かりません。
画面の検査は、実際に見つかった崩れを再現し、それを検出できることを確かめてから採用しました。コードの構造を調べるアーキテクチャテストも、違反を仕込んだ一時ファイルで失敗することを確認しています。
正常な状態を問題ありと判定する誤検知もありました。文字リンク風のボタンなどを実際の画面と見比べて調整し、誤検知を12件から2件に減らしました。
画面遷移中だけ現れる要素や、実行ごとに変わる課題番号など、結果が不安定になる原因も一つずつ取り除きました。
特定のユーザーや操作でしか出ない問題を拾う
画面検査は、その後に追加した部品の問題も見つけました。
一つは、アバターの配色です。ユーザー名から背景色を決め、白い頭文字を重ねていました。管理者では問題が出ませんでしたが、テスト用の一般メンバーでは背景が明るくなり、文字のコントラストが不足していました。
もう一つは、3回に1回ほどしか失敗しないテストです。
失敗時の要素を記録すると、原因は「返信を投稿」ボタンでした。クリック後もマウスがボタンの上に残り、ホバー時の明るい背景色と白い文字の組み合わせで、コントラストが不足していたのです。同じ指定のボタンは約100か所あり、まとめて修正しました。
どちらも、普段使うユーザーで一度画面を眺めるだけでは、気づきにくい問題でした。
見た目の改善も、検査しながら進める
機能がそろってから実際に触ってみると、今度は「難しそうで使いたくない」という印象が残りました。Redmine の画面をそのまま再現するだけでは、自分が使いたいと思える操作感には届かなかったのです。
そこで、機能の同等性を保ちながら、画面構成は見直すことにしました。Backlog と Jira を参考に、左サイドバー、ステータスの色分け、控えめな上部バーを取り入れました。
変更は、画面の外枠、共通部品、課題一覧、課題詳細とプロジェクト概要の4段階に分けました。各段階で、既存の全テストに加え、画面の巡回検査とシナリオテストを通してからコミットしています。
検査結果だけで「使いやすくなった」とは言えません。それでも、変更によって別の操作や表示を壊していないかは確かめられます。
アクセシビリティの指摘にも取り組みました。特に多かったのは、ラベルと入力欄が関連付けられていない問題です。
定型的な箇所はスクリプトで処理し、42ファイル・250か所をまとめて修正しました。同じ wire:model がファイル内に一つしかない場合に対象を絞り、ID の重複を避けています。
一連の修正によって、検査の指摘数は次のように変わりました。
| 指摘 | 修正前 | 修正後 |
|---|---|---|
| ラベルと関連付けられていない入力欄 | 463 | 4 |
| 名前のない選択欄 | 136 | 3 |
| コントラスト不足 | 85 | 0 |
修正後は基準ファイルも更新し、改善した状態を維持できるようにしました。
なお、検査中には Docker が応答しなくなり、テストが止まることも2回ありました。再起動後は同じ検査が1分弱で完了したため、このときは実行環境の問題と判断しました。検査が止まった場合は、コードだけでなく環境の状態も確認するようにしています。
次のプロジェクトでは、最初から何を用意するか
今回の経験を踏まえ、次に同様の再実装を進めるなら、完了の条件と検証手順を最初から用意します。
Claude Code に渡す情報は、役割ごとに分けて置くつもりです。
| 置き場所 | 用意する内容 |
|---|---|
CLAUDE.md |
両側のソースを比較し、file:line と判断の根拠を残すという完了条件 |
| 監査スキル | 比較の手順、検索の注意点、判定基準、エージェントの起動と結果回収の手順 |
| 差分バックログ | 作業状態、残っている差分、次に着手する項目 |
| 自動テスト・CI | 翻訳の不足、画面の崩れ、一般メンバーの操作などの反復検査 |
| hooks | ビューや CSS の編集後に、再ビルドと画面検査を促す仕組み |
そのうえで、次の3点を運用に組み込みます。
- 過去の報告だけで判定しない。 ドキュメントを調査の起点にし、元の製品と再実装したアプリケーションを直接確かめる。
- スキルの効果も検証する。 同じ評価タスクをスキルあり・なしで実行し、期待した違いが出るかを確認する。
- 権限や閲覧範囲を定期的に再監査する。 機能追加や変更がなくても、独立して見直す機会を設ける。
特に3つ目は、まだ仕組み化できていない課題です。変更がない機能ほど見直す機会が少ないことを、今回の監査で痛感しました。
おわりに
AI と一緒に開発すると、実装の速さは強く実感できます。一方で、完成したように見えるものを、どう確かめ続けるかが大きな課題になりました。
役立ったのは、完了の根拠を残すこと、過去の報告を独立して見直すこと、実際の利用に近いデータと操作で検査することです。そして、検査手順やスキルも、用意しただけで安心せず、その効果まで確かめる必要がありました。
AI の「できました」を、使う人にとっての「ちゃんと動く」に変える。 artisan-pm の開発で最も多く学んだのは、そのための仕組みづくりでした。