Case Study · AI-Driven Migration
オープンソースのBytecode Viewer 2.13.2をJava 8からJava 21へ移行する過程を、Kiro IDEのAI-DLC(AI-Driven Lifecycle)ルールの上で最初から最後まで記録しました。AIがコードを代筆したのではなく、人間が意思決定し、AIがドキュメント・計画・コード・検証を一つずつ積み上げていった話です。
なぜこのプロジェクトをAI-DLCに乗せたのか
Bytecode Viewerは、JavaバイトコードやAndroid APKを解析するときに使われる古参のオープンソースデスクトップアプリです。6つのデコンパイラ(CFR、Procyon、FernFlower、JADX、JD-GUI、Krakatau)と3つのディスアセンブラ、そしてJavaScript・Python・Ruby・Groovy・Javaのプラグインシステムを1つのプロセスに収めている、小さいながらも堅実なアプリです。ただしJava 8に固定 されており、その上にモダンなツールチェーンを載せるのは年々重くなっていました。
今回の課題はシンプルです。「Bytecode ViewerをJava 8からJava 21へ移してくれ」。シンプルですが、実際にやってみるとすぐに厄介になります。SecurityManagerはJava 18で削除され、内蔵のNashornエンジンはJava 15で外され、旧バージョンの依存ライブラリ40余りの多くは最新版がJava 11以上を要求します。さらにこのプロジェクトはテストが事実上1つだけ です。
このような典型的なブラウンフィールド・モダナイゼーションを感覚で処理すると、必ず何かを忘れます。どこを消したのか、なぜ消したのか、どんな代替案を検討したのかが揮発しやすいのです。そこで今回はKiro IDEのAI-DLC(AI-Driven Lifecycle) 方法論をそのまま被せ、すべての決定をドキュメント化し、承認ゲートを4回経由して進めました。
結果から先に示します。
| 指標 | 値 |
|---|---|
| ソースファイル数 | 302(main 301 + test 1) |
| 変更ファイル | pom.xml + Javaソース4つ + 新規 Dockerfile.build + 新規 Dockerfile.vnc |
| Javaバージョン | 1.8 → 21 |
| ビルド成果物 | target/Bytecode-Viewer-2.13.2.jar(97MB fat JAR) |
| ビルド環境 | Docker(maven:3.9-eclipse-temurin-21) |
| ビルド結果 | [DONE] SUCCESS — 302ファイルすべてコンパイル成功 |
Kiro IDE、AI-DLC、そしてSteering
Kiro IDE
Kiroはスペック駆動開発(Spec-Driven Development) を前提に作られたAI IDEです。今回の作業で活用した機能は4つです。
- Spec workflow — Requirements → Design → Tasksの3段階でスペックを作成
- Hooks — ファイル保存やツール呼び出し前後などのイベントにAIアクションを自動接続
- Steering — ワークスペース単位でAIに常時適用するルール・ガイドを注入
- MCP — Model Context Protocol。外部コンテキストサーバーとの連携
このケースの肝はSteering です。AI-DLCのルールをSteeringファイルに埋め込んでおけば、ユーザーが「始めてみて」と一言だけ投げても、エージェントがAI-DLCのフローに沿って段階ごとに動きます。
AI-DLCとは何か
AI-DLCは、AIエージェントがソフトウェアライフサイクル全体を段階的に進める一方で、各段階で人間の承認ゲートを必ず置く 方法論です。構造は3段階だけです。
┌───────────────────────────────────────────────────────┐
│ INCEPTION │
│ Workspace Detection → Reverse Engineering → │
│ Requirements → User Stories → Workflow Planning → │
│ Application Design → Units Generation │
├───────────────────────────────────────────────────────┤
│ CONSTRUCTION (per unit) │
│ Functional Design → NFR Req/Design → │
│ Infra Design → Code Generation → Build & Test │
├───────────────────────────────────────────────────────┤
│ OPERATIONS │
│ Deploy / Run / Monitor / Maintain │
└───────────────────────────────────────────────────────┘
各段階の成果物はaidlc-docs/配下にドキュメントとして固定 されます。aidlc-state.mdで現在の段階を追跡し、すべての意思決定はタイムスタンプとともにaudit.mdへ積み上げられます。ある段階をSKIPすることもできますが、SKIPしたという事実と根拠も必ず記録します。
GreenfieldとBrownfield
| 区分 | Greenfield | Brownfield |
|---|---|---|
| 定義 | 新規プロジェクト | 既存コードがあるプロジェクト |
| 必須段階 | 要件分析から開始 | Reverse Engineering必須 |
| 本ケース | [N] |
[Y] |
今回のケースは当然ブラウンフィールドです。Kiroは作業開始直後にワークスペースをスキャンし、pom.xmlを見つけてReverse Engineering段階を自動で組み込みました。
Kiro Steeringでルールをエージェントに強制する
Steeringファイルは.kiro/steering/aidlc-rules/*.mdに置きます。フロントマターでいつルールを注入するかを選びます。
inclusion: always— すべての会話に含める(今回のAI-DLCルール)inclusion: fileMatch+fileMatchPattern— 特定ファイルを開いたときのみ含めるinclusion: manual— ユーザーが#プロンプトで明示的に参照したときのみ含める
今回使ったルールから、要点だけを抜粋します。
ブラウンフィールドが検出されたら、まずReverse Engineeringを実行する。
各段階の完了後にユーザーへ承認を要求する。
すべての承認・却下は
aidlc-docs/audit.mdにタイムスタンプとともに記録する。ドキュメントは
aidlc-docs/の中に、コードはワークスペースのルートに置く(混在禁止)。
対象 — Bytecode ViewerがJava 8に縛られていた理由
このアプリのルートはsrc/main/java/the/bytecode/club/bytecodeviewer/で、その下にapi · bootloader · cli · compilers · decompilers · gui · malwarescanner · plugin · resources · searching · translation · utilのパッケージがあります。Swing GUIとCLIを同時に提供し、外部プラグインまで実行できる構造なので、依存関係が全方位に広がっています。
モダンJavaから見ると、障壁もはっきりしています。
| 課題 | 影響 |
|---|---|
SecurityManagerの使用(util/SecurityMan.java) |
Java 18で削除 |
| Nashorn JSエンジン(JSプラグイン対応) | Java 15で削除 |
| 古い依存(DarkLaf 3.0.2、JADX 1.4.7、RSyntaxTextArea 3.6.0) | 最新版はJava 11以上を要求 |
| テスト1件 | 自動検証に限界 |
| ローカルにJava/Mavenが未インストール | Dockerでのビルド環境構築が必要 |
実際の進行タイムライン
時間帯ごとの要約から見ていきます。すべての承認エントリはaudit.mdにそのまま残っています。
2026-05-12 18:22 "始めてみて" → Workspace Detection
2026-05-12 18:23 スキャン完了 → Reverse Engineering 完了
2026-05-12 18:33 質問書生成(requirement-verification-questions.md)
2026-05-12 19:04 ユーザー回答収集 → requirements.md 作成
2026-05-12 19:08 "承認" → Workflow Plan 承認
2026-05-12 23:45 "承認" → Code Generation Plan 承認
2026-05-12 23:57 "承認" → 実コード修正開始
2026-05-13 00:25 "承認" → Code Generation 完了承認
2026-05-13 00:47 Docker ビルド SUCCESS、97MB JAR 生成
flowchart TD
Start(["User: 始めてみて"])
subgraph INCEPTION["INCEPTION PHASE"]
WD["Workspace Detection [DONE]"]
RE["Reverse Engineering [DONE]"]
RA["Requirements Analysis [DONE]"]
WP["Workflow Planning [DONE]"]
end
subgraph CONSTRUCTION["CONSTRUCTION PHASE"]
CG["Code Generation (Plan + Execute) [DONE]"]
BT["Build and Test [DONE]"]
end
Start --> WD --> RE --> RA --> WP --> CG --> BT --> End(["Complete"])
User Stories · Application Design · Units Generation · Functional Design · NFR · Infrastructure Designの各段階は、理由を記録したうえでSKIP しました。根拠はaidlc-docs/inception/plans/execution-plan.mdに残っています。
段階別の記録 — 検出・リバースエンジニアリング・要件・計画
Workspace Detection — 事実だけを集める
最初のトリガーは本当に短いです。ユーザーは始めてみての一言だけです。AI-DLCの第一原則は、この段階で事実(Fact)だけを収集 し、意見や決定は次の段階に回すというものです。
- ルートのファイル一覧をなめて
pom.xmlの存在を確認 → Brownfieldと確定 src/main/java配下のファイル数をカウント → 302pom.xmlをパースし<java.version>1.8</java.version>を抽出- システムにJava/Mavenがインストールされているか確認 → 未インストール
この段階の成果物は2つだけです。aidlc-docs/aidlc-state.md(状態トラッキングの初期化)と、aidlc-docs/audit.mdの最初のエントリです。
Reverse Engineering — 以降すべての段階の根拠
ブラウンフィールド・プロジェクトではこの段階がすべてを左右します。Kiroはaidlc-docs/inception/reverse-engineering/配下に、次のドキュメントを一気に作成しました。
architecture.md # 階層/モジュール構造
business-overview.md # このアプリは何をするのか
technology-stack.md # Java 8, Maven, ASM, Swing, ...
component-inventory.md # 6 decompiler + 3 disassembler ...
dependencies.md # pom.xmlの依存関係 + リスクのコメント
code-structure.md # パッケージツリー、主要クラスの位置
reverse-engineering-timestamp.md
肝心なのは、これらのドキュメントが次の段階の入力 になるという点です。例えばtechnology-stack.mdで「Nashornエンジンの使用」が見つかると、要件質問書のQ3(「Nashornの代替策は?」)へ自動的に引き継がれます。
Requirements Analysis — 質問書でやり取りする
自動生成される質問書はaidlc-docs/inception/requirements/requirement-verification-questions.mdに出力されます。フォーマットはシンプルです。ガイド + 選択肢 + 空欄の [Answer]: です。ガイドには背景知識とトレードオフを入れて、ユーザーがなぜその選択肢を取るのかを自然と振り返るように仕向けます。
実際の回答結果はこのように整理されました。
| Q | テーマ | ユーザーの選択 |
|---|---|---|
| Q1 | Javaバージョン | B) Java 21(LTS) |
| Q2 | SecurityManagerの代替戦略 | C) ProcessBuilder分離 |
| Q3 | Nashornの代替 | A) GraalJS |
| Q4 | 依存アップグレードの範囲 | D) Java 21で最新、必要ならJava 17+へフォールバック |
| Q5 | セキュリティ規則の適用 | B) Skip(デスクトップアプリ) |
| Q6 | PBTの方針 | B) 部分的に採用 |
| Q7 | ワークフロー計画 | B) マイグレーション中心、Dockerビルド |
回答が揃うと、エージェントがrequirements.mdを書きます。注目すべきは質問そのものがドキュメントとして残る 点です。後日「なぜ21を選んだのか」を振り返るとき、質問書の[Answer]:がそのまま監査の根拠になります。
Workflow Planning — 実行とSKIPを併記する
成果物はaidlc-docs/inception/plans/execution-plan.mdです。
[DONE]Workspace Detection · Reverse Engineering · Requirements · Workflow Planning[DONE]Code Generation · Build & Test[SKIP]User Stories — マイグレーションのためユーザーシナリオがない[SKIP]Application Design — 既存アーキテクチャを維持[SKIP]Units Generation — 単一のモノリシックアプリ[SKIP]Functional Design / NFR / Infra — 機能・NFRの変更なし、インフラなし
ユーザーが承認を入力すると、audit.mdにエントリが追加され、Construction段階へ遷移します。
コード生成と実際の変更
まずプラン文書を作る
コードにすぐ手を入れることはしません。チェックリスト文書を先に作ります。成果物はaidlc-docs/construction/plans/bytecode-viewer-code-generation-plan.mdで、日本語要約版のbytecode-viewer-code-generation-plan-ko.md(韓国語版)を併設します。
pom.xml—java.version変更、依存アップグレード、GraalJSのコメント解除- SecurityManagerの除去 —
SecurityMan.java削除 + 参照3ファイルの整理 JavascriptPluginLaunchStrategy.java— プライマリエンジンをgraal.jsへ- Deprecated APIの点検 —
javap.Main、java.security.* Dockerfile.buildの作成(ビルド検証用)migration-summary.mdの作成
Plan → Approve → Executeの構造。 承認前は実ファイルに触りません。この原則1つを守るだけで、マイグレーション中の偶発的な副作用は大きく減らせます。
承認後に実際に行われた変更
pom.xml
<java.version>1.8</java.version>→<java.version>21</java.version>google-java-format: 1.7 → 1.25.2jadx: 1.4.7 → 1.5.1js.version: 21.2.0 → 24.1.1(後にビルド中に24.1.2へ調整)- GraalJSの依存をコメント解除(
org.graalvm.polyglot:js、org.graalvm.js:js-scriptengine)
Before(Java 8、Nashorn依存、GraalJSコメントアウト)
<java.version>1.8</java.version>
<google-java-format.version>1.7</google-java-format.version>
<jadx.version>1.4.7</jadx.version>
<js.version>21.2.0</js.version>
<!-- TODO Re-add for Graal.JS support -->
<!--<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>${js.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>${js.version}</version>
</dependency>-->
After(Java 21、GraalJS有効化)
<java.version>21</java.version>
<google-java-format.version>1.25.2</google-java-format.version>
<jadx.version>1.5.1</jadx.version>
<js.version>24.1.2</js.version>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>${js.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>${js.version}</version>
</dependency>
BytecodeViewer.java — SecurityManagerの除去
Before
public class BytecodeViewer {
// ...
public static SecurityMan sm = new SecurityMan(); // line 140
public static void main(String[] args) {
// ...
System.setSecurityManager(sm); // line 175
// ...
}
}
After
public class BytecodeViewer {
// ...
// SecurityManager removed — deprecated in Java 17, removed in Java 18.
// Plugin sandboxing migrated to ProcessBuilder-based isolation (see FR-02).
public static void main(String[] args) {
// ...
// No setSecurityManager call
// ...
}
}
これによりこのプロセスは、JVM内部に独自の権限モデルを持たなくなりました。代わりにプラグインを実行する際はProcessBuilderベースの分離 という経路を選び、分離ロジック自体は後続のFR-02作業として切り出しました。1次PRでは「とにかくコンパイルと既存機能を維持する」ことに集中しています。
JavascriptPluginLaunchStrategy.java — NashornからGraalJSへ
// Before
private static final String FIRST_PICK_ENGINE = "nashorn";
private static final String FALLBACK_ENGINE = "graal.js";
// After
private static final String FIRST_PICK_ENGINE = "graal.js";
private static final String FALLBACK_ENGINE = "js";
// GraalJS polyglot バインディング
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup",
(Predicate<String>) s -> true);
削除・維持・新規ファイルの整理
- 削除 —
src/main/java/.../util/SecurityMan.java - 修正 —
BytecodeViewer.java、EZInjection.java、JavapDisassembler.java、JavascriptPluginLaunchStrategy.java - 維持 —
NullSecurityManagerScanner.java(自前のSecurityManager除去とは無関係で、解析対象のバイトコード から悪性パターンを探すマルウェアスキャナです) - 新規 —
Dockerfile.build(マルチステージ、maven:3.9-eclipse-temurin-21)
DockerベースのビルドとVNCによるGUI実測
再現可能なビルド — Dockerfile.build
私たちのビルドマシンにはJavaもMavenも入っていません。代わりにDockerイメージが1つあれば、誰でも同じ結果を出せます。
# Stage 1: build
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
COPY libs ./libs
RUN mvn -q -DskipTests package
# Stage 2: runtime (image only carries the JAR)
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/Bytecode-Viewer-2.13.2.jar ./bcv.jar
ENTRYPOINT ["java", "-jar", "bcv.jar"]
この1ファイルで期待される結果はシンプルです。
- 302個のJavaファイルがコンパイル成功
- 97MB Fat JARを生成(shadeプラグイン)
- ユニットテストは事実上なし → 手動の機能検証チェックリストで補完
ビルド中に解決した問題。 GraalJSのバージョンを24.1.1から24.1.2へ調整し、アーティファクトIDも
org.graalvm.js:jsからorg.graalvm.polyglot:js(POM type)へ変更する必要がありました。Javaマイグレーションでは*「どのバージョンを定数として固定するか」はビルド前に断定できない* という教訓がここでも出ました。
GUIを実際に立ち上げて初めて完了する — Dockerfile.vnc
Bytecode ViewerはSwing GUIアプリです。mvn packageが通っても「マイグレーション OK」にはなりません。サーバー環境にはX serverもないため、仮想ディスプレイを作りVNC越しに穴を開けて実際にウィンドウを起動する必要があります。Xvfb(仮想ディスプレイ) + fluxbox(軽量WM) + x11vnc(リモートアクセス)の組み合わせを使います。
FROM eclipse-temurin:21-jdk
RUN apt-get update && apt-get install -y --no-install-recommends \
xvfb x11vnc fluxbox xterm \
libxrender1 libxtst6 libxi6 libxext6 libx11-6 \
&& rm -rf /var/lib/apt/lists/*
ENV DISPLAY=:0
COPY target/Bytecode-Viewer-2.13.2.jar /app/bytecode-viewer.jar
EXPOSE 5900
CMD Xvfb :0 -screen 0 1280x800x24 & \
sleep 1 && \
fluxbox & \
x11vnc -display :0 -forever -passwd test1234 -rfbport 5900 & \
sleep 2 && \
java -jar /app/bytecode-viewer.jar
| パッケージ | 役割 |
|---|---|
xvfb |
ヘッドレスサーバーでも動作する仮想Xディスプレイ |
fluxbox |
ウィンドウマネージャ(WM)。Swingウィンドウにタイトルバーやリサイズを提供 |
x11vnc |
仮想ディスプレイをVNCプロトコルで公開(ポート5900) |
libxrender1、libxtst6、libxi6、libxext6、libx11-6 |
Swing/AWTが依存するXランタイムライブラリ |
イメージビルドとコンテナ起動
# 1) GUI用Dockerイメージのビルド
cd bytecode-viewer
docker build -f Dockerfile.vnc -t bcv-vnc . 2>&1 | tail -10
# 2) コンテナ起動(バックグラウンド、ポート5900をマッピング)
docker run -d --name bcv-test -p 5900:5900 bcv-vnc
# 3) 起動ログの確認
sleep 5 && docker logs bcv-test 2>&1 | tail -15
# 4) テスト終了後のクリーンアップ
docker stop bcv-test && docker rm bcv-test
実際の実行ログ
[engine] WARNING: The polyglot engine uses a fallback runtime that does not
support runtime compilation to native code. Execution without runtime
compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM.
Enable JVMCI using -XX:+EnableJVMCI.
(...中略...)
Extracting Krakatau
Start up took 1 seconds
Failed to read: session.screen0.titlebar.left
Setting default value
Failed to read: session.screen0.titlebar.right
Setting default value
Successfully extracted Krakatau
Extracting Enjarify
ログの読み方は次のとおりです。
- GraalJSの警告 — JVMCIが無効である旨の情報メッセージです。起動には影響しません。さらに静かにしたければ、JVM引数に
-Dpolyglot.engine.WarnInterpreterOnly=falseを付与します。 - Extracting Krakatau / Enjarify — BCVが同梱するサブツールを展開している正常な動作です。Java 21でもそのまま動きます。
- fluxbox
Failed to read ...— 軽量WMの既定テーマに関する無害な警告です。
WindowsからVNCで接続する
- VNCクライアントをインストール — RealVNC ViewerまたはTightVNC Viewer
- 接続情報 —
<サーバーIP>:5900、パスワードtest1234 [NOTE]Windows標準のリモートデスクトップ(RDP)はVNCプロトコルではないため使用できません。
GUI実測結果(スクリーンショット)



確認項目をチェックリストとして残します。
[DONE]GUIメインウィンドウが正常にレンダリング(Swing + DarkLaf)[DONE]メニューバー・ツールバー・ステータスバーの正常表示[DONE]デコンパイラタブ(6種)の読み込み可能[DONE]GraalJSエンジン初期化に警告のみ、クラッシュなし[DONE]Krakatau・Enjarifyサブツールの展開が正常[TODO](手動)実際のJARをドラッグ&ドロップして各デコンパイラの結果を比較 — 後続ステップで実施
このテストが監査ログに残る形
## Build and Test - GUI Verification (VNC)
Timestamp : 2026-05-13T00:53:00Z
Artifact : target/Bytecode-Viewer-2.13.2.jar (97MB)
Image : bcv-vnc:latest (eclipse-temurin:21-jdk + Xvfb + fluxbox + x11vnc)
Container : bcv-test (-p 5900:5900)
Result : PASS — GUI started on Java 21, Krakatau/Enjarify extraction OK
Warnings : GraalJS "JVMCI not enabled" (informational)
Screenshots: mydoc/bytecode-viewer-main-java21.png, mydoc/bytecode-viewer-decompiler-view.png, mydoc/bytecode-viewer-sample-loaded.png
要点はシンプルです。マイグレーションの検証は**mvn package OK** とランタイム起動 OK の2軸に分けるべきで、GUIアプリならVNC/Xvfbの組み合わせでCIやリモート環境でも実測できます。ログとスクリーンショットのパスまでaudit.mdに固定しておけば、ずっと後で見直してもそのまま再現できます。
成果物の構造と読む順序
aidlc-docs/
├── aidlc-state.md # 現在のAI-DLC進行状況(チェックボックス)
├── audit.md # すべての承認・意思決定の監査ログ
├── inception/
│ ├── plans/
│ │ └── execution-plan.md # 実行・スキップする段階の決定
│ ├── requirements/
│ │ ├── requirement-verification-questions.md # 質問書
│ │ ├── requirement-verification-questions-ko.md # 韓国語版
│ │ └── requirements.md # 最終要件
│ └── reverse-engineering/
│ ├── architecture.md
│ ├── business-overview.md
│ ├── technology-stack.md
│ ├── component-inventory.md
│ ├── dependencies.md
│ ├── code-structure.md
│ └── reverse-engineering-timestamp.md
└── construction/
├── plans/
│ ├── bytecode-viewer-code-generation-plan.md
│ └── bytecode-viewer-code-generation-plan-ko.md
├── bytecode-viewer/
│ └── code/
│ └── migration-summary.md # 実変更内容の要約
└── build-and-test/
└── build-and-test-summary.md # Dockerビルド・検証結果
初めてこのプロジェクトを受け取る人には、以下の順序を推奨します。
aidlc-state.md— 今どこまで進んでいるかreverse-engineering/business-overview.md— このアプリは何かrequirements/requirements.md— 何を達成すべきかinception/plans/execution-plan.md— どの道を行くかconstruction/plans/bytecode-viewer-code-generation-plan.md— 具体的な修正ポイントconstruction/bytecode-viewer/code/migration-summary.md— 実際に変更したものconstruction/build-and-test/build-and-test-summary.md— どう確認したかaudit.md— どんな意思決定があったか(監査用)
Steering・Hooksで自分のプロジェクトへ移植する
新しいプロジェクトにAI-DLCを乗せる5ステップ
- Steeringの設置 —
.kiro/steering/aidlc-rules/にAI-DLCルールファイルを配置 - ステートファイルの準備 —
aidlc-docs/aidlc-state.mdを初期化 - 監査ログの準備 —
aidlc-docs/audit.mdを空ファイルとして作成 - トリガー — ユーザーは「始めてみて」または「Start AI-DLC」の一言
- 承認ループ — エージェントが段階ごとに成果物を作る → レビュー → 承認・却下 → 次の段階
Steeringファイルの例(簡略版)
.kiro/steering/aidlc-rules/00-aidlc.md
---
inclusion: always
---
# AI-DLC Rules
1. Code lives at the workspace root.
Documentation lives only under `aidlc-docs/`. Never mix them.
2. On every turn, consult `aidlc-docs/aidlc-state.md` to determine the
current stage. If the file is missing, treat it as a new project.
3. If a `pom.xml`, `package.json`, `Cargo.toml`, etc. is present,
the project is **Brownfield** — run Reverse Engineering before
anything else.
4. Each stage produces files in a fixed location
(see `docs/GENERATED_DOCS_REFERENCE.md`).
5. Every user decision must be appended to `aidlc-docs/audit.md`
with an ISO-8601 timestamp.
6. Skipping a stage is allowed but must record a `Rationale:` line.
7. Before executing Code Generation, produce a plan document and
ask for explicit user approval.
.kiro/steering/aidlc-rules/10-brownfield.md
---
inclusion: fileMatch
fileMatchPattern: "**/pom.xml"
---
# Brownfield Expansion
When a `pom.xml` file is detected:
- Record the Maven `groupId`, `artifactId`, `version`, and `java.version`.
- Enumerate top-level dependencies and flag those with known EOL
(e.g. Nashorn, SecurityManager based APIs) in
`aidlc-docs/inception/reverse-engineering/dependencies.md`.
Hooksで反復作業を自動化する
Kiro Hooksを使うと、特定のイベントにAIアクションを自動で結び付けられます。
pom.xmlが保存されるたびに依存ツリーを再ドキュメント化
{
"name": "Refresh dependency doc",
"version": "1.0.0",
"when": {
"type": "fileEdited",
"patterns": ["pom.xml"]
},
"then": {
"type": "askAgent",
"prompt": "pom.xmlが変更されました。aidlc-docs/inception/reverse-engineering/dependencies.mdを最新化してください。"
}
}
Specタスクが完了するたびにmvn testを実行
{
"name": "Verify after task",
"version": "1.0.0",
"when": { "type": "postTaskExecution" },
"then": {
"type": "runCommand",
"command": "docker run --rm -v $(pwd):/app -w /app maven:3.9-eclipse-temurin-21 mvn test -q"
}
}
今回のケースでAI-DLCがうまく機能した理由
- 承認ゲートが4回 ありました。質問書 → 要件 → ワークフロー計画 → コード生成計画 → 実コード生成という流れで、ユーザーは決定ポイントだけ確認しました。
- 「まずドキュメント」 という原則のおかげで、GraalJSのアーティファクトIDのような細部をビルド前に明文化でき、ビルド中の修正もスムーズでした。
- 監査ログ が残るため、監査・レビュー・ロールバックの根拠が自然に確保されました。
私たちが得た教訓
- Plan → Approve → Executeを省略しないこと。 どんなに「シンプルな変更」でも、
pom.xml一つからGraalJSのartifactId不一致といった問題が現れます。 - Deprecated APIの除去は連鎖する。
SecurityManを1つ消すだけで、BytecodeViewer・EZInjection・JavapDisassemblerまで芋づる式に修正が必要になります。プラン文書に「Related files to touch」をあらかじめ記しておくと被害を抑えられます。 - Dockerビルドを1次検証手段に。 ローカルにJava/Mavenがなくても再現可能なビルドが手に入ります。エージェントのループに
mvn package -qの成功可否を組み込むと、回帰防止に効果的です。 - テストが乏しいときは手動検証を成果物にする。
build-and-test-summary.mdにGUIの手動テストチェックリストをまとめておくだけで、次の人が迷いません。
実務チェックリスト
[CHECK].kiro/steering/にAI-DLCルールファイルがある[CHECK]aidlc-docs/aidlc-state.mdに現在の段階が表示されている[CHECK]承認・却下はaidlc-docs/audit.mdにのみ残す[CHECK]ブラウンフィールドならreverse-engineering/のドキュメントが揃っている[CHECK]requirements.mdがユーザーの選択肢回答に基づいて構成されている[CHECK]execution-plan.mdにSKIP理由が明記されている[CHECK]Code Generationはプラン文書 から始めた[CHECK]ビルド検証はDockerまたは同等のイメージ・バージョンで再現可能
付録A. 用語集
| 用語 | 説明 |
|---|---|
| AI-DLC | AI-Driven Lifecycle。AIエージェントがソフトウェアライフサイクル全体を段階的に進める方法論 |
| Brownfield | 既存コードの上で作業するプロジェクト |
| Greenfield | 新規に始めるプロジェクト |
| Inception Phase | 要件・設計・計画を確定する上流の段階 |
| Construction Phase | 実装と検証を行う段階 |
| Steering | Kiroの常時適用ルールファイル(.kiro/steering/**/*.md) |
| Hook | ファイル保存やタスク完了などのイベントでAIアクションを自動トリガー |
| MCP | Model Context Protocol。外部コンテキストサーバーとの連携規格 |
| Fat JAR | すべての依存をまとめた実行可能JAR(shadeプラグイン) |
| Nashorn | JDKに同梱されていたJavaScriptエンジン。Java 15で削除 |
| GraalJS | Nashornの後継JSエンジン。Java 21と互換 |
| SecurityManager | パーミッションのサンドボックス。Java 17で非推奨、Java 18で削除 |
| ProcessBuilder | 別のJVMプロセスとしてプラグインを分離実行 |
| PBT | Property-Based Testing。ランダム入力でエッジケースを検出 |
付録B. コマンドチートシート
# 1) ローカルにJavaがなくてもコンパイル確認
docker run --rm -v "$(pwd)":/app -w /app \
maven:3.9-eclipse-temurin-21 mvn compile -q
# 2) Fat JARのパッケージング
docker run --rm -v "$(pwd)":/app -w /app \
maven:3.9-eclipse-temurin-21 mvn package -DskipTests -q
# 3) ビルド済みJARの実行(Java 21)
java -jar target/Bytecode-Viewer-2.13.2.jar
# 4) CLIモード(回帰検証用)
java -jar target/Bytecode-Viewer-2.13.2.jar \
-i test.jar -o output.java \
-decompiler cfr -t all
# 5) Dockerfile.buildベースのイメージビルド
cd bytecode-viewer
docker build -f Dockerfile.build -t bcv:java21 .
# 6) GUI実測用VNCイメージのビルドと実行
cd bytecode-viewer
docker build -f Dockerfile.vnc -t bcv-vnc .
docker run -d --name bcv-test -p 5900:5900 bcv-vnc
docker logs bcv-test | tail -20 # 起動ログの確認
# VNCクライアントで<server>:5900に接続、パスワードtest1234
docker stop bcv-test && docker rm bcv-test # テスト終了後のクリーンアップ
まとめ
このケースで得た一行のまとめはこうです。AI-DLCは「AIに任せてみる」ではなく、「事実収集 → 意思決定 → 計画 → 実行 → 検証 → 監査ログ」という古典的なエンジニアリングサイクルをドキュメントで強制する道具です。 AIは断定的に成果物を出し、人間は短く決定だけを下します。その間に承認ゲートと監査ログが挟まることで、小さく退屈に見えた決定も、後から必ず辿れる場所に残ります。
次回はこのフローの上にProperty-Based Testing を載せ、ProcessBuilderベースのプラグイン分離実装における盲点を自動で見つける実験を共有する予定です。今まさにJava 8のレガシーコードを前にため息をついている方がいらしたら、小さなモジュール一つからでもこのフローに乗せてみることをお勧めします。最初の一歩は本当にシンプルです。始めてみて。