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

> 触发：定时走查 cron（每 30 分钟）。本轮范围 = 自上次走查起新增 6 提交（`fddca9d`/`892a642`/`19d64fc`/`d809cf4`/`8710d6c`/`06a2dea`）前端改动 + 工作区未提交（`types.ts`/`project.ts` M）。
> 方法：3 路后台代理并行（A：ARC-05 store 拆分 / B：B-22 状态同步含 B-32/33 核对 / C：CR-11 健壮性+工作区未提交）+ 主代理地面核对（`useAiStream.ts`/`useAiSend.ts`/`workflow.rs`/`f93b758` 提交）。
> 性质：核对记录，不改产品代码（本会话 session-role-diagnose-only）。todo 待修项见 `docs/todo.md`。
> 关联：[定时走查-2026-06-15-P0复核.md](./定时走查-2026-06-15-P0复核.md)（第 1 轮，B-32/33 结论被本轮纠正）。

---

## 核心结论：3 前端 P0 全修（第 3 次纠正过时认知）

| P0 | 第1轮判断 | 第2轮地面复核 | 提交 | 处置 |
|---|---|---|---|---|
| **B-34** selectType | 已修 ✅ | 已修 ✅（确认） | — | 保持 ✅ |
| **B-32** 队列收尾 | 「未修」 | **已修 ✅** | `f93b758` | todo 标 ✅，纠正第1轮 |
| **B-33** 骨架屏 | 「未修」 | **已修 ✅** | `f93b758` | todo 标 ✅，纠正第1轮 |

**B-32/B-33 地面证据**（`useAiStream.ts` 当前 81 行，第1轮 Read 时为 73 行旧版）：
- `:28` `state.queue = [] // B-32:超时收尾同步清队列` + useAiEvents.ts:225(AiError) + useAiSend.ts:152(stopChat)/124(approveToolCall catch) **四路径全清队列**
- `:43-45` onStreamTimeout 单遍反向扫描 running toolCall → rejected（合并探测 completed 为单次 O(n)，实现干净）

**B-32/B-33 修复提交**：`f93b758`「修复: B-32+33 流式收尾清队列+回滚 running toolCall」3 文件 +18/-7。

> 第1轮「B-32/33 未修」为过时快照（working tree 当时旧版，与 B-34 同根因）。**定时走查连续 3 轮纠正过时 P0 判断**（B-34 → B-32 → B-33），说明走查必须以地面 Read 为准，不可信赖记忆/旧快照。前端 3 P0 至此全部闭环。

---

## 新发现 P0：AR-11 前端 listener 永不 attach（功能半接通死代码）

**位置**：`src/stores/project.ts:31-55`（定义）+ `:109-110`（export）+ `src/App.vue:203-213`（onMounted 未挂载）

**根因**：工作区未提交改动新增 `startDataChangedListener`/`stopDataChangedListener` 两函数 + export，但**全项目零调用方**（grep 确认）。后端 `emit_data_changed`（audit.rs:259-270）+ 自动执行路径（audit.rs:351）+ 审批路径（commands.rs:175）三处 emit 已就位且逻辑正确，事件 `df-data-changed` 正常发出，但前端 `listen('df-data-changed', …)` 永不执行 — 监听器从未 attach。

**影响**：AR-11「数据变更联动刷新」功能**完全失效**。用户通过 AI 工具（create/update/delete project/task/idea）改数据后，列表不自动刷新，仍需手动刷新（正是 AR-11 要消除的痛点）。后端 emit 成死事件。

**修复方向**：`App.vue` onMounted 调 `await projectStore.startDataChangedListener()`，onUnmounted 调 `stopDataChangedListener()`（对齐 `ProjectDetail.vue:463/470` 的 workflow listener 生命周期模式，数据变更可能来自任意页面）。

**性质**：工作区未提交半成品（非历史遗留，非 ARC-05 拆分收尾）。代理 C 确认 diff 是纯 AR-11 新增。

> 注：todo:77 AR-11 条目原标「暂缓」，第2轮复核更正为「后端已实施 ✅，前端半接通 🔴」。

---

## ARC-05 上帝 store 拆分 — 质量评级：优

**提交** `8710d6c`：`project.ts` 362→barrel + 5 子 store（state/projects/tasks/ideas/workflow）。

**3 项硬指标全部兑现**：
| 指标 | 证据 |
|---|---|
| barrel 零改动兼容 | 逐字段比对 8710d6c~1 与 8710d6c 的 `return reactive({...})`，37 key 完全一致（8 getter state + clearError + 9 project + 4 task + 6 idea + 7 workflow actions + pendingApproval/stats computed）。state getter 用 `get projects() { return state.projects }` 保留「避免 loadXxx 重赋值后视图为空」关键设计 |
| state 真单例 | state.ts 模块级 `export const state = reactive({...})`，ESM 保证全 app 单实例，4 子 store import 同一引用 |
| 依赖图无环 | 4 子 store 互不 import，只依赖叶子 `./state`；barrel 单向依赖子 store，无循环/无初始化顺序陷阱 |

**🟡 建议 3**：
1. `state.ts:32-37` 可变 `let _eventUnlisten` 跨模块共享 + setter，依赖 ESM live-binding（运行时正确但反直觉），建议注释点明或并入 reactive
2. **AR-11 监听器已侵入 barrel**（working tree project.ts:31-55），违反「纯 barrel」定位 — 建议抽第 6 子 store `project/dataChange.ts`（与上面 AR-11 P0 同源）
3. `projects.ts:105` clearError 夹在 projects return 语义错位（属 state 层），建议移除（barrel 已独立从 state 导入）

