给宿主收尾修复补阶段回归断言
Project CI / AI game creator shell Rust crates (push) Successful in 1m27s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m55s
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled

- host_ended_turn_is_not_a_failure 追加断言:宿主自己收尾后账本阶段仍是 Working,不得被改成 Interrupted(这正是 2026-09-28 CLI 回执变成"执行通道已断开"的根因)
- 变异验证:临时补回缺陷版本的 self.interrupt(&reason).await 后该断言以 left=Interrupted/right=Working 变红;还原后 codex_app_server::execution 13 passed
- decision-log 记录该守卫与变异结果
This commit is contained in:
kdletters
2026-09-28 23:57:59 +08:00
parent a59329995e
commit 8b305162c1
2 changed files with 10 additions and 1 deletions
@@ -1263,6 +1263,14 @@ mod tests {
assert!(adapter.host_stop_requested());
// 原因照样进报告:不算失败不等于不用记。
assert!(adapter.report().contains("模型本次执行结束"));
// 阶段不能被改成 Interrupted:宿主自己收尾时,账本必须保留它自己的结论。
// 这一条是 2026-09-28「CLI 每轮回执都变成『执行通道已断开』」缺陷的回归判据——
// 缺陷版本里 `fail_turn` 会无条件 `session.interrupt(reason)`,把阶段打成 Interrupted。
assert_eq!(
adapter.session.snapshot().unwrap().phase,
ExecutionPhase::Working,
"host-ended turn must keep its own phase"
);
}
/// 用户按下的「终止」不记失败事实:`cancel_from_host` 先同步置位 `host_stop_requested`、再异步
@@ -9723,7 +9723,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 背景:修掉 DirectProject 空历史注入后,真机夹具(真 AGC CLI → 真 app-server → loopback Responses)6/6 都走到 Provider,但仍统一失败在账本阶段:`phase=interrupted`、CLI 回执是「执行通道已断开,不能自动重放未确认操作」。插桩排除 codex 版本漂移、stdin、`Drop for ExecutionBinding`、app-server 崩溃之后,关闭原因日志给出结论:`reason=宿主执行预算或交付收尾`,来自 `shutdown_and_report()`(`execution.rs:938`)——宿主收尾先置 `closed` 再关 app-server,关进程会给在途回合发 `TransportClosed`;消费端(`mod.rs:4015`)只看 session 阶段、没看 `closed`,而 `fail_turn()` 内部无论如何都调 `session.interrupt(reason)`,于是把正常收尾改写成了失败。
- 决策:`fail_turn()` 在 `is_closed()`(宿主自己收尾:正常终态、预算与交付收尾)时**只把原因追加进终态说明、不改阶段**,新增 `ExecutionSession::append_terminal_note()`(与 `interrupt()` 同款留痕,但不设 `Interrupted`);用户主动终止(`host_stop_requested`)与真实通道故障仍按原口径记失败并中断。判据用标志而不是"事件到达先后",与既有 `fail_turn` 守卫保持一致。
- 验证:`node apps/ai-game-creator-shell/scripts/direct-execution-production-fixture.mjs --agc-exe <debug exe> --cases completed,passes,mcp,mcp-write,native,deadline` → **6/6 passed**;`completed` 用例账本 `phase=completed`、`executorStopped=true`,`terminalReport` 为宿主真实交付报告(不再是通道断开文案)。`cargo test … codex_app_server::execution` 13 passed(含 `host_ended_turn_is_not_a_failure`、`user_requested_stop_is_not_recorded_as_a_failure`)、`cargo test … direct_execution` 26 passed、`cargo fmt --check`、`cargo build` 通过。
- 验证:`node apps/ai-game-creator-shell/scripts/direct-execution-production-fixture.mjs --agc-exe <debug exe> --cases completed,passes,mcp,mcp-write,native,deadline` → **6/6 passed**(后续又加了 `restart` 与 `running`,现为 **8/8**);`completed` 用例账本 `phase=completed`、`executorStopped=true`,`terminalReport` 为宿主真实交付报告(不再是通道断开文案)。`cargo test … codex_app_server::execution` 13 passed(含 `host_ended_turn_is_not_a_failure`、`user_requested_stop_is_not_recorded_as_a_failure`)、`cargo test … direct_execution` 26 passed、`cargo fmt --check`、`cargo build` 通过。
- 回归守卫:`host_ended_turn_is_not_a_failure` 追加断言「宿主自己收尾后阶段仍是 `Working`(不得是 `Interrupted`)」。变异验证:在该分支临时补回 `self.interrupt(&reason).await`(即缺陷版本行为)后,这条以 `left: Interrupted, right: Working`、文案 `host-ended turn must keep its own phase` 变红;还原后 13 passed。
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/{direct_execution.rs,codex_app_server/execution.rs,codex_app_server/mod.rs}`、pitfalls、DirectProject 里程碑。GUI 常驻 app-server 不走这条每轮关闭路径,受影响的是 CLI / 单回合宿主(`--direct-codex-chat` 及依赖它的夹具)。
- 边界:这修的是"宿主收尾被记成失败",不改变两条 DirectProject 运行时验收仍需真实客户端窗口(UI 忙碌态与页面重进)。
- 决策(口径回归):macOS 现行契约是 arm64 单架构(2026-09-21 决策),里程碑里「两个 macOS 平台键指向 universal 产物」的旧文字按现行决策改写;不得据此重新切回 universal,除非按该决策给出的恢复路径补齐按架构的 Node 运行时。