fix: integrate audit findings round 1 - tools, worker, scheduler, main agent

- Unify ToolResultEnvelope (output vs content) for built-in tools
- Fix shell.run AsyncGenerator consumption in ToolRegistry.call/streaming
- Scheduler: consume WorkerResult.status instead of marking all running tasks completed
- WorkerProcess/WorkerManager: surface exit events and generate failed/cancelled result
- MainAgent: integrate ContextAssembler, Chinese destructive regex, ArchitectureDesigner impact gate
- run.ts: pendingConfirmation flow, dispatch extracted, .air files filtered from /results
- CapabilityRegistry wired into RuntimeApp and ServiceRegistry; DoctorService uses it
- release.ts: findRepoRoot/findBun, run air e2e + depcruise + runtime regression
- New gates: release-critical-gates, CLI run command regression
- 14/14 e2e gates pass; 3/3 release dry-run pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AirCoding
2026-06-05 18:39:10 +08:00
parent a2d7aa0339
commit ddefcbb2b1
24 changed files with 1992 additions and 186 deletions

View File

@@ -0,0 +1,196 @@
# 集成测试阶段 MiniMax-M3 审查结果
**审计日期**: 2026-06-05
**项目**: AirCoding V1.0.0 Alpha
**审计员**: MiniMax-M3 (整合阶段)
**审计模式**: 四视角交叉审计(系统架构师 / 开发工程师 / 真实用户 / 测试工程师)
---
## 0. 核心矛盾 (E2E Gates 失真)
13/13 E2E gates 全部通过,但用户视角测试**真实任务成功率近乎为零**。
| 维度 | 表现 |
|---|---|
| tsc typecheck | 0 errors |
| 单元测试 | 169/169 passed |
| E2E gates | 13/13 passed |
| 真实用户场景 | **多数失败** |
E2E gates 仅验证"测试能跑通"和"组件存在"**从未验证"用户场景成功"**。所有 gate 仅检查语法、类型、stub 数量、单元测试通过率;没有一项 gate 验证"LLM 调通 + 文件真的写出 + 多步骤任务真完成"。
---
## 1. 系统架构师审查
### FR-005 (Main Agent 对话) — 部分实现
`MainAgent.chat_with_llm()` 每次调用仅构造 system+user 两条消息,**无 conversation history**,无 context reference无 Anthropic canonical content blocks——严重违反 FR-014 上下文分层。
追问"现在什么情况"时LLM **没有上下文**,是 stateless 单轮调用,必然幻觉。
### FR-016 (TUI) — 不支持交互输入
`TuiApp.setup_input` 只处理 `q/1/2/3/4/h` 键盘事件,**无文本输入路径**。`run.ts``readline` 在 TUI 之外独立处理 stdinTUI 和 CLI 互相竞争字符。TUI 实际是**只读 viewer不能输入任务**。
### FR-007/008 (Scheduler/Worker) — 链路通但判定错误
Worker 真实 spawnIPC 真实 NDJSONtool.call / llm.request 通过 IPC 回父进程由 ProviderManager 真实调用 LLM——**链路是通的**。
`Scheduler.step``MONITORING` 状态存在严重 bugworker 还在跑就 mark `completed`,任务可能"假完成"。
### FR-009 (执行原语) — 不强制 read-before-edit
`ExecutorRole` 直接接受代码块写入,**不强制 read-before-edit**——违反 FR-009。run.ts 的"扫描最近 60 秒修改的文件"是 post-hoc 发现,不验证文件来源、不执行 verification-before-completion。
### FR-017 (C++ 完整闭环) — 工具链与主链路不联动
`toolchain-cpp` 实现了 cpp.detect / cpp.build / cpp.test 工具,但**无 fixture 项目、无自动 configure→build→test→fix→review e2e 集成**。Executor 用 fs.write 写 C++ 源文件,**不调用任何 cpp.* 工具**。C++ 工具链对用户不可见地存活,但与主链路不联动。
### 未对齐设计的关键差异
| 设计 | 实际 |
|---|---|
| ProjectionStore 消费 DB+EventBus | `run.ts` 直接 `receive_snapshot` 构造假数据绕开 EventStore |
| 严格状态机 12 态 | 真实遍历,但 MONITORING 完成判定错误 |
| 5 角色独立 worker | Debugger/Compactor/ExperienceMiner 未被 run.ts 触发 |
| Anthropic canonical messages | MainAgent 用 string content非 content block[] |
---
## 2. 开发工程师 Bug 报告
### P0 阻塞 (2)
**Bug #2 — Scheduler 早判完成** (Scheduler.ts:252-260)
`worker_manager.has_running() === false` 时,将**所有** `running` 状态任务直接标记为 `completed`**不管 worker 是否实际成功**。失败任务可能显示为成功。
**Bug #6 — MainAgent 确认门形同虚设** (run.ts:108-112)
`MainAgent.classify()` 对"delete/remove"类消息返回 `{action: 'delegate', response: 'Are you sure? (y/n)'}`,但 `run.ts:115` 直接 `console.log` `classification.response``rl.prompt()`——**用户的 y/n 永远到不了 `MainAgent.handle_confirmation()`**。破坏性操作无防护。
### P1 重要 (3)
**Bug #3 — ExecutorRole 转义顺序错误** (ExecutorRole.ts:222)
代码块内容转义还原顺序:`\\n → \n`, `\\t → \t`, `\\" → "`, `\\\\ → \\`。若 LLM 输出 `\n`(单反斜杠 n会被错误地换成真换行破坏 JSON/Python 字面量。
**Bug #4 — MainAgent 不带上下文** (MainAgent.ts:73)
`chat_with_llm(message)` 只发送单轮 system + user。追问"现在什么情况"时,**LLM 不知道项目里有啥**。
**Bug #5 — DONE 竞态** (ask.ts:182-186)
若 LLM 单次输出既有 `fs.write` 又有 `DONE`,执行完 tool_call 后立即返回 `completed`。**剩余未完成的 acceptance_criteria 被静默忽略**。
**Bug #7 — 多文件任务漏写**
第一次写到 `src/main.cpp`(正确),第二次写到根 `main.cpp`(简化版 stub。两个 task 缺乏协调。
### P2 nice-to-have (2)
- **Bug #1 — 退出码误判** (WorkerProcess.ts:127): `code || 1` 误判为错误
- **Bug #8`has_running` 误判** (WorkerManager.ts:303-305): `ready` 状态也算 running
---
## 3. 真实用户测试报告 (UAT)
| 用例 | 状态 | 关键现象 |
|---|---|---|
| `air init` | ✅ PASS | 创建 .air/ 目录和 project.json |
| `air ask "..."` 写文件 | ✅ PASS | fs.write 工作 |
| TUI 启动 | ✅ PASS | OpenTUI 渲染正确 |
| `air doctor` | ✅ PASS | 检测 bun/sqlite/git/shell |
| **shell.run 任意命令** | ❌ **FAIL** | 10/10 次返回 `Error: undefined` |
| **追问项目内容** | ❌ **FAIL** | LLM 答"我没有文件系统访问" |
| **危险操作 (删除)** | ❌ **FAIL** | 静默通过,文件仍在 |
| **false-positive 成功** | ❌ **FAIL** | tool 报错但 CLI 仍打印 `✅` |
| **多文件 C++ 任务** | ❌ **FAIL** | main.cpp 内容退化为 stub |
| `air doctor --fix` | ❌ FAIL | 标 [fixable] 不修 |
### 用户最痛 3 个问题
1. **shell 工具整体坏死**,让 AI 跑任何命令都失败 10/10 次。
2. **报错说"成功",实际没干**——用户以为工作流跑通了回头看磁盘空的。false-positive 比直接报错危险十倍。
3. **TUI 和 CLI 是两套东西**`air ask` 能用工具,`air run` TUI 输入路径不可见(实际仅 readline 跑,渲染层 OK 但端到端未跑通)。
---
## 4. QA 测试矩阵
| Test | 状态 | 关键观察 |
|---|---|---|
| 1: 简单文件创建 | PASS | hello.txt 创建成功内容正确。Worker stderr 异常但不影响产出。 |
| 2: 多文件 (C++ + CMake) | **PARTIAL** | main.cpp 内容退化为 stubCMakeLists.txt 正确。两文件被写到不同位置 |
| 3: 追问项目内容 | **FAIL** | agent.handle_user_message("我的项目有哪几个文件?") 走 answer 分支LLM 幻觉 |
| 4: TUI 启动 | PASS | OpenTUI 渲染正确,状态栏正常 |
| 5: E2E gates | PASS | 13/13 (但 gates 与用户场景脱节) |
---
## 5. 用户核心痛点 (按严重度排序)
| 排名 | 问题 | 严重度 | 触发场景 |
|---|---|---|---|
| 1 | **shell.run 工具完全坏死** | P0 阻塞 | 任何命令执行 (编译、运行、git) |
| 2 | **追问上下文失效** (LLM 答"我没文件系统") | P0 阻塞 | 完成任务后问"现在什么情况" |
| 3 | **危险操作无 confirmation 路由** | P0 安全 | 删除、覆盖文件 |
| 4 | **Scheduler 早判完成** (假完成) | P0 阻塞 | 多步任务 |
| 5 | **TUI 无文本输入路径** | P0 用户体验 | TUI 实际只读 |
| 6 | **多文件任务漏写 / 覆盖** | P1 | 复杂任务 |
| 7 | **false-positive 成功 banner** | P1 | 任何报错场景 |
| 8 | **DONE 竞态导致提前退出** | P1 | LLM 同 turn 输出 tool+DONE |
| 9 | **ExecutorRole 转义顺序错误** | P1 | LLM 输出含 `\n` 字符 |
| 10 | **退出码误判** (stderr 噪音) | P2 | 排查时迷惑 |
---
## 6. 根因分析
E2E gates 框架本身存在结构性缺陷:
- 仅做静态检查tsc/depcruise/单元测试
- 不做端到端真实任务验证
- 缺"用户成功完成一个 C++ 任务"等综合场景 gate
需要新增的 gates
- 真实 LLM 端到端任务完成
- 多文件任务产出验证
- shell.run 工具真实执行
- 上下文追问验证
- 危险操作 confirmation 验证
---
## 7. 修复建议 (按优先级)
### 立即修复 (P0, 阻塞发布)
1. 修复 `shell.run` AsyncGenerator 序列化问题
2. 修复 Scheduler MONITORING 早判完成 bug
3. MainAgent 注入工具/项目上下文到 LLM 调用
4. 修复 confirmation 路由 (run.ts → MainAgent.handle_confirmation)
5. TUI 增加文本输入路径 (或移除 TUI 包装直接 CLI)
### 必须修复 (P1, 关键)
6. ExecutorRole 转义顺序
7. DONE 竞态检测
8. 多文件任务协调 (检查文件已存在)
9. Tool 失败时正确报失败 (false-positive 修复)
10. 真实集成测试加入 E2E gates
### 长期改进 (P2)
11. 退出码误判
12. has_running 误判
13. Worker stderr 去噪
14. Anthropic canonical content blocks 完整支持
---
## 8. 综合判断
**V1.0.0 Alpha 不应发布**
- 13/13 E2E gates passed 但**用户场景真实成功率极低**——这是 E2E gate 设计缺陷
- 5 个 P0 问题中3 个是用户每次使用都会触发的shell 坏死、追问失效、确认门失效)
- C++ 工作流工具链与主链路未联动FR-017 名存实亡
- TUI 渲染层 OK 但无输入路径FR-016 名存实亡
修复 P0 后必须**用真实任务重新端到端测试**,而不是用 E2E gates 替代。需要在 E2E gates 框架中**加入"真实 LLM 任务闭环"维度**,否则同样的审计盲点会再次出现。
**核心教训**: 代码质量指标 (tsc/stub 数/单元测试) 与用户可用性指标是正交的。质量分高 ≠ 产品可用。后续每个审计必须包含**真实任务端到端测试**作为硬性 gate。