# AI 单测插件完整 UI 优化实施计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 将 AI 单测插件改为四个主 Tab 的低密度工作台,并提供目录选择、准确的模块反馈和只在主动任务期间出现的全局进度。 **Architecture:** `AiTestPane` 保留工作流编排,但把可独立验证的行为下沉到 `DirectoryPathSelector`、`TaskProgressState` 和现有 `StartModuleSelector`。项目加载继续使用 `LatestRequestGate`,Git 分析继续传递显式 `baseline` 与 `targetRef`。 **Tech Stack:** Java 17、JavaFX 17、Spring Boot 2.7、Maven、JUnit 5 ## Global Constraints - 主页面固定为 `项目与增量`、`工具链`、`质量规范`、`结果与合并` 四个不可关闭 Tab,不再使用 Accordion。 - JDK 8 Home 与 Maven Home 必须通过目录选择器选择,路径框只读。 - 基线默认 `master`,目标默认 `HEAD`;不得破坏任意 `baseline...targetRef` 分析能力。 - 项目加载不得进入全局 busy,较早异步结果不得覆盖最新选择。 - 空闲时全局进度条隐藏并重置;仅用户主动的分析、生成、验证、合并和工具链检查显示。 - 真实 `ft-loancapital-core` 必须继续检出 `ft-loancapital-core-start`。 - 不触碰现有未跟踪的审计文档、截图目录和 SQL 文件。 - 全程单线程实施,不使用子代理。 --- ### Task 1: 可测试的目录选择组件 **Files:** - Create: `src/main/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelector.java` - Test: `src/test/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelectorTest.java` **Interfaces:** - Produces: `DirectoryPathSelector(String label, Supplier ownerSupplier)` - Produces: `Path getPath()`、`void setPath(Path)`、`TextField pathField()`、`Button chooseButton()` - Internal test seam: package-private `void applySelection(File selected)`,`null` 表示取消。 - [ ] **Step 1: 写失败测试** ```java @Test void pathIsReadOnlyAndCancelKeepsExistingValue() throws Exception { DirectoryPathSelector selector = FxTestSupport.fx( () -> new DirectoryPathSelector("JDK 8 Home", () -> null)); FxTestSupport.fx(() -> selector.setPath(Path.of("C:/jdk8"))); FxTestSupport.fx(() -> selector.applySelection(null)); assertFalse(FxTestSupport.fx(() -> selector.pathField().isEditable())); assertEquals(Path.of("C:/jdk8"), FxTestSupport.fx(selector::getPath)); } @Test void selectedDirectoryUpdatesValue() throws Exception { DirectoryPathSelector selector = FxTestSupport.fx( () -> new DirectoryPathSelector("Maven Home", () -> null)); FxTestSupport.fx(() -> selector.applySelection(new File("C:/maven"))); assertEquals(Path.of("C:/maven"), FxTestSupport.fx(selector::getPath)); } ``` - [ ] **Step 2: 运行 RED** Run: `mvn -Dtest=DirectoryPathSelectorTest test` Expected: FAIL,因为 `DirectoryPathSelector` 尚不存在。 - [ ] **Step 3: 实现组件** 组件继承 `HBox`,内部包含只读 `TextField` 与文字按钮“选择目录”。按钮创建 `DirectoryChooser`;初始目录仅在当前路径存在且为目录时设置。`applySelection(null)` 不改变路径。 - [ ] **Step 4: 运行 GREEN** Run: `mvn -Dtest=DirectoryPathSelectorTest test` Expected: 2 tests,0 failures。 - [ ] **Step 5: 提交** ```bash git add src/main/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelector.java src/test/java/com/example/workutils/biz/aitest/ui/DirectoryPathSelectorTest.java git commit -m "feat: add directory selectors for AI test toolchain" ``` ### Task 2: 进度状态与模块展示策略 **Files:** - Create: `src/main/java/com/example/workutils/biz/aitest/ui/TaskProgressState.java` - Create: `src/main/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentation.java` - Test: `src/test/java/com/example/workutils/biz/aitest/ui/TaskProgressStateTest.java` - Test: `src/test/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentationTest.java` **Interfaces:** - Produces: `TaskProgressState.start()`、`update(double)`、`finish()`,以及 `visible()`、`progress()`。 - Consumes: `StartModuleSelector.autoSelect(MavenProjectModel)`。 - Produces: `ModuleLoadPresentation.from(MavenProjectModel, String preferredArtifactId)`,返回 `moduleCount()`、`selectedModule()` 与 `message()`。 - [ ] **Step 1: 写失败测试** ```java @Test void taskProgressIsHiddenUntilStartedAndResetsWhenFinished() { TaskProgressState state = new TaskProgressState(); assertFalse(state.visible()); state.start(); assertTrue(state.visible()); state.update(0.5); assertEquals(0.5, state.progress()); state.finish(); assertFalse(state.visible()); assertEquals(0.0, state.progress()); } @Test void uniqueStartModuleIsSelectedAndCountIsReported() { ModuleLoadPresentation view = ModuleLoadPresentation.from(model("core", "app-start"), ""); assertEquals(2, view.moduleCount()); assertEquals("app-start", view.selectedModule().orElseThrow().artifactId()); assertEquals("已检出 2 个 Maven 模块,已选择 app-start", view.message()); } @Test void multipleStartModulesRequireManualSelection() { ModuleLoadPresentation view = ModuleLoadPresentation.from(model("a-start", "b-start"), ""); assertTrue(view.selectedModule().isEmpty()); assertEquals("已检出 2 个 Maven 模块,请选择生成模块", view.message()); } ``` - [ ] **Step 2: 运行 RED** Run: `mvn -Dtest=TaskProgressStateTest,ModuleLoadPresentationTest test` Expected: FAIL,因为两个状态类型尚不存在。 - [ ] **Step 3: 实现最小状态类型** `TaskProgressState` 将进度约束在 `0..1`;`finish()` 固定恢复隐藏和 0。`ModuleLoadPresentation` 优先使用仍存在的用户偏好,否则调用 `StartModuleSelector`,并生成包含模块数量的中文状态。 - [ ] **Step 4: 运行 GREEN** Run: `mvn -Dtest=TaskProgressStateTest,ModuleLoadPresentationTest,StartModuleSelectorTest test` Expected: 全部通过。 - [ ] **Step 5: 提交** ```bash git add src/main/java/com/example/workutils/biz/aitest/ui/TaskProgressState.java src/main/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentation.java src/test/java/com/example/workutils/biz/aitest/ui/TaskProgressStateTest.java src/test/java/com/example/workutils/biz/aitest/project/ModuleLoadPresentationTest.java git commit -m "feat: model AI test progress and module loading states" ``` ### Task 3: 四 Tab 页面与低密度表单 **Files:** - Modify: `src/main/java/com/example/workutils/biz/aitest/AiTestPane.java` - Replace: `src/test/java/com/example/workutils/biz/aitest/AiTestPaneArchitectureTest.java` **Interfaces:** - Consumes: `DirectoryPathSelector`、`TaskProgressState`、`ModuleLoadPresentation`。 - Preserves: `AiTestRequest(project, module, baseline, targetRef, includeWorkingTree, settings)`。 - [ ] **Step 1: 写失败的 JavaFX 结构测试** 测试通过真实 `AiTestPane` 的节点树断言:中心是含四个指定 Tab 的 `TabPane`;不存在 `Accordion`;JDK/Maven 选择器路径框不可编辑;初始进度条不可见;基线和目标值分别为 `master` 与 `HEAD`。依赖使用 Mockito 返回空项目和空历史,测试只断言真实控件行为,不检查源码字符串。 - [ ] **Step 2: 运行 RED** Run: `mvn -Dtest=AiTestPaneArchitectureTest test` Expected: FAIL,因为中心仍是包含 Accordion 的 VBox,且进度条初始可见。 - [ ] **Step 3: 重构 `buildContent()`** 创建四个不可关闭主 Tab:项目页放项目、模块、baseline、targetRef、工作区选项和模块状态;工具链页放两个目录组件、CodeFree、覆盖率、修复轮次和检查按钮;质量规范页放左右 SplitPane;结果页放任务按钮、结果二级 Tab 与审核合并卡片。 - [ ] **Step 4: 接入目录组件** 删除 `jdk8Field`、`mavenField`,`settingsFromForm()` 改读 `jdkSelector.getPath()` 与 `mavenSelector.getPath()`;`applySettings()` 改调 `setPath()`。 - [ ] **Step 5: 接入模块展示** `applyProjectData()` 使用 `ModuleLoadPresentation.from(...)`,一次性更新模块、选中值和状态文案;继续在成功和失败回调前检查 `projectLoadGate.isLatest(projectRequest)`。 - [ ] **Step 6: 接入进度展示** 初始配置加载、项目加载和模块偏好保存使用静默后台任务;`runAsync` 仅供用户主动任务。`setBusy(true)` 显示进度,`setBusy(false)` 隐藏并重置。项目页状态展示项目加载,不触发全局进度。 - [ ] **Step 7: 运行 GREEN** Run: `mvn -Dtest=AiTestPaneArchitectureTest,LatestRequestGateTest,DirectoryPathSelectorTest,TaskProgressStateTest,ModuleLoadPresentationTest test` Expected: 全部通过。 - [ ] **Step 8: 提交** ```bash git add src/main/java/com/example/workutils/biz/aitest/AiTestPane.java src/test/java/com/example/workutils/biz/aitest/AiTestPaneArchitectureTest.java git commit -m "feat: reorganize AI test plugin into four tabs" ``` ### Task 4: 回归保护与最终验证 **Files:** - Modify only if a regression is exposed: files already listed in Tasks 1-3 - Verify: `src/test/java/com/example/workutils/biz/aitest/project/FtLoanCapitalCoreRegressionTest.java` - Verify: `src/test/java/com/example/workutils/biz/aitest/git/GitIncrementAnalyzerTest.java` **Interfaces:** - Preserves: `master...DEV_1156339215001460262` real-repository regression. - Preserves: latest project request wins. - [ ] **Step 1: 运行 AI 插件测试** Run: `mvn -Dtest='com.example.workutils.biz.aitest.**' test` Expected: 0 failures;真实仓库存在时 `FtLoanCapitalCoreRegressionTest` 不跳过。 - [ ] **Step 2: 运行全量测试** Run: `mvn test` Expected: 0 failures,0 errors;仅允许既有无显示环境 GUI 测试跳过。 - [ ] **Step 3: 打包** Run: `mvn -DskipTests package` Expected: `target/workutils-1.0.1-SNAPSHOT.jar` 生成成功。 - [ ] **Step 4: 差异和工作树检查** Run: `git diff --check` 与 `git status --short` Expected: 无空白错误;用户原有审计文档、截图目录和 SQL 仍保持未跟踪且未暂存。 - [ ] **Step 5: 提交验证修正(仅当 Step 1-4 暴露并修复问题时)** ```bash git add src/main/java/com/example/workutils/biz/aitest src/test/java/com/example/workutils/biz/aitest git commit -m "fix: complete AI test plugin UI regression coverage" ```