# 定时代码走查 — 第 6 轮（2026-06-15）

> 触发：定时走查 cron（每 30 分钟）。范围 = **列表页 + Dashboard + AI 面板/窗口垂直切片**（此前从未深查）。1 新提交（CR-22 修复）。
> 方法：2 路代理（A 列表页 / B Dashboard+AI窗口）+ 主代理地面核 stopListener。
> 性质：核对记录，不改产品代码（本会话 session-role-diagnose-only）。todo 见 `docs/todo.md`。
> 关联：[第1-5轮](./)。

---

## CR-22 ✅ + CR-23 撤销（价值第 5 次闭环）

- `16aeacc` CR-22 ✅：types.ts 3 枚举注释对齐后端（Task/Idea/Project status）+ 删 AiConversationSummary.models 冗余
- CR-23 撤销（不适用）：接收方核对 B-34「Tauri 不转」注释在 ARC-05 拆分时已简化（workflow.ts:81/project.ts:58 仅风格对齐无错误说明）

---

## 🔴 分离窗口生命周期（新功能首次深查，P1 核心）

**代理 B R1-R5**。地面核 `stopListener`(useAiEvents.ts:258) **全仓零调用**（grep App/AiChat/AiDetached onUnmounted/onBeforeUnmount 都不调）确认 R2。

### CR-260615-24 [P1] listener 双注册 + 永不清理（R1+R2）
- **R1 双窗口重复消费 ai-chat-event**：后端 `app.emit`（stream_recv/agentic/commands/audit 多处）是**全局广播**发给所有 webview。主窗口（AiChat.vue:790）+ 分离窗口（AiDetached 渲染 AiChat）各注册 `listen('ai-chat-event')`，两窗口 handleEvent 都触发 → delta 各 `+=` 一次 / AiAgentRound 各 push 空气泡。当前靠 App.vue:94 `v-if="panelOpen && !detached"` detach 时卸载主窗口 AiChat 间接停，但 state 是模块级单例（handleEvent 仍写主窗口 state），**脆弱隐式契约**。
- **R2 stopListener 零调用**：useAiEvents.ts:258 定义但全仓无调用点 → listener + watchdog 永久泄漏（startListener 幂等防重复注册，但卸载不释放）。
- **边界影响**：reattach 重叠窗口期多一条空 assistant 消息（非崩溃/非数据损坏）。
- **修**：AiChat.vue onBeforeUnmount 补 `store.stopListener()` + detach 时主窗口显式 stopListener / reattach startListener，或后端 `emit_to(label)` 定向。

### CR-260615-25 [P2] 分离窗口状态机缺口（R3/R4/R5）
- **R3**：`win.once('tauri://destroyed')` 置 detached=false 但**不恢复 panelOpen=true**（只 reattachPanel 显式设）→ 用户直接 X 关分离窗口后面板消失，需手动 Ctrl+I
- **R4**：`resumeInDetached`(useAiWindow.ts:79-95) 强行重建生成态（读 df-ai-gen 置 streaming=true + push 空气泡），与后端 `ai_is_generating` 真值可能失配 → 假气泡 / 130s 后 watchdog 假超时
- **R5**：分离窗口 onMounted `switchConversation`(useAiWindow.ts:81) + loadConversations 内自动 switch，后端 **AiSession 单例** activeConversationId 被分离窗口覆盖 → 主窗口期间发消息可能落错会话（B-22 同类延伸）

---

## 🟡 列表页（代理 A）

### CR-260615-26 [P2] AR-11 + B-29 契约交互 + Tasks 空态（R-1/Y-2）
- **R-1**：AR-11 监听器（project.ts:38）对 task 调 `loadTasks()` **无参 → 全量加载**，破坏 B-260615-29 建立的「store.tasks 反映当前筛选」契约。filteredGroups 二次过滤**兜住结果正确**，但 store.tasks 变全量（createTask push 到全量数组）+ 性能
- **Y-2**：Tasks.vue:39-71 **无 loading/error/empty 兜底**，加载失败或无结果时空白页（store.error/loading 零引用）。对比 Projects.vue:107 至少有空态

### ✅ 列表页亮点
- **Projects/Tasks i18n 100%**（grep 确认中文仅注释/console）— CR-08 范围内这两页**无硬编码遗漏**
- Projects.vue useConfirm composable 抽离（DRY，消除 4 视图重复）
- B-260615-29 筛选重载注释保留（可追溯）

---

## 🟡 Dashboard（代理 B）

### CR-260615-27 [P2] refresh 兜底 + stage 颜色 + 死代码（M1/M3/L4）
- **M1**：refresh（Dashboard.vue:202-204）Promise.all **无 try/catch**，任一接口失败 unhandledrejection + 用户无反馈（onMounted 有 try/catch 但 refresh 无）
- **M3**：getProjectStage(:150) status→stage 映射 **planning 归 coding stage（颜色紫）但 label planning**，颜色语义撒谎（cancelled 归 testing 同）
- **L4**：决策/批注两 panel(:103-120) 永远空态，CSS(.decision-row/.anno-row :504-572) 完整但模板未用 = **死代码**

### ✅ Dashboard 亮点
- **无图表库**，纯 CSS 进度条 + 数字 stat，GPU 友好动画，**无性能问题**
- aiShared 破环正确（ARC-06），nextMsgId 两窗口各自独立不冲突

---

## 📊 本轮摘要

| 类别 | 数 | 代表 |
|---|---|---|
| 价值闭环 | 1 | CR-22 ✅ + CR-23 撤销 |
| P1 新发现 | 1 | 分离窗口 R1+R2 listener 双注册+泄漏（CR-24）|
| P2 新发现 | 3 | 分离窗口状态机（CR-25）/ 列表页契约+空态（CR-26）/ Dashboard（CR-27）|
| 亮点 | 4 | 列表页 i18n 100% / Dashboard 纯 CSS 无性能问题 / aiShared 破环 / useConfirm DRY |

**本轮价值**：**分离窗口是新功能，前几轮横切（按提交/改动）完全没覆盖**，垂直切片（Dashboard+AI窗口维度）首次深查即暴露 P1（listener 泄漏）+ 状态机缺口。**再次印证 task2/3 垂直走查价值** — 横切收敛后，垂直切片是新发现来源（i18n/api/列表页/AI窗口 4 个垂直切片均挖出真问题）。

**方法论**：分离窗口 P1（R1+R2）经地面核 stopListener 零调用坐实（避免第5轮代理 A 假阳性教训），走查发现须地面验证。

todo 映射：CR-260615-24（P1 分离窗口 listener）/ CR-25（P2 状态机）/ CR-26（P2 列表页）/ CR-27（P2 Dashboard）。
