cloud-drive-renamer/docs/superpowers/plans/2026-03-20-diagnostic-export-completion-plan.md

397 行
10 KiB
Markdown

# Diagnostic Export Completion 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:** 补完失败诊断导出与反馈引导剩余链路,使用户在失败结果页中可以直接导出 JSON 诊断文件并看到后续反馈动作。
**Architecture:** 以 `FileSelectorPanel` 为诊断状态源,`ConfigPanel` 仅负责渲染提示条并抛事件;background 通过 `diagnostic-service` 管理最近日志与导出 payload;content 侧通过 Blob 下载文件,不新增权限。
**Tech Stack:** TypeScript、Lit、Chrome Extension MV3、chrome.runtime messaging、chrome.storage.local、Vitest、jsdom
---
## Inputs
- Spec: `docs/superpowers/specs/2026-03-20-diagnostic-export-completion-design.md`
- Existing design: `docs/superpowers/specs/2026-03-19-diagnostic-export-feedback-design.md`
- Existing ring buffer: `src/background/diagnostic-log-store.ts`
## File Structure
### Create
- `src/types/runtime-message.ts`
- `src/background/diagnostic-service.ts`
- `src/core/diagnostic-session.ts`
- `src/utils/diagnostic-download.ts`
- `tests/unit/logger.test.ts`
- `tests/unit/diagnostic-service.test.ts`
- `tests/unit/diagnostic-session.test.ts`
- `tests/unit/config-panel-diagnostic-feedback.test.ts`
- `tests/unit/file-selector-panel-diagnostic-feedback.test.ts`
### Modify
- `src/utils/logger.ts`
- `src/background/service-worker.ts`
- `src/content/index.ts`
- `src/content/components/config-panel.ts`
- `src/content/components/file-selector-panel.ts`
- `src/types/diagnostic.ts`
- `src/locales/zh_CN.json`
- `src/locales/zh_TW.json`
- `src/locales/en.json`
- `docs/testing/manual-test-cases.md`
### Keep Unchanged
- `manifest.json`
---
### Task 1: Runtime message 与 background diagnostic service
**Files:**
- Create: `src/types/runtime-message.ts`
- Create: `src/background/diagnostic-service.ts`
- Modify: `src/background/service-worker.ts`
- Test: `tests/unit/diagnostic-service.test.ts`
- [ ] **Step 1: 写 `diagnostic-service` 的失败测试**
```ts
it('builds export payload from stored last failure and recent logs', async () => {
const payload = await service.getExportPayload();
expect(payload.lastFailure?.failure.reason).toBe('batch-execution-failed');
expect(payload.logs).toHaveLength(2);
});
it('returns null lastFailure when no snapshot is stored', async () => {
const payload = await service.getExportPayload();
expect(payload.lastFailure).toBeNull();
});
```
- [ ] **Step 2: 运行测试确认先失败**
Run:
```bash
npx vitest run tests/unit/diagnostic-service.test.ts
```
Expected:
- FAIL,提示 `diagnostic-service` 或相关 message type 不存在。
- [ ] **Step 3: 实现最小 runtime message 类型**
Implementation notes:
- 在 `src/types/runtime-message.ts` 定义:
- `APPEND_DIAGNOSTIC_LOG`
- `GET_DIAGNOSTIC_EXPORT`
- `OPEN_EXTERNAL_URL`
- [ ] **Step 4: 实现 `diagnostic-service.ts`**
Implementation notes:
- 封装 `appendLog(entry)`
- 封装 `getExportPayload()`
- 封装 `openExternalUrl(url)`
- 读取 `DIAGNOSTIC_STORAGE_KEYS.LAST_FAILURE` 和 ring buffer
- [ ] **Step 5: 接到 `service-worker.ts`**
Implementation notes:
- `service-worker.ts` 只做 message routing
- 不把 payload 组装逻辑散落在 switch 中
- [ ] **Step 6: 复跑测试确认通过**
Run:
```bash
npx vitest run tests/unit/diagnostic-service.test.ts tests/unit/diagnostic-log-store.test.ts
```
Expected:
- PASS
---
### Task 2: Logger diagnostic transport
**Files:**
- Modify: `src/utils/logger.ts`
- Modify: `src/content/index.ts`
- Test: `tests/unit/logger.test.ts`
- [ ] **Step 1: 写 logger transport 的失败测试**
```ts
it('extracts module prefix and forwards structured diagnostic entry', () => {
const transport = vi.fn();
configureLoggerDiagnostics({ source: 'content', transport });
logger.error('[FileSelectorPanel] Retry failed:', new Error('boom'));
expect(transport).toHaveBeenCalledWith(expect.objectContaining({
module: 'FileSelectorPanel',
level: 'ERROR',
message: 'Retry failed:',
}));
});
```
- [ ] **Step 2: 运行测试确认先失败**
Run:
```bash
npx vitest run tests/unit/logger.test.ts
```
Expected:
- FAIL,提示 `configureLoggerDiagnostics` 不存在或 transport 未被调用。
- [ ] **Step 3: 在 `logger.ts` 实现 transport 配置**
Implementation notes:
- 保持既有 `logger.info/warn/error` 调用方式兼容
- 用模块前缀正则提取 `[Module]`
- transport 异常要吞掉,不影响 console 输出
- [ ] **Step 4: 在 `content/index.ts` 里接入 transport**
Implementation notes:
- 启动时配置一次
- transport 使用 `chrome.runtime.sendMessage`
- 失败时静默降级
- [ ] **Step 5: 复跑测试确认通过**
Run:
```bash
npx vitest run tests/unit/logger.test.ts
```
Expected:
- PASS
---
### Task 3: 最近失败快照领域模型
**Files:**
- Modify: `src/types/diagnostic.ts`
- Create: `src/core/diagnostic-session.ts`
- Test: `tests/unit/diagnostic-session.test.ts`
- [ ] **Step 1: 写失败快照构建的失败测试**
```ts
it('builds failure snapshot from batch results', () => {
const snapshot = buildLastFailureDiagnosticSnapshot(...);
expect(snapshot.summary.failed).toBe(2);
expect(snapshot.failures[0]?.targetName).toBe('new-a.txt');
});
it('builds feedback summary text with platform and file name', () => {
const text = buildDiagnosticFeedbackText(...);
expect(text).toContain('夸克网盘');
expect(text).toContain('cloud-drive-renamer-diagnostic');
});
```
- [ ] **Step 2: 运行测试确认先失败**
Run:
```bash
npx vitest run tests/unit/diagnostic-session.test.ts
```
Expected:
- FAIL,提示诊断 session 纯函数不存在。
- [ ] **Step 3: 在 `diagnostic.ts` 扩展必要类型**
Implementation notes:
- 增加 summary / failure item / prompt state 联合类型
- 保持现有 ring buffer 类型兼容
- [ ] **Step 4: 实现 `diagnostic-session.ts`**
Implementation notes:
- 提供构造快照、重试统计合并、反馈摘要构造等纯函数
- [ ] **Step 5: 复跑测试确认通过**
Run:
```bash
npx vitest run tests/unit/diagnostic-session.test.ts
```
Expected:
- PASS
---
### Task 4: ConfigPanel 结果区诊断提示条
**Files:**
- Modify: `src/content/components/config-panel.ts`
- Modify: `src/locales/zh_CN.json`
- Modify: `src/locales/zh_TW.json`
- Modify: `src/locales/en.json`
- Test: `tests/unit/config-panel-diagnostic-feedback.test.ts`
- [ ] **Step 1: 写 `ConfigPanel` 提示条失败测试**
```ts
it('renders diagnostic ready prompt when finished with failures', async () => {
const panel = new ConfigPanel();
panel.finished = true;
panel.progress = { completed: 2, total: 2, currentFile: '', success: 0, failed: 2 };
panel.diagnosticPromptState = 'ready';
panel.diagnosticFailureCount = 2;
document.body.appendChild(panel);
await panel.updateComplete;
expect(panel.shadowRoot?.querySelector('[data-role=\"diagnostic-export-button\"]')).toBeTruthy();
});
```
- [ ] **Step 2: 运行测试确认先失败**
Run:
```bash
npx vitest run tests/unit/config-panel-diagnostic-feedback.test.ts
```
Expected:
- FAIL,提示诊断属性或按钮不存在。
- [ ] **Step 3: 在 `ConfigPanel` 增加诊断只读属性与事件**
Implementation notes:
- 新增 `diagnosticPromptState`
- 新增 `diagnosticFailureCount`
- 新增 `diagnosticFileName`
- 新增 `diagnosticErrorMessage`
- 新增导出/反馈相关事件
- [ ] **Step 4: 在 `renderExecutionView()` 插入提示条**
Implementation notes:
- 提示条位置放在 sync status 后、动作区前
- 不破坏现有 footer 布局
- [ ] **Step 5: 补齐 i18n**
- [ ] **Step 6: 复跑测试确认通过**
Run:
```bash
npx vitest run tests/unit/config-panel-diagnostic-feedback.test.ts tests/unit/config-panel-undo.test.ts
```
Expected:
- PASS
---
### Task 5: FileSelectorPanel 导出与反馈动作闭环
**Files:**
- Modify: `src/content/components/file-selector-panel.ts`
- Create: `src/utils/diagnostic-download.ts`
- Test: `tests/unit/file-selector-panel-diagnostic-feedback.test.ts`
- Modify: `docs/testing/manual-test-cases.md`
- [ ] **Step 1: 写 `FileSelectorPanel` 导出动作失败测试**
```ts
it('stores a diagnostic snapshot and exposes ready state after failed execution', async () => {
// arrange failed results
expect(panel.diagnosticPromptState).toBe('ready');
});
it('downloads diagnostic payload and transitions to exported state', async () => {
await panel.handleDiagnosticExport();
expect(downloadDiagnosticPayload).toHaveBeenCalled();
expect(panel.diagnosticPromptState).toBe('exported');
});
```
- [ ] **Step 2: 运行测试确认先失败**
Run:
```bash
npx vitest run tests/unit/file-selector-panel-diagnostic-feedback.test.ts
```
Expected:
- FAIL,提示导出状态/方法不存在。
- [ ] **Step 3: 实现 `diagnostic-download.ts`**
Implementation notes:
- `downloadDiagnosticPayload(payload): Promise<string>`
- `buildGithubIssueUrl(...)`
- `buildMailtoUrl(...)`
- `buildDiagnosticFeedbackText(...)`
- [ ] **Step 4: 在 `FileSelectorPanel` 实现诊断状态机**
Implementation notes:
- 在 execute / retry 结果落地后保存最近失败快照
- 失败数为 0 时隐藏提示条
- 导出时请求 `GET_DIAGNOSTIC_EXPORT`
- 成功后进入 `exported`
- 失败后进入 `error`
- [ ] **Step 5: 把状态透传到 `ConfigPanel` 并接线事件**
- [ ] **Step 6: 补充手动测试文档**
- [ ] **Step 7: 复跑针对性测试**
Run:
```bash
npx vitest run \
tests/unit/file-selector-panel-diagnostic-feedback.test.ts \
tests/unit/config-panel-diagnostic-feedback.test.ts \
tests/unit/diagnostic-session.test.ts \
tests/unit/diagnostic-service.test.ts \
tests/unit/logger.test.ts
```
Expected:
- PASS
---
### Task 6: 全量验证
**Files:**
- Verify only
- [ ] **Step 1: 运行完整验证**
Run:
```bash
npm run validate
```
Expected:
- PASS,允许既有 lint warnings 存在,但不得新增 error
- [ ] **Step 2: 运行构建验证**
Run:
```bash
npm run build
```
Expected:
- PASS
- [ ] **Step 3: 整理手动测试路径**
Manual:
- 在开发环境制造至少 1 个失败结果
- 验证结果区出现导出入口
- 点击下载 JSON
- 检查导出后引导状态
- 验证“暂不处理”会隐藏提示条