**⚪ 可选 2**：注释「四子 store」实为 5 文件（四领域+共享 state）/ createXxx 入参类型内联可抽 types。

**✅ 亮点**：越层 invoke 下沉彻底（`approve_human_approval`/`cancel_workflow_node` 下沉 `api/workflow.ts:39-54`，B-34 snake_case 对齐在 api 层统一）/ ProjectStore 类型 + 单例 `_storeInstance` 保留 / view 端 7 处 `useProjectStore` 零改动。

---

## B-22 前后端状态同步 — 实现质量良好

**提交** `d809cf4`：`ai_is_generating` IPC（commands.rs:12）+ sendMessage 预检（useAiSend.ts:27）。

**设计对症**：双源（前端 streaming / 后端 generating）各自维护确有不同步风险，发送前查后端真值对症。

**竞态窗口可接受**：`useAiSend.ts:42` 查后端与 `:97` 实际 sendMessage 之间有窗口，但后端 `ai_chat_send:57` 原子检查+占用（`if session.generating { Err }` + `generating=true` 同锁内）是真兜底。前端预检仅优化 UX（提前入队而非发出去被拒），降级路径完备（IPC 失败 catch 退化为原 streaming 预检）。

**回归安全**：正常事件流（delta/tool/AiAgentRound/AiCompleted/AiError）未改，仅 sendMessage 入口前置预检，不影响 handleEvent。

**🟡 Issue 1（低）**：`useAiSend.ts:53-57` 入队分支复位 `streaming=true` 但**未启动看门狗**（resetStreamWatchdog 只在 :89 正常发送路径调）。极端卡死场景（后端既不 emit、Drop spawn 又未执行）streaming 可能永真。建议入队分支也调一次 resetStreamWatchdog，代价一行。非 d809cf4 新引入回归（onStreamTimeout 本就依赖正常路径启动的看门狗）。

**信息 Issue 2**：`useAiSend.ts:60-61` 注释「streaming=true 但后端 false」描述与实际复位方向（onStreamTimeout/AiError 复位 streaming=false）有歧义，建议改注释。

---

## CR-11 健壮性批（fddca9d）— ✅5 亮点 + 1 死逻辑副产品

**子项 ⑦⑧⑨⑩⑪⑫ 全部实施**：

**✅ 5 亮点**：
1. `useAiConversations.ts:77-93` JSON.parse 逐条 try/catch（原单条坏 args 清空整对话 → 单条降级空对象）
2. `useAiEvents.ts:130-133` AiHeartbeat 显式 case 防 TS 穷举穿透
3. `useAiSend.ts:84-85` 复用 findToolCall（消除内联 flatMap+find 重复 + 反向扫描 O(1) 均）
4. `ToolCard.vue:236-242` argString 去 3 处 `as any`，`Record<string,unknown>` 类型收窄
5. `workflow.ts:44-92` approveHumanApproval 签名收敛 `(decisions: string[], comment?)`（消除单/多选调用方歧义）

**⚠️ ⑪ 实施引入死逻辑（CR-260615-18）**：`workflow.ts:67-68` `decision = selectType === 'multiple' ? decisions[0] ?? '' : decisions[0] ?? ''` 两分支返回值完全相同，三元无意义。后端 workflow.rs:291-296 有兜底。修：直接 `const decision = decisions[0] ?? ''`。

**🟡 其他**：CR-19（action 字段 emit 但前端不消费，契约冗余）/ CR-20（stopDataChangedListener try/catch 过度防御，与 workflow.ts:108 不一致）。

---

## P0 汇总（仍未修）

| ID | 问题 | 状态 |
|---|---|---|
| B-260615-35 | broadcast Lagged 兜底仅 warn → 终态事件丢失前端永久卡死（后端 workflow.rs:90-110） | **未修**（第1轮发现，本轮确认仍在）|
| AR-11 前端 | listener 永不 attach（App.vue 未挂载） | **未修**（本轮新发现，工作区半接通）|

---

## 📊 本轮摘要

| 类别 | 数 | 代表 |
|---|---|---|
| P0 全修确认 | 3 | B-34（保持✅）/ B-32 / B-33（第1轮过时，本轮纠正）|
| 新发现 P0 | 1 | AR-11 前端 listener 永不 attach（功能半接通）|
| 仍存 P0 | 2 | B-35 broadcast Lagged（后端）/ AR-11 前端 attach |
| ✅ 质量肯定 | 2 | ARC-05 store 拆分（优）/ CR-11 健壮性批（5 亮点）|
| 🟡 建议 | 6 | ARC-05 三项 / B-22 看门狗 / CR-18 死逻辑 / CR-19·20 |
| ⚪ 可选 | 4 | ARC-05 两项 / ToolCard 兜底 |

**本轮价值**：第 3 次纠正过时 P0 判断（B-32/33 实已修，f93b758），前端 3 P0 全闭环；新发现 AR-11 前端 listener 永不 attach（工作区半接通死代码，功能未闭环）；肯定 ARC-05 拆分（3 硬指标全兑现）与 CR-11 健壮性批质量。

**方法论警示**：连续 3 轮纠正过时判断（B-34→B-32→B-33），根因是第1轮 Read 到 working tree 旧版（73 行）而 HEAD 已是新版（81 行）。**走查必须每次地面 Read，不可信赖上轮快照**。后续走查代理 prompt 应强调「以当前 working tree + HEAD 为准，git log 看提交，勿用记忆」。

todo 映射：B-32/33 标 ✅ + 复核更正 / AR-11（todo:77）状态更新 / CR-11（todo:212）标 ✅ / 新增 CR-260615-18/19/20。
