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>
19 KiB
Executable File
集成测试阶段 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-016;TUI 状态与 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 不计入 → COMPLETED;REPAIRING_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 不报错 |
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 | 清晰可理解 |
最痛问题
air runTUI 与 readline 双写终端(中)——全屏 TUI 与行式>提示符混在同一 stdout,worker 运行时刷屏交错。- 新建项目 doctor 立即失败(低-中)——init 不生成 package.json,紧接 doctor 报 FAIL,负面第一印象。
- 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 是本项目第一次出现真正执行被测代码的发布级门禁。但门禁整体三个结构性问题未达"可发布":
- P6 门禁形同虚设——引用的
projection-store-apply.test.ts不存在,bun 静默跳过缺失路径,仅靠 workspace-enum.test.ts 让 gate 变绿,假阳性。 - 关键集成路径无真实验证——28 个测试文件无一真正 spawn worker 子进程、无一真正编译运行 C++。
- 源码字符串断言占比过高——28 个测试中 18 个(64%)用 readFileSync + toContain,只证明"代码还在"不证明"功能正确"。
测试矩阵(真实运行)
| 项 | 实测结果 | 备注 |
|---|---|---|
| tsc | EXIT=0,1.2s | 增量编译(未 clean rebuild) |
| air e2e | 14/14 passed,4.9s | 见逐 gate 评估 |
| release --dry-run | 3/3 — READY,8.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 final(call+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-trip(worker-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 无真实集成 gate(QA)。
与前几轮对比
| 维度 | 前几轮 | 第一轮修复后(本轮实测) |
|---|---|---|
| 工具契约 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 已警告、本轮仍重演的盲点。
按修复优先级(遵守"不接受架构降级"原则,全部为补齐而非删功能):
- 闭合事件地基:RuntimeApp.start 调用
eventStore.setRepositories({...})注入全部 domain repo;create_tasks 真正 ingest task.created。(P0-1/P0-2) - 修复失败处理:Scheduler PLANNING_WAVE 终态区分 failed,接线 RetryPlanner;run_until_idle 反映失败。(P0-3)
- 修复 Worker 竞态:WorkerProcess 监听 'close' 或退出前复查 result。(P0-4)
- 统一执行路径:air ask 复用 Scheduler→Worker,删除 CLI 内联循环。(P0-7)
- 收口 Projection 事实源:EventBus→ProjectionStore.apply→TUI,删 run.ts 手工 snapshot。(P0-5)
- 门禁完整性:e2e runTest fail-on-missing;补 P6 真实测试;补 Worker spawn / C++ build / false-positive / projection rebuild gate。(P0-6 + QA-P0)
- TUI 技术栈归位:接 @opentui/*,或走正式架构变更声明(不可默默降级)。(P0-8)
- P1 批量:fs.edit 参数、心跳刷新、ArchDesigner session_id、状态机补齐、CapabilityRegistry 加载、真实 C++ 工具链进主链路。
核心教训重申:代码质量指标(tsc/门禁数)与产品可用性指标正交。第一轮把"被测代码从不执行"推进到"核心修复点被真实执行"是实质进步,但门禁完整性(fail-on-missing)与集成层(真 spawn / 真编译 / 真事件落库)仍是发布前硬缺口。
审计模型: Opus(4 子代理并行) 审计时间: 2026-06-08 仓库状态: 源码未改动(只读审计)