Files
AirCoding/集成测试阶段opus审查结果.md
AirCoding 5e282a39b4 feat(round2+round3): 完整实现 A/B/C/D 主线 + round3-F/H 修复
Round2 主线:
- A: 事件落库地基 (RuntimeApp EventStore 单例 + 14 repo wiring)
- B: 执行体对齐 (read-before-edit, verification-before-completion)
- C: 界面对齐 (@opentui/solid, 删除 runtime 依赖)
- D: 经验闭环 (ExperienceMiner, DebuggerRole, CompactorRole)

Round2 补充修复:
- fail-on-missing 反作弊门禁
- projection-store-apply.test.ts 补写
- 3个空壳测试转行为 (evidence-store, recovery-impl, knowledge-store)
- ask 项目根支持 AIRCODING_PROJECT_ROOT
- Worker 事件契约修复 (task.attempt.started → checkpoint)

Round3-F: cpp 工具切换
- 删除 BuiltInToolRegistrar cpp.* 闭包
- 接入 toolchain-cpp 真实 CppToolRegistrar
- canonical envelope {status/output/metadata}
- ExecutorRole system prompt 对齐新工具名

Round3-H: Doctor 5 类报告
- toolchain (cmake/ninja/cppcheck/clangd/g++)
- display (X11/Wayland + ImageMagick)
- network (internet connectivity)
- provider (api_key/base_url/model/connectivity)

Secret 脱敏:
- 状态交接.md: sk- → \${OPENAI_API_KEY}
- .gitignore: 添加 .air/ .claude/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:13:16 +08:00

270 lines
19 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 集成测试阶段 opus 审查结果
**审计日期**: 2026-06-08
**项目**: AirCoding V1.0.0 Alpha
**审计模型**: Opus四子代理并行各持不同文档
**审计模式**: 四视角交叉审计(系统架构师 / 开发工程师 / 真实用户 / 测试工程师)
**审计基线**: 第一轮集成修复后commit ddefcbb
---
## 0. 总体结论
第一轮修复**方向正确、happy-path 可用**:四份历史报告的头号 P0工具契约 content/output、shell.run AsyncGenerator、Scheduler 假完成、MainAgent 无上下文、确认门断裂)已真实闭合并有功能级门禁守护。但本轮 opus 交叉审查发现**第一轮未触及的更深层架构问题与新缺陷**
- **架构师**:事件驱动地基在运行路径上是空的(`setRepositories` 从不调用、`task.created` 从不发出、ProjectionStore 运行时从不被事件驱动、TUI 非 OpenTUI、`air ask` 旁路整个调度架构)。判定**不可发布**。
- **工程师**:发现 failed 任务被调度机当成 COMPLETED、Worker exit/result 竞态、fs.edit 参数名不匹配、心跳不刷新。判定**不阻断内测,但对外 Alpha 前必修 P1-1/P1-2/P1-3**。
- **用户**:核心闭环真实跑通无幻觉无假成功,评分 8/10判定**可演示、Alpha 可发布带条件**。
- **QA**5 条新增 gate 中 4 条真实有效,但 P6 门禁引用不存在的测试文件形成假阳性Worker/C++/Projection 仍无真实集成测试。判定**不建议标记 Release READY**。
四视角分歧点在于"发布标准":用户/工程师视 happy-path 可用为 Alpha 达标;架构师/QA 视事实源与门禁完整性未达标。**综合判定:可内测演示(限 `air ask`),但不可对外 Alpha 发布,事件地基与失败处理必须先闭合。**
---
## 1. 系统架构师审查
### 架构结论
**不具备产品演示/Alpha 发布标准。** 第一轮闭合了一批工具契约/Worker 结果/确认门的真实 bug但**事件驱动这一架构地基在运行路径上仍是空的**domain 表从不被写入、`task.created` 从不发出、TUI 不是 OpenTUI、ProjectionStore 运行时从不被事件驱动。`air run` 能跑通"创建文件"是因为它绕过事实源,直接用 WorkerResult 内存对象 + 手工 snapshot 显示结果,掩盖了 INV-1/INV-5/FR-004 运行时整体失效。
存在两条割裂的执行实现:
- `air ask`CLI 进程内自带 LLM→工具循环`ask.ts:82-215`**完全不经过 Scheduler/Worker/IPC**。
- `air run`:走 MainAgent→Scheduler→WorkerManager→子进程→IPC→ExecutorRole 真实链路。
状态交接.md 的 UAT 全部用 `air ask` 验证,因此真实执行链路实际未被 UAT 覆盖。
### P0 问题表(阻断发布)
| # | 问题 | 证据 | 影响 |
|---|---|---|---|
| ARCH-P0-1 | **durable 事件从不投影到 domain 表**`EventStore.setRepositories()` 运行路径从未调用,仅测试调用。运行时 `project()` 内所有 repo 为 null每个 case 静默 no-op | `EventStore.ts:299-312``EventStore.ts:492-935``RuntimeApp.ts:80-84`(只 setTransactionManager | INV-1 运行时整体失效FR-004 不成立domain 表永远为空SQLite 不是事实源 |
| ARCH-P0-2 | **`task.created` 从不发出**`Scheduler.create_tasks()` 注释称发出但函数体只加内存 TaskGraph 节点,无 ingest | `Scheduler.ts:65-78`L76 注释谎称)对比 `Scheduler.ts:149` task.started 确实 ingest | tasks 表无 pending 行task.started 投影 update 不存在的行rebuild_from_db 永远查不到任务 |
| ARCH-P0-3 | **TUI 非 OpenTUI**,是手写 ANSI 转义渲染器;`@opentui/*` 零依赖零引用 | `TuiApp.tsx:27-310``tui/package.json` 无 opentui | 违反 baseline §3/§18 + 需求约束 #3 + FR-016任务 #158 标记 completed 与事实不符 |
| ARCH-P0-4 | **ProjectionStore 运行时从不被事件驱动**`apply()` 运行路径零调用run.ts 手工构造假 snapshot | `run.ts:59-72``run.ts:170-183`EventBus→ProjectionStore 无订阅 | 违反 baseline §18 + INV-5 + FR-016TUI 状态与 DB 可任意不一致 |
| ARCH-P0-5 | **`air ask` 旁路整个调度/Worker 架构**,自带内联执行循环 | `ask.ts:71-78``ask.ts:82-215` | FR-007/FR-008 在主力 demo 命令上不成立;两套执行语义割裂 |
### P1 问题表
| # | 问题 | 证据 |
|---|---|---|
| ARCH-P1-1 | ArchitectureDesigner 发出事件 `session_id:''` 必抛错被 `.catch(()=>{})` 吞掉 | `ArchitectureDesigner.ts:59-73``EventIngestor.ts:204` |
| ARCH-P1-2 | MainAgent 15 态多数无真实触发路径AWAITING/SCHEDULING/SUMMARIZING/ERROR/TERMINATED 无进入点) | `MainAgent.ts:15-30,75-122,278-313` |
| ARCH-P1-3 | Scheduler 13 态部分空壳过场COLLECTING_RESULTS/REVIEWING_WAVE 直接切换200ms 轮询 | `Scheduler.ts:340-356,329-331` |
| ARCH-P1-4 | CapabilityRegistry 接入但运行路径无 discover/load恒空 | `CapabilityRegistry.ts:48``RuntimeApp.ts:71-75` |
| ARCH-P1-5 | 真实 C++ 工具链 CppToolRegistrar 未进主链路live 用 BuiltInToolRegistrar 简化版 | `grep CppToolRegistrar` 无命中;`BuiltInToolRegistrar.ts:288-364` |
| ARCH-P1-6 | contracts ToolRegistry 接口签名与实现背离,靠 `as any` 掩盖 | `contracts/src/tool.ts:100-106` vs `ToolRegistry.ts:91,128,60` |
---
## 2. 开发工程师审查
### 工程结论
第一轮修复方向正确、主路径可用,`tsc --noEmit` 0 错误。但发现第一轮未覆盖的真实缺陷,两项触及"诚实性/正确性"底线failed 任务被伪装成 COMPLETED、Worker exit/result 竞态。happy-path 能跑通UAT 结论可信,但"任务失败"会被系统性伪装成成功。
### P1 问题表(对外发布前必修)
| # | 问题 | 证据 | 修复方向 |
|---|---|---|---|
| ENG-P1-1 | **failed 任务被调度机当成 COMPLETED**PLANNING_WAVE 算 `remaining=pending+running`failed 不计入 → COMPLETEDREPAIRING_OR_CONTINUING 的 `if(failed>0){}` 是空壳retry_planner 从未调用 | `Scheduler.ts:117-122,358-368``grep retry_planner.` 无调用 | PLANNING_WAVE 终态区分 failed → BLOCKED/TERMINATED 或经 RetryPlanner 重试run_until_idle 终态反映失败 |
| ENG-P1-2 | **Worker 退出/结果竞态**WorkerProcess 用 `'exit'``'close'`worker report_result 后立即 process.exit(0)exit 可能先于最后一行 stdout 解析handle_worker_exit 误判 failed | `WorkerProcess.ts:134-142``WorkerManager.ts:339-362``main.ts:92-93` | 监听 `'close'`;或 handle_worker_exit 对无结果做微任务让步后复查worker 端 exit 前 await stdout drain |
| ENG-P1-3 | **fs.edit 参数名不匹配Agent 调用恒失败**:执行器读 `{find,replace}`ExecutorRole/ask.ts 传 `{old_str,new_str}`find 恒 undefined → "Exact text not found" | `tools/fs/index.ts:191-197``ExecutorRole.ts:292``ask.ts:107` | 统一参数名(执行器接受 old_str/new_str 或兼容 find=old_str补 Agent 路径编辑回归 |
| ENG-P1-4 | **Worker 心跳不刷新**worker.heartbeat 无 WorkerManager 处理器record_heartbeat 仅派发时调一次,>5min 任务被判 stalled>10min 被 cancel | `WorkerManager.ts:162-257``Scheduler.ts:177,193``AgentMonitor.ts:90-104` | WorkerManager 注册 worker.heartbeat/checkpoint → record_heartbeat 刷新 |
### P2 问题表
| # | 问题 | 证据 |
|---|---|---|
| ENG-P2-1 | shell.run 流式块顺序错乱且重复(退出后先聚合 stdout/stderr 再 drain 增量 chunks | `tools/shell/index.ts:94-135` |
| ENG-P2-2 | ServiceRegistry 为分叉死代码DB 路径与 RuntimeApp 不一致 | `ServiceRegistry.ts:38-49``RuntimeApp.ts:64-66` |
| ENG-P2-3 | 内建工具成功 envelope 夹带遗留 `call_id/tool_name/type:'text'` 顶层字段,靠 Promise<any> 不报错 | `BuiltInToolRegistrar.ts:180-183` 等 18 处 |
| ENG-P2-4 | ProviderManager.complete_text 硬编码 anthropic provider_id/canonical_format | `ProviderManager.ts:110-119``ask.ts:13,43` |
| ENG-P2-5 | process.kill 工具无权限门perms 全 false可 kill 任意 PID | `BuiltInToolRegistrar.ts:118-120,190-201` |
| ENG-P2-6 | 空 catch 吞错 | `ContextAssembler.ts:127``run.ts:155,157` |
| ENG-P2-7 | ArchitectureDesigner 事件 session_id 为空 | `ArchitectureDesigner.ts:59-73` |
### 第一轮修复正确性核验表
| 第一轮声称 | 核验结论 |
|---|---|
| 内建工具改 canonical {status,output,metadata} | ✅ 部分status/output 已加,但仍夹带 type:'text'/顶层 call_id |
| grep content: 无残留 | ⚠️ 残留多为合法fs.read 输出、layer.content、IPC payload |
| shell.run 两路径正确 | ✅ 消费正确;⚠️ 流式块顺序/重复有缺陷 |
| Scheduler 按 status 终结 | ✅ 已删假完成逻辑;❌ 但 failed 在 PLANNING_WAVE 被当已完成 |
| WorkerProcess on_exit 覆盖退出语义 | ⚠️ 映射对,但 exit/result 顺序竞态未解决 |
| MainAgent 真用 ContextAssembler | ✅ assemble 注入;⚠️ answer 模式 agent_type 误用 'executor' |
| run.ts pendingConfirmation 健壮 | ✅ 空输入/y/n/非y-n 路由成立 |
| ExecutorRole 严格 DONE/失败不 completed/保留原文 | ✅ 全部成立 |
| CapabilityRegistry/ArchitectureDesigner 接入 | ✅ 实例化绑定;但 ArchDesigner 事件 session_id 空P1、Capability 运行时空集 |
| ContextAssembler L3/L6/L8 | ✅ project_files/evidence 签名/tool role 均落地 |
| release.ts findRepoRoot/findBun | ✅ 成立 |
---
## 3. 真实用户 / UAT 审查
### 用户体验评分8/10
核心闭环(创建文件、上下文问答、多文件生成、危险操作确认门、调度执行)全部真实跑通,无幻觉、无假成功。扣分来自 `air run` TUI/readline 交织、新建项目立即 doctor 失败两个体验摩擦点。
### 测试矩阵
| # | 场景 | 结果 | 观察 |
|---|---|---|---|
| 1 | air init | PASS | 创建 .air 结构 + project.json退出码 0 |
| 2 | air ask 创建 hello.txt | PASS | fs.write磁盘内容精确 `HelloWorld`(10B) |
| 3 | air ask 项目有哪些文件 | PASS | answer 模式准确列出真实文件,无幻觉,未列 .air |
| 4 | air ask C++ + CMakeLists | PASS | main.cpp(97B)+CMakeLists.txt(153B)g++ 实测编译运行输出 Hello World |
| 5 | air run 中文删除 + n | PASS | 命中确认门,"Cancelled. No task was created.",文件保留 |
| 6 | air run 中文删除 + y | PASS | 确认后派发 worker 走 shell.run文件被删除 |
| 7 | air doctor | PASS含告警 | 6 项全绿project_structure 报缺 package.json [fixable] |
| 8 | air e2e | PASS | 14/14 gates |
| 9 | /help /tools /results | PASS | 清晰可理解 |
### 最痛问题
1. `air run` TUI 与 readline 双写终端(中)——全屏 TUI 与行式 `> ` 提示符混在同一 stdoutworker 运行时刷屏交错。
2. 新建项目 doctor 立即失败(低-中——init 不生成 package.json紧接 doctor 报 FAIL负面第一印象。
3. glm-5.1 reasoning token 消耗(信息项)——低 max_tokens 时正文可能为空。
### False-positive 风险:低
文件产物均落盘后 cat/ls/g++ 实测复核删除查磁盘确认cpp.build 失败是真实无 cmake优雅降级如实说明。唯一留意/results 是 run 进程内存态,重启不持久。
### 是否可演示/可发布
- **可演示:是**(建议用 air ask输出干净
- **可发布 Alpha带条件**——功能完整、门禁 14/14、确认门中英文生效达 Alpha 线;正式版前收口 run 输入统一、init/doctor 体验、/results 持久化。
---
## 4. QA / 发布门禁审查
### QA 总结
第一轮源码修复方向正确,`release-critical-gates.test.ts` 是本项目第一次出现真正执行被测代码的发布级门禁。但门禁整体三个结构性问题未达"可发布"
1. **P6 门禁形同虚设**——引用的 `projection-store-apply.test.ts` 不存在bun 静默跳过缺失路径,仅靠 workspace-enum.test.ts 让 gate 变绿,**假阳性**。
2. **关键集成路径无真实验证**——28 个测试文件无一真正 spawn worker 子进程、无一真正编译运行 C++。
3. **源码字符串断言占比过高**——28 个测试中 18 个64%)用 readFileSync + toContain只证明"代码还在"不证明"功能正确"。
### 测试矩阵(真实运行)
| 项 | 实测结果 | 备注 |
|---|---|---|
| tsc | EXIT=01.2s | 增量编译(未 clean rebuild|
| air e2e | 14/14 passed4.9s | 见逐 gate 评估 |
| release --dry-run | 3/3 — READY8.6s | |
| P4 Worker IPC | 21 pass/53 expect | 无真实 spawn |
| P5 C++ | 5 pass/10 expect | 仅 1 文件纯源码断言,无真实编译 |
| P8 全回归 | 142 pass/346 expect/21 files | 体量真实但大量字符串断言 |
### 新增 gate 有效性评估
`release-critical-gates.test.ts`5 条)——质量最高:
| Gate | 判定 |
|---|---|
| 1 tool 用 output 非 content | ✅ 真实调用工具断言 output 存在/content undefined |
| 2 shell.run finalcall+streaming | ✅ 真跑 printf ok 断言 exit_code/stdout/is_final |
| 3 Scheduler 不假完成 | ✅ 命中核心 bug workerManager 是字面量 mock |
| 4 MainAgent answer 用 context | ✅ 真实 ContextAssembler + 临时文件 |
| 5 危险操作 CONFIRMING→IDLE | ✅ 真实 MainAgent 状态机 |
5 条中 4 条真实执行被测逻辑——**合格,是门禁里唯一可信功能层**。
`run-command-regression.test.ts`3 条)——全部源码字符串断言,不执行 run 命令,仅防回退快照。
### 仍缺失的关键 gate
| # | 缺失项 | 风险 |
|---|---|---|
| G1 | Worker 真实 spawn + IPC round-tripworker-fixture 自承认 stub | 最高 |
| G2 | 真实 C++ build/run | 高 |
| G3 | Worker exit consistency 端到端 | 高 |
| G4 | Projection rebuild/replay门禁引用文件不存在假绿 | 高 |
| G5 | False-positive 成功检测ExecutorRole 无任何测试) | 高 |
| G6 | complex C++ build/run e2e | 中-高 |
### QA P0/P1/P2
**P0阻断发布**
- QA-P0-1 修复 P6 假阳性门禁(补 projection-store-apply.test.ts 或移除路径并 fail-on-missing
- QA-P0-2 e2e runTest 加 fail-on-missing任一路径不存在直接 fail
- QA-P0-3 Worker 真实 spawn round-trip 接入 P4
**P1**
- QA-P1-1 真实 C++ build/run e2e
- QA-P1-2 ExecutorRole DONE/失败不 completed 功能测试
- QA-P1-3 Worker exit→result 一致性端到端
- QA-P1-4 Projection rebuild/replay 覆盖
**P2**
- QA-P2-1 降低源码字符串断言占比64%
- QA-P2-2 tsc clean rebuild 验证
- QA-P2-3 P7 direct-mode mock 标注边界
### 发布门禁建议
**当前不建议标记 Release READY**,尽管 release --dry-run 3/3。理由release 的绿建立在 e2e 14/14 之上,而 14/14 里 P6 假阳性、P4/P5 源码断言冒充集成。最低放行QA-P0-1/2/3 完成 + 手工 UAT 脚本化为可重放 e2e 纳入门禁。
---
## 5. 四视角交叉综合
### P0 汇总(阻断对外发布)
| # | 问题 | 来源视角 | 根因 |
|---|---|---|---|
| 1 | EventStore.setRepositories 运行路径从不调用 → domain 表恒空 | 架构师 | INV-1/FR-004 地基失效 |
| 2 | task.created 从不发出 | 架构师 | 事件溯源断链 |
| 3 | failed 任务被调度机当成 COMPLETED | 工程师 | 失败伪装成功(触碰红线)|
| 4 | Worker exit/result 竞态误判 failed | 工程师 | 成功也可能被误判 |
| 5 | ProjectionStore 运行时不被事件驱动run.ts 手工 snapshot | 架构师 | INV-5/FR-016 |
| 6 | P6 门禁引用不存在文件,假阳性 | QA | 门禁完整性 |
| 7 | air ask 旁路调度/Worker 架构 | 架构师 | FR-007/008 双实现割裂 |
| 8 | TUI 非 OpenTUI | 架构师 | FR-016/约束#3 |
### P1 汇总
fs.edit 参数不匹配恒失败工程师、Worker 心跳不刷新工程师、ArchitectureDesigner 事件 session_id 空被吞(架构师+工程师、MainAgent/Scheduler 状态机空壳架构师、CapabilityRegistry 运行时空集(架构师)、真实 C++ 工具链未进主链路架构师、Worker/C++/Projection 无真实集成 gateQA
### 与前几轮对比
| 维度 | 前几轮 | 第一轮修复后(本轮实测) |
|---|---|---|
| 工具契约 output/content | 头号 P0 | ✅ 已修 + 真实 gate |
| shell.run AsyncGenerator | 阻断 | ✅ 已修 + 真实验证 |
| Scheduler 假完成 | 阻断 | ✅ 已删假逻辑;❌ 但 failed→COMPLETED 新问题 |
| MainAgent 上下文/确认门 | 阻断 | ✅ 已修 + 真实验证 |
| ProjectionStore-only/TUI snapshot | Deepseek/Gpt5.5 P1 | ❌ 未修(更深:本轮查实 setRepositories 从不调用)|
| durable task events 主路径 | Gpt5.5 P0 | ❌ 未修根因task.created 不发出)|
| Worker 真实 spawn | 一直缺失 | ❌ 仍缺失(自承认 stub|
| C++ 真实 build/run | 一直缺失 | ❌ 仍缺失 |
**本轮新发现**setRepositories 从不调用最严重、task.created 不发出、TUI 非 OpenTUI、failed→COMPLETED、Worker exit/result 竞态、fs.edit 参数不匹配、P6 假阳性门禁、ArchitectureDesigner 事件被吞。
---
## 6. 发布建议与修复优先级
**综合判定:可内测演示(限 air ask不可对外 Alpha 发布。**
14/14 e2e 与 3/3 release 全绿,但这些门禁不触碰本轮 P0 任何一条——绿灯与可用性正交,这正是 MiniMax 已警告、本轮仍重演的盲点。
按修复优先级(遵守"不接受架构降级"原则,全部为补齐而非删功能):
1. **闭合事件地基**RuntimeApp.start 调用 `eventStore.setRepositories({...})` 注入全部 domain repocreate_tasks 真正 ingest task.created。P0-1/P0-2
2. **修复失败处理**Scheduler PLANNING_WAVE 终态区分 failed接线 RetryPlannerrun_until_idle 反映失败。P0-3
3. **修复 Worker 竞态**WorkerProcess 监听 'close' 或退出前复查 result。P0-4
4. **统一执行路径**air ask 复用 Scheduler→Worker删除 CLI 内联循环。P0-7
5. **收口 Projection 事实源**EventBus→ProjectionStore.apply→TUI删 run.ts 手工 snapshot。P0-5
6. **门禁完整性**e2e runTest fail-on-missing补 P6 真实测试;补 Worker spawn / C++ build / false-positive / projection rebuild gate。P0-6 + QA-P0
7. **TUI 技术栈归位**:接 @opentui/*或走正式架构变更声明不可默默降级P0-8
8. P1 批量fs.edit 参数、心跳刷新、ArchDesigner session_id、状态机补齐、CapabilityRegistry 加载、真实 C++ 工具链进主链路。
**核心教训重申**代码质量指标tsc/门禁数)与产品可用性指标正交。第一轮把"被测代码从不执行"推进到"核心修复点被真实执行"是实质进步但门禁完整性fail-on-missing与集成层真 spawn / 真编译 / 真事件落库)仍是发布前硬缺口。
---
*审计模型: Opus4 子代理并行)*
*审计时间: 2026-06-08*
*仓库状态: 源码未改动(只读审计)*