Files
yidaima/RuoYi-Vue/docs/superpowers/plans/2026-07-07-project-code-analysis.md

582 lines
23 KiB
Markdown
Raw Normal View History

2026-07-07 16:12:42 +08:00
# Project Code Analysis Implementation Plan
> **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:** Add project-bound code analysis that reads the current generated project source, calls the existing AI task service, saves history, and supports continue generation.
**Architecture:** Reuse the existing EasyCode front workbench, preview source service, AI async task table, and generation history table. Add a small backend code-analysis layer that builds a safe source summary from preview output and exposes semantic endpoints; add a Vue page that starts tasks, polls status, shows history, and renders/export reports.
**Tech Stack:** Spring Boot, MyBatis XML, existing DeepSeek AI service integration, Vue 3, Element Plus, node:test source assertions.
---
## File Map
- `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/GenerateCodeAnalysisRequest.java`: request fields for fresh and continue generation.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisResponse.java`: AI result payload and source snapshot metadata.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisSourceSummary.java`: safe code snapshot summary used by UI and prompt.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisHistoryResponse.java`: history item wrapper with parsed response.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisService.java`: collect preview source, filter files, build prompt context, list and load history.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IAiGenerateService.java`: add `generateCodeAnalysis`.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java`: build code-analysis prompt and parse AI response.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskServiceImpl.java`: allow `code_analysis`.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskWorker.java`: execute `code_analysis` tasks.
- `ruoyi-generator/src/main/resources/mapper/front/FrontProjectGenerationMapper.xml`: add user/project scoped generation lookup.
- `ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front/FrontProjectGenerationMapper.java`: expose lookup method.
- `ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java`: add code-analysis semantic endpoints.
- `easycode-web/src/api/project.js`: add code-analysis API wrappers.
- `easycode-web/src/router/index.js`: add `/project/:projectId/code-analysis`.
- `easycode-web/src/views/CodeAnalysisView.vue`: page UI, history, task polling, continue generation, report export.
- `easycode-web/src/views/codeAnalysisView.test.mjs`: front-end source assertions.
- `easycode-web/src/views/ProjectListView.vue`, `PreviewView.vue`, `GenerateView.vue`: add project-level entry buttons.
- Relevant backend tests under `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/` and `ruoyi-admin/src/test/java/com/ruoyi/web/controller/front/`.
---
### Task 1: Backend DTOs and Source Summary Service
**Files:**
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/GenerateCodeAnalysisRequest.java`
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisResponse.java`
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisSourceSummary.java`
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisService.java`
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisServiceTest.java`
- [ ] **Step 1: Write failing source summary tests**
Create `ProjectCodeAnalysisServiceTest.java` with tests that use a fake `IFrontProjectPreviewService` returning nested nodes and file contents. Include these behaviors:
```java
@Test
public void shouldCollectAllowedPreviewFilesAndSkipBuildArtifacts() {
ProjectCodeAnalysisService service = newServiceWithPreview(
node("backend:src/main/java/com/example/UserController.java", "UserController.java", "file"),
node("backend:target/classes/App.class", "App.class", "file"),
node("frontend:node_modules/vue/index.js", "index.js", "file"));
CodeAnalysisSourceSummary summary = service.buildSourceSummary(1L, 10L);
assertEquals(1, summary.getAnalyzedFileCount());
assertTrue(summary.getIncludedFiles().get(0).getPath().contains("UserController.java"));
assertTrue(summary.getSkippedFiles().stream().anyMatch(file -> file.getReason().contains("构建产物")));
}
@Test
public void shouldTrimLargeFilesAndKeepSummaryUsable() {
String large = repeat("public void method() {}\n", 7000);
ProjectCodeAnalysisService service = newServiceWithContent("backend:src/main/java/App.java", large);
CodeAnalysisSourceSummary summary = service.buildSourceSummary(1L, 10L);
assertEquals(1, summary.getAnalyzedFileCount());
assertTrue(summary.getIncludedFiles().get(0).getContent().length() < large.length());
assertTrue(summary.getWarnings().stream().anyMatch(value -> value.contains("超过 100KB")));
}
```
- [ ] **Step 2: Run tests and verify RED**
Run: `mvn -pl ruoyi-generator -Dtest=ProjectCodeAnalysisServiceTest test`
Expected: compile fails because `ProjectCodeAnalysisService` and DTO classes do not exist.
- [ ] **Step 3: Add DTOs**
Implement DTOs with JavaBean getters/setters:
```java
public class GenerateCodeAnalysisRequest {
private String mode;
private Long previousGenerationId;
private String previousMarkdown;
private String extraRequirements;
// getters and setters
}
```
`CodeAnalysisResponse` fields: `title`, `markdown`, `sections`, `sourceSnapshot`, `warnings`.
`CodeAnalysisSourceSummary` fields: `projectId`, `fileCount`, `analyzedFileCount`, `skippedFileCount`, `charCount`, `estimatedTokens`, `includedFiles`, `skippedFiles`, `warnings`, `promptContext`. Add nested static classes `SourceFile` and `SkippedFile`.
- [ ] **Step 4: Implement minimal source summary service**
`ProjectCodeAnalysisService.buildSourceSummary(userId, projectId)` should:
- call `frontProjectPreviewService.getStructure` for `backend`, `frontend`, `admin_frontend`, `sql`;
- walk `children`;
- include only configured code extensions;
- skip `node_modules`, `target`, `dist`, `.git`, `.idea`;
- create `PreviewFileRequest` with `templateType`, `nodeId`, `tableId`, `category`;
- read `content` from `frontProjectPreviewService.getFileContent`;
- trim content above `100 * 1024` characters;
- build `promptContext` from file path headings and content.
- [ ] **Step 5: Run tests and verify GREEN**
Run: `mvn -pl ruoyi-generator -Dtest=ProjectCodeAnalysisServiceTest test`
Expected: tests pass.
- [ ] **Step 6: Commit**
```bash
git add ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/GenerateCodeAnalysisRequest.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisResponse.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisSourceSummary.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisService.java \
ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisServiceTest.java
git commit -m "feat: collect project source for code analysis"
```
---
### Task 2: AI Task Type and Generation
**Files:**
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IAiGenerateService.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskServiceImpl.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskWorker.java`
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerationTaskServiceImplTest.java`
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerationTaskWorkerTest.java`
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerateServiceImplTest.java`
- [ ] **Step 1: Write failing task acceptance tests**
Add assertions that `code_analysis` is accepted:
```java
@Test
public void createTaskShouldAcceptCodeAnalysisType() {
AiGenerationTaskCreateRequest request = new AiGenerationTaskCreateRequest();
request.setGenerateType("code_analysis");
AiGenerationTaskStatusResponse response = service.createTask(1L, 100L, request);
assertEquals("code_analysis", response.getGenerateType());
assertEquals("QUEUED", response.getStatus());
}
```
Add worker test verifying dispatch calls `generateCodeAnalysis`:
```java
@Test
public void workerShouldExecuteCodeAnalysisTask() {
AiGenerationTask task = queuedTask("code_analysis");
mapper.insertAiGenerationTask(task);
worker.processTask(task.getTaskId());
assertTrue(aiGenerateService.codeAnalysisCalled);
assertEquals("SUCCEEDED", mapper.selectAiGenerationTaskById(task.getTaskId()).getStatus());
}
```
- [ ] **Step 2: Run tests and verify RED**
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest,AiGenerationTaskWorkerTest test`
Expected: unsupported generate type or missing interface method.
- [ ] **Step 3: Add interface and task dispatch**
Add to `IAiGenerateService`:
```java
CodeAnalysisResponse generateCodeAnalysis(Long userId, Long projectId, GenerateCodeAnalysisRequest request);
```
Allow `code_analysis` in `AiGenerationTaskServiceImpl.validateRequest()`.
In `AiGenerationTaskWorker.execute()` add:
```java
if ("code_analysis".equals(task.getGenerateType())) {
return aiGenerateService.generateCodeAnalysis(task.getUserId(), task.getProjectId(), toCodeAnalysisRequest(request));
}
```
Add `toCodeAnalysisRequest()` mapping `mode`, `previousGenerationId`, `previousMarkdown`, `extraRequirements`.
- [ ] **Step 4: Add prompt generation test**
In `AiGenerateServiceImplTest`, assert prompt contains fixed sections and source context:
```java
@Test
public void codeAnalysisPromptShouldRequireFixedSectionsAndUseSourceSummary() {
CodeAnalysisResponse response = service.generateCodeAnalysis(1L, 100L, new GenerateCodeAnalysisRequest());
String prompt = deepSeekClient.lastPrompt();
assertTrue(prompt.contains("项目概述与技术栈"));
assertTrue(prompt.contains("系统架构与核心模块"));
assertTrue(prompt.contains("核心业务流程"));
assertTrue(prompt.contains("数据库设计"));
assertTrue(prompt.contains("答辩准备"));
assertTrue(prompt.contains("不要编造未出现的模块"));
}
```
- [ ] **Step 5: Implement AI generation method**
In `AiGenerateServiceImpl.generateCodeAnalysis()`:
- call `projectCodeAnalysisService.buildSourceSummary(userId, projectId)`;
- throw `ServiceException("请先生成源码预览后再进行代码解读")` if no analyzed files;
- build prompt with fixed sections and `summary.getPromptContext()`;
- include previous markdown when `mode = continue`;
- parse AI markdown into `CodeAnalysisResponse`;
- attach `sourceSnapshot` and warnings.
- [ ] **Step 6: Run tests and verify GREEN**
Run: `mvn -pl ruoyi-generator -Dtest=AiGenerationTaskServiceImplTest,AiGenerationTaskWorkerTest,AiGenerateServiceImplTest test`
Expected: tests pass.
- [ ] **Step 7: Commit**
```bash
git add ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IAiGenerateService.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskServiceImpl.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerationTaskWorker.java \
ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerationTaskServiceImplTest.java \
ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerationTaskWorkerTest.java \
ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerateServiceImplTest.java
git commit -m "feat: run code analysis through ai tasks"
```
---
### Task 3: History and Controller Endpoints
**Files:**
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisHistoryResponse.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front/FrontProjectGenerationMapper.java`
- Modify: `ruoyi-generator/src/main/resources/mapper/front/FrontProjectGenerationMapper.xml`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisService.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java`
- Test: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisServiceTest.java`
- [ ] **Step 1: Write failing history tests**
Add tests:
```java
@Test
public void historyShouldOnlyReturnCodeAnalysisRecordsForOwnedProject() {
List<CodeAnalysisHistoryResponse> history = service.listHistory(1L, 100L);
assertTrue(history.stream().allMatch(item -> "code_analysis".equals(item.getGenerateType())));
assertTrue(history.stream().allMatch(item -> Long.valueOf(100L).equals(item.getProjectId())));
}
@Test
public void getHistoryDetailShouldRejectOtherProjectRecord() {
assertThrows(ServiceException.class, () -> service.getHistoryDetail(1L, 100L, 999L));
}
```
- [ ] **Step 2: Run tests and verify RED**
Run: `mvn -pl ruoyi-generator -Dtest=ProjectCodeAnalysisServiceTest test`
Expected: missing history DTO/service methods.
- [ ] **Step 3: Add mapper lookup**
Add mapper method:
```java
FrontProjectGeneration selectGenerationForUser(@Param("userId") Long userId,
@Param("projectId") Long projectId, @Param("generationId") Long generationId);
```
Add XML select:
```xml
<select id="selectGenerationForUser" resultMap="FrontProjectGenerationResult">
<include refid="selectFrontProjectGenerationVo"/>
where user_id = #{userId}
and project_id = #{projectId}
and generation_id = #{generationId}
</select>
```
- [ ] **Step 4: Implement history service methods**
Add `listHistory(userId, projectId)`, `getHistoryDetail(userId, projectId, generationId)`, `createTaskRequest(...)`, and `createContinueTaskRequest(...)` to `ProjectCodeAnalysisService`. Filter list results in Java to `generateType = code_analysis` if reusing `selectGenerationsByProjectId`.
- [ ] **Step 5: Add controller endpoints**
In `FrontProjectController` add:
```java
@GetMapping("/{projectId}/code-analysis/source-summary")
public AjaxResult codeAnalysisSourceSummary(@PathVariable Long projectId) {
Long userId = SecurityUtils.getUserId();
return success(projectCodeAnalysisService.buildSourceSummary(userId, projectId));
}
```
Add history/detail/task/continue endpoints. Task endpoints call `aiGenerationTaskService.createTask(userId, projectId, requestFromService)`.
- [ ] **Step 6: Run tests and verify GREEN**
Run: `mvn -pl ruoyi-generator -Dtest=ProjectCodeAnalysisServiceTest test`
Expected: tests pass.
- [ ] **Step 7: Commit**
```bash
git add ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/CodeAnalysisHistoryResponse.java \
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front/FrontProjectGenerationMapper.java \
ruoyi-generator/src/main/resources/mapper/front/FrontProjectGenerationMapper.xml \
ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisService.java \
ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java \
ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/ProjectCodeAnalysisServiceTest.java
git commit -m "feat: expose project code analysis history"
```
---
### Task 4: Frontend API, Routing, and Page Skeleton
**Files:**
- Modify: `easycode-web/src/api/project.js`
- Modify: `easycode-web/src/router/index.js`
- Create: `easycode-web/src/views/CodeAnalysisView.vue`
- Create: `easycode-web/src/views/codeAnalysisView.test.mjs`
- [ ] **Step 1: Write failing frontend source tests**
Create `codeAnalysisView.test.mjs`:
```js
test('code analysis route and page expose core controls', () => {
const router = readSource('../router/index.js')
const view = readSource('CodeAnalysisView.vue')
const api = readSource('../api/project.js')
assert.equal(router.includes("path: '/project/:projectId/code-analysis'"), true)
assert.equal(router.includes("name: 'code-analysis'"), true)
assert.equal(view.includes('章节导航'), true)
assert.equal(view.includes('开始 AI 解读'), true)
assert.equal(view.includes('继续生成'), true)
assert.equal(view.includes('历史'), true)
assert.equal(api.includes('getCodeAnalysisSourceSummary'), true)
})
```
- [ ] **Step 2: Run test and verify RED**
Run: `node --test easycode-web/src/views/codeAnalysisView.test.mjs`
Expected: file missing or assertions fail.
- [ ] **Step 3: Add API wrappers**
Add to `project.js`:
```js
export function getCodeAnalysisSourceSummary(projectId) { ... }
export function listCodeAnalysisHistory(projectId) { ... }
export function getCodeAnalysisHistory(projectId, generationId) { ... }
export function createCodeAnalysisTask(projectId, data) { ... }
export function continueCodeAnalysisTask(projectId, data) { ... }
```
- [ ] **Step 4: Add route**
Import `CodeAnalysisView` and add route with `requiresAuth: true`.
- [ ] **Step 5: Add page skeleton**
Create page with:
- `projectId` from route;
- `project`, `sourceSummary`, `history`, `activeTask`, `reportMarkdown`;
- buttons for start, continue, refresh, history;
- empty state when no report;
- tabs for preview/edit;
- history drawer placeholder.
- [ ] **Step 6: Run frontend test and verify GREEN**
Run: `node --test easycode-web/src/views/codeAnalysisView.test.mjs`
Expected: tests pass.
- [ ] **Step 7: Commit**
```bash
git add easycode-web/src/api/project.js easycode-web/src/router/index.js \
easycode-web/src/views/CodeAnalysisView.vue easycode-web/src/views/codeAnalysisView.test.mjs
git commit -m "feat: add code analysis page shell"
```
---
### Task 5: Frontend Task Polling, History, Continue, and Export
**Files:**
- Modify: `easycode-web/src/views/CodeAnalysisView.vue`
- Test: `easycode-web/src/views/codeAnalysisView.test.mjs`
- [ ] **Step 1: Add failing tests for behavior markers**
Extend source test:
```js
assert.equal(view.includes('function waitForCodeAnalysisTask'), true)
assert.equal(view.includes('continueCodeAnalysisTask'), true)
assert.equal(view.includes('listCodeAnalysisHistory'), true)
assert.equal(view.includes('downloadMarkdown'), true)
assert.equal(view.includes('downloadWordDoc'), true)
assert.equal(view.includes('renderMarkdown'), true)
```
- [ ] **Step 2: Run test and verify RED**
Run: `node --test easycode-web/src/views/codeAnalysisView.test.mjs`
Expected: missing function markers.
- [ ] **Step 3: Implement task polling**
Use existing `getAiTask(projectId, taskId)` with interval or async loop. On success parse `resultPayload` JSON and set report/history. On failed show error.
- [ ] **Step 4: Implement history loading**
Load history on mount, pick first successful item with parsed `response.markdown`, and allow drawer selection.
- [ ] **Step 5: Implement continue generation**
When continuing:
- use current edited markdown if present;
- fallback to latest successful history markdown;
- call `continueCodeAnalysisTask(projectId, { previousGenerationId, previousMarkdown })`;
- poll resulting task.
- [ ] **Step 6: Implement exports**
Reuse `saveBlob` and `renderMarkdown`. Markdown filename: `${projectName || 'code-analysis'}-代码解读报告.md`. Word compatible filename: `.doc`.
- [ ] **Step 7: Run test and verify GREEN**
Run: `node --test easycode-web/src/views/codeAnalysisView.test.mjs`
Expected: tests pass.
- [ ] **Step 8: Commit**
```bash
git add easycode-web/src/views/CodeAnalysisView.vue easycode-web/src/views/codeAnalysisView.test.mjs
git commit -m "feat: complete code analysis report workflow"
```
---
### Task 6: Project Entry Points and Verification
**Files:**
- Modify: `easycode-web/src/views/ProjectListView.vue`
- Modify: `easycode-web/src/views/projectListView.test.mjs`
- Modify: `easycode-web/src/views/PreviewView.vue`
- Modify: `easycode-web/src/views/previewView.test.mjs`
- Modify: `easycode-web/src/views/GenerateView.vue`
- Modify: `easycode-web/src/views/generateView.test.mjs`
- [ ] **Step 1: Add failing entry tests**
Project list test:
```js
assert.equal(source.includes('el-tooltip content="代码解读"'), true)
assert.equal(source.includes('aria-label="代码解读"'), true)
assert.equal(source.includes('openCodeAnalysis'), true)
```
Preview test:
```js
assert.equal(source.includes('代码解读'), true)
assert.equal(source.includes('openCodeAnalysis'), true)
assert.equal(source.includes('`/project/${projectId.value}/code-analysis`'), true)
```
Generate test:
```js
assert.equal(source.includes('打开代码解读'), true)
assert.equal(source.includes('/code-analysis'), true)
```
- [ ] **Step 2: Run tests and verify RED**
Run:
```bash
node --test easycode-web/src/views/projectListView.test.mjs
node --test easycode-web/src/views/previewView.test.mjs
node --test easycode-web/src/views/generateView.test.mjs
```
Expected: assertions fail.
- [ ] **Step 3: Add entry buttons**
Project list: import `Reading` or another Element Plus icon and add compact icon button that routes to `/project/${projectId}/code-analysis`.
Preview: add toolbar button near download.
Generate: add generated result action after preview/download where a project id is available.
- [ ] **Step 4: Run tests and verify GREEN**
Run the three node tests again.
Expected: tests pass.
- [ ] **Step 5: Run targeted backend tests**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=ProjectCodeAnalysisServiceTest,AiGenerationTaskServiceImplTest,AiGenerationTaskWorkerTest,AiGenerateServiceImplTest test
```
Expected: tests pass.
- [ ] **Step 6: Run frontend build check**
Run:
```bash
cd easycode-web
npm run build
```
Expected: Vite build succeeds.
- [ ] **Step 7: Commit**
```bash
git add easycode-web/src/views/ProjectListView.vue easycode-web/src/views/projectListView.test.mjs \
easycode-web/src/views/PreviewView.vue easycode-web/src/views/previewView.test.mjs \
easycode-web/src/views/GenerateView.vue easycode-web/src/views/generateView.test.mjs
git commit -m "feat: add project code analysis entry points"
```
---
## Self-Review
- Spec coverage: the plan covers no ZIP upload, project-bound source preview collection, AI task integration, history, continue generation, report UI, project entry points, and verification.
- No placeholders: task steps include concrete files, method names, commands, and expected outputs.
- Type consistency: `GenerateCodeAnalysisRequest`, `CodeAnalysisResponse`, `CodeAnalysisSourceSummary`, and `code_analysis` are introduced before later tasks reference them.