- 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>
16 KiB
Executable File
集成测试阶段 Deepseek 审查结果
审计日期: 2026-06-05 项目: AirCoding V1.0.0 Alpha 审计模式: 四视角交叉审计(系统架构师 / 开发工程师 / 真实用户 / 测试工程师)
0. 前置验证
TypeScript 类型检查: PASS (0 errors) E2E Gates: 13/13 PASS
1. 系统架构师审查报告
一、实际运行验证结果
三项测试全部通过:
- Test 1 (tsc): 零类型错误,17 个 contracts 文件完整
- Test 2 (E2E gates): 13/13 通过,覆盖 P0 monorepo/depcruise/tsc + P1-P8 回归测试 + SEC 命令注入 + CAP 能力信任
- Test 3 (集成测试): RuntimeApp 启动成功,MainAgent regex 正确将 "Create hello.txt" 路由为 delegate,Scheduler 调度完成,文件真实创建(内容 "HELLO from AirCoding"),39 个工具注册完毕,fs.read/fs.list 正确返回
二、FR 实现情况
真正实现的 FR(完整端到端链路可通)
| FR | 描述 | 实现状态 |
|---|---|---|
| FR-001 | CLI 启动与项目初始化 | ✅ runCommand 含 auto-init、项目检测 |
| FR-002/003 | 项目本地状态与 Session 持久化 | ✅ SQLite session.db + 16 Repository |
| FR-005 | Main Agent 15 状态机 + 分类 | ✅ regex/llm 双模式 + chat_with_llm |
| FR-007 | Scheduler 13 状态机 + TaskGraph | ✅ 完整状态链 + WavePlanner/RetryPlanner |
| FR-008 | 5 种 Worker Agent + NDJSON IPC | ✅ Executor/Reviewer/Debugger/Compactor/ExperienceMiner |
| FR-009 | 执行原语 (code block + tool_call 解析) | ✅ ExecutorRole.parse_actions 三种解析模式 |
| FR-010 | ToolRegistry 39 工具 | ✅ fs/shell/git/cpp/project/artifact 全覆盖 |
| FR-013 | Provider 层 (Anthropic + OpenAI-compatible) | ✅ 适配器边界清晰 |
名存实亡的 FR(结构存在但深度不足)
| FR | 描述 | 问题 |
|---|---|---|
| FR-004 | 事件驱动运行时 | EventBus/Store/Ingestor 类齐全,但 run.ts 同步阻塞 push Scheduler,未真正依赖事件总线驱动状态流转 |
| FR-006 | Architecture Designer | 影响评估类完整,但 MainAgent.classify() 路由到 delegate 时从未调用 ArchitectureDesigner——架构审查是死代码 |
| FR-011 | Permission/Security | PermissionEngine 六层模型存在,但 ToolRegistry.call() 未显示逐调用通过 PermissionEngine 评估的证据 |
| FR-012 | Plugin/Capability Foundation | CapabilityRegistry 存在但运行时未深入集成 |
| FR-016 | TUI/HUD | TuiApp.ts 渲染 ANSI 视图,但 run.ts 手工推送构造数据而非 ProjectionStore 作为单一可信源 |
| FR-017 | C++ 工作流 | cpp.* 工具存在但 debug→fix→review 证据闭环未端到端验证 |
三、架构基线遵守评估
遵守良好:
- 依赖方向正确:tui 不 import runtime,depcruise 零违规
- IPC 协议正确:NDJSON over stdio,worker.ready 握手,heartbeat 心跳
- contracts 包独立,17 个文件覆盖所有核心类型
关键偏离:
- 依赖边界: runtime/cli 直接依赖 @aircoding/llm 创建适配器——架构基线要求 runtime 通过接口间接使用 llm
- 事件驱动承诺未兑现: 基线 §6 声明 "AirCoding is event-driven",但主循环是同步 pull 模式
- TUI 消费方式偏离: 基线 §18 要求 "HUD/TUI consumes ProjectionStore only",但 TuiApp 接收手工 snapshot 而非 ProjectionStore hydrate/apply
四、状态机评估
Scheduler 状态机 (13 状态): 逻辑完整但 MONITORING 状态存在竞态——200ms 轮询 vs 事件驱动等待,has_running() 与 graph.update_status 之间的窗口可能导致假完成。
MainAgent 状态机 (15 状态): CONFIRMING 状态仅在 regex 检测 breaking 词时触发,但确认回调在 run.ts 中断裂。ARCHITECTURE_DESIGNING 和 ARCHITECTURE_REVISING 状态无实际触发路径。
五、架构师结论
V1.0.0 Alpha 已实现核心能力闭环:CLI→MainAgent→Scheduler→Worker→LLM→Tool→File 的完整链路可以跑通并创建真实文件。三个关键缺口:(a) Architecture Designer 是死代码,(b) PermissionEngine 未在 ToolRegistry 调用路径中逐次生效,(c) Scheduler 轮询等待对长任务存在竞态窗口。
2. 开发工程师审计报告
P0 问题
1. BuiltInToolRegistrar 中 18 个工具的结果形状不一致 (create_real_executor.ts:175-437)
create_real_executor 返回 { status, call_id, tool_name, type, content, metadata },但正确的 ToolResultEnvelope 形状使用 output 而非 content。影响:fs.stat, process.kill, cpp.detect, cpp.build, cpp.test, doctor.run, project.scan, debug.run, gui.screenshot, network.capture 等。
WorkerManager 第 191 行执行 result.output || result.error || {}——每个通过 worker IPC 调用这些工具的任务都会收到空内容 {}。ExecutorRole 的 LLM 循环看不到任何工具输出,导致每项任务崩溃或无限循环。
P1 问题
2. shell.run 生成器未被解包 (shell/index.ts)
createShellExecutor 返回 async function*。ToolRegistry.execute_branch 直接调用执行器并返回结果——对生成器函数来说,返回的是生成器对象而非 ToolResultEnvelope。WorkerManager 会再次回退到 {}。shell 命令无法通过 worker chain 工作。
3. MainAgent.classify_via_llm API 不匹配 (MainAgent.ts:185)
classify_via_llm 调用 this.provider_manager.complete(...),但 run.ts 创建的提供者只挂接了 complete_text。如果 classify_mode: 'llm',provider_manager.complete is not a function 会导致崩溃。
4. Scheduler DISPATCHING 缺少 await (Scheduler.ts:163)
this.worker_manager.spawn(...) 没有 await——方法返回 Promise,但计划程序立即进入 MONITORING。worker 进程在调度程序检查 has_running() 时可能尚未准备好,任务被错误标记。
P2 问题
5. 资源泄漏: run.ts progressInterval 在 run_until_idle() 拒绝时永不清理
6. 空 catch 块: scanDir 中的 catch {} 默默丢弃所有文件系统错误
7. 延迟浪费: Scheduler.step() 使用 setTimeout(r, 200) 轮询而非事件驱动
8. require() 调用: DoctorService.check_capability_deps 使用同步 require('child_process') 而非静态导入
用户可感知的核心影响
用户输入 "创建一个 C++ hello world 程序",MainAgent 正确分类委托,Scheduler 调度到 DISPATCHING,Worker 启动并调用 LLM。LLM 响应代码块,ExecutorRole 调用 fs.write(成功),然后 LLM 调用 cpp.detect(通过 IPC)。WorkerManager 收到成功结果但提取 result.output 为 undefined,向 worker 发送 {}。LLM 看不到工具输出,困惑,重试,15 轮后被 BLOCKED。用户只看到 "Task blocked" 而没有文件。根本原因是形状不一致——约 50% 的工具注册表通过 worker 路径被静默破坏。
3. 真实用户测试报告
测试结果汇总
| 测试 | 命令 | 结果 | 关键观察 |
|---|---|---|---|
| Test 1 | air init |
PASS | 创建 6 个子目录 + project.json,project_id 自动生成 |
| Test 2 | air doctor |
PASS | 5/6 检查通过;project_structure 报 FAIL(缺少 package.json/tsconfig.json),标记为 fixable |
| Test 3 | air ask 创建hello.txt |
PASS | 委托模式正常工作,1 轮完成,文件内容正确 (HelloWorld, 10 bytes) |
| Test 4 | air ask C++项目 |
部分通过 | main.cpp 和 CMakeLists.txt 创建成功,但 cpp.build 和 shell.run 报错,LLM 却声称 "程序已成功编译" |
| Test 5 | air run TUI |
PASS | TUI 渲染正常,面板和快捷键显示正确,输入 q 退出干净 |
| Test 6 | air e2e |
PASS | 13/13 gates 全部通过 |
| Test 7 | air history / session list |
PASS | 三个 session 被正确记录 |
用户最痛 3 个问题
-
doctor 语义误导: "All checks: FAIL" 用红色大字——但只是缺少 package.json 和 tsconfig.json 模板文件。新用户看到 FAIL 会以为产品坏了,其实项目完全正常工作。
-
结果不可信——工具报错但 LLM 说成功: Test 4 中
cpp.build返回了 Error,shell.run返回了Error: undefined,但最终输出却写着 "程序已成功编译并输出 Hello World"。用户分不清到底是真成功了还是 LLM 幻觉。这是信任问题。 -
session/history 毫无辨识度:
air history输出裸 session_id 数字加大小,不知道哪个 session 干了什么。用户做了 3 次 air ask,回头想找之前的任务,面对 3 个无区分的数字完全懵了。
用户体验评分: 5/10
扣分项:
- 工具错误被吞掉(-2 分):信任问题,用户无法区分真实成功和幻觉
- doctor 诊断语义不准确(-1 分):把可修复警告当成硬失败
- session/history 不可辨识(-1 分):无法快速定位之前的任务
- 多轮交互体验存疑(-1 分)
加分项: init 流程干净、TUI 渲染正常、e2e 全绿、响应速度快
4. QA 测试报告
测试矩阵
| Test | 描述 | 结果 | 严重级别 |
|---|---|---|---|
| T1 | TypeScript 类型检查 | PASS | - |
| T2 | E2E Gates (13门) | 13/13 PASS | - |
| T3 | 简单文件创建 | PASS (Worker 退出信号异常) | P1 |
| T4 | C++ AI 终端 (FR-017) | PASS (Worker 退出信号异常) | P1 |
| T5 | 追问上下文验证 | FAIL | P0 |
详细测试分析
T3: 简单文件创建 — PASS (P1 警告)
hello.txt成功创建,内容HELLO(6 字节),分类正确为delegate。- 警告: Worker 输出
[Worker] exited with code 0 (error): Unrecoverable error occurred。Worker 以 exit code 0 退出但附加 "(error)" 标记——信号噪音导致诊断困难。
T4: C++ AI 终端 (FR-017) — PASS (P1 警告)
- 成功生成
main.cpp(13824 字节,含#include和main())、CMakeLists.txt和script.sh。 - 功能需求基本满足:代码具备 C++ 程序骨架。
- 警告: 同样出现 Worker exit code 噪音。
T5: 追问上下文验证 — FAIL (P0)
- LLM 回答: "抱歉,我目前无法直接访问您的本地文件系统,所以不知道您的项目里有哪些源代码文件。"
- 根因:
MainAgent.chat_with_llm()方法(MainAgent.ts:102-122)完全绕过 ContextAssembler。仅发送一条裸 system prompt'You are AirCoding, an AI coding assistant...',没有注入项目根路径、文件列表、对话历史等任何上下文。 - 影响: 所有问答类交互(占总交互的很大比例)都没有项目上下文感知能力。
问题分类
P0 — 阻断发布
- MainAgent.chat_with_llm 无 ContextAssembler 集成 (MainAgent.ts:102-122) 修复方向:chat_with_llm 应接收 AssemblyContext 参数,先通过 ContextAssembler 组装上下文再发给 LLM。
P1 — 重要缺陷
-
Worker 退出信号不一致 (T3/T4): Worker 以 exit code 0 退出但附带 "(error)" 字符串——应排查 Worker 退出码逻辑。
-
BuiltInToolRegistrar 结果形状不一致 (工程审计 P0): 18 个工具的返回形状使用
content而非output,导致 WorkerManager 收到空结果。 -
shell.run 生成器未解包 (工程审计 P1)
P2 — 改进项
- 无 workspace files 快照层:ContextAssembler 缺少 project_files 提示层
- Scheduler DISPATCHING 缺少 await (竞态)
- classify_via_llm API 不匹配
- history/session 输出缺乏可辨识性
E2E Gates 补充建议
当前 13 个 gate 全部基于单元测试和静态检查,缺少以下端到端 gate:
| 优先级 | 建议 Gate | 检查内容 |
|---|---|---|
| P0 | Answer-Mode Context Gate | 验证 chat_with_llm 回复包含项目文件信息 |
| P0 | Simple E2E Create Gate | LLM 驱动的文件创建端到端 |
| P0 | Tool Output Shape Gate | 验证所有 39 个工具的返回形状符合 ToolResultEnvelope |
| P1 | Complex E2E Generate Gate | 多文件代码生成端到端 |
| P1 | Worker Exit Consistency Gate | 验证 Worker exit code 0 不与 "(error)" 同时 |
| P1 | Follow-up Context Gate | 新建文件后追问,验证 Agent 感知已有文件 |
| P2 | ContextAssembler Integration Gate | 验证所有 Agent 路由经过 ContextAssembler |
5. 四视角交叉审计综合结论
P0 问题汇总(3 项,阻塞发布)
| # | 问题 | 来源视角 | 触发条件 | 影响范围 |
|---|---|---|---|---|
| 1 | MainAgent.chat_with_llm 无项目上下文 | QA + 架构师 + 用户 | 所有 answer 交互 | 用户追问项目状态时 LLM 100% 幻觉 |
| 2 | BuiltInToolRegistrar 18 个工具结果形状不一致 | 工程师 + QA | Worker IPC 调用这些工具 | 50% 工具通过 worker 返回空结果 |
| 3 | shell.run 生成器未解包 | 工程师 | 任何 shell.run 调用 | shell 命令 100% 失败 |
P1 问题汇总(5 项)
| # | 问题 | 来源视角 |
|---|---|---|
| 4 | Worker 退出信号噪音 | QA + 架构师 |
| 5 | Scheduler MONITORING 竞态窗口 | 架构师 + 工程师 |
| 6 | classify_via_llm API 不匹配 | 工程师 |
| 7 | Scheduler DISPATCHING 缺少 await | 工程师 |
| 8 | 工具错误被吞掉 + false-positive 成功 | 用户 |
P2 问题汇总(5 项)
| # | 问题 | 来源视角 |
|---|---|---|
| 9 | Architecture Designer 是死代码 | 架构师 |
| 10 | PermissionEngine 未在调用路径生效 | 架构师 |
| 11 | history/session 输出不可辨识 | 用户 |
| 12 | doctor 语义误导 (warn→FAIL) | 用户 |
| 13 | 资源泄漏 + 空 catch | 工程师 |
根因分析
本轮与前几轮审计相同的模式再次出现:
-
E2E gates 框架盲区 — 13/13 gates pass 但真实场景 5/13 (38%) 核心功能失败。gates 检查代码质量(tsc、depcruise、单元测试),不检查功能可用性(工具结果形状、LLM 上下文注入、Worker IPC 往返完整性)。
-
集成测试仅覆盖 Happy Path — 测试创建 hello.txt 验证了最简单场景,但未覆盖 C++ 多文件任务、shell 执行、追问上下文等复杂度递增的场景。
-
组件间接口契约缺失 — BuiltInToolRegistrar 返回
{ content }vs WorkerManager 期望{ output },这种接口不一致在组件隔离开发时无法发现,只能在集成时暴露。缺少跨组件的 TypeScript 接口强约束。 -
审计员不跑端到端 — 所有审计员检查了 MainAgent.ts 的方法签名、Chat 函数的类型正确性,但没有人实际问一句 "LLM 回到 '我没有文件系统访问' 合理吗?"
发布建议
不建议发布 V1.0.0 Alpha。3 个 P0 问题影响了核心体验链路:
- P0-1: 用户追问项目状态 → LLM 答"我无法访问文件系统"(每次触发)
- P0-2: Worker 调用 cpp.detect / doctor.run / project.scan 等 → 收到空结果 → LLM 困惑 → 任务假完成或失败
- P0-3: LLM 调用 shell.run → 永远返回
undefined→ 编译/运行/测试全部失败
修复 P0 后,必须:
- 将真实任务端到端集成测试加入 E2E gate suite
- 统一工具结果形状为 contracts.ToolResultEnvelope (使用
output字段) - MainAgent 注入 ContextAssembler 到所有路由
6. 修复优先级矩阵
| 优先级 | 数量 | 问题 |
|---|---|---|
| P0 | 3 | chat_with_llm 无上下文、工具结果形状不一致、shell.run 坏死 |
| P1 | 5 | Worker exit 噪音、Scheduler 竞态、classify API、spawn await 缺、false-positive |
| P2 | 5 | ArchDesigner 死代码、PermissionEngine 未生效、history 不可读、doctor 语义、资源泄漏 |
| E2E gates 增强 | 6 | 新增 answer-mode context gate, simple E2E create gate, tool output shape gate 等 |
总计: 需修复 13 个代码问题 + 新增 6 个 E2E gate