From 41c03d7199851e7b757fe0ff0c5e7e982ae3bf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 10:30:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E6=A0=B8=E5=AE=9E?= =?UTF-8?q?=E5=88=A0=E8=B0=83=E7=94=A8=E8=BA=AB=E4=BB=BD=E5=AE=88=E5=8D=AB?= =?UTF-8?q?=E7=9A=84=E5=89=8D=E7=BD=AE=E5=88=A4=E6=8D=AE=EF=BC=8C=E6=94=B6?= =?UTF-8?q?=E6=95=9B=E7=AC=AC=205=20=E6=AD=A5=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADR §9 记录核实结论:取消兜底本来就同时释放占用、补写 turn.completed 并踢队列,删守卫不需要新增无条件终态;连接死亡的失败事实由 app-server 连接层落地 - 实施计划第 5 步改写:60 秒启动窗口闸门改挂 Thread Manager 的占用登记年龄,读者改读 direct_active_turn_id_at,TurnAlreadyRunning 一并删 - decision-log 追加 2026-09-30 条目,记录核实过程、决策与影响范围 --- ...命令入队化与待发消息队列归宿主-2026-09-24.md | 12 ++++++--- .../shared-memory/decision-log.md | 21 +++++++++++++++ ...ject命令入队化与待发消息队列归宿主-2026-09-24.md | 27 ++++++++++++------- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/docs/adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md b/docs/adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md index 67afbd699..90d8b2735 100644 --- a/docs/adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md +++ b/docs/adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md @@ -85,9 +85,15 @@ Thread Manager 在一个回合收口**之后**原子地做:取队首 → 登 - CLI 一退,`DirectTurnError::TurnAlreadyRunning` 的两个生产点(调用身份守卫、占用登记)都没有调用方, 它连同前端"同一轮消息仍在处理中"文案、专属分支与测试一起删。 - `DirectTaonierActiveInvocationGuard` 的**身份**与 Thread Manager 的 `active_turn.turn_id` 是同一件事的两份记录 - (GUI 路径下同源字符串),而 09-23 ADR 立的是"同一件事只许有一处真相"。CLI 退役后它的硬阻塞消失:第二步让那五个读者 - 改读 Thread Manager 的活动回合身份,守卫连同它的测试与 60 秒卡死兜底一起删。删之前必须确认取消路径的无条件终态 - 仍然解得开"回合卡死"(那次兜底来自真实事故 `d833ca9d3`)。 + (GUI 路径下同源字符串),而 09-23 ADR 立的是"同一件事只许有一处真相"。CLI 退役后它的硬阻塞消失:五个读者 + (`direct_execution` / `direct_tool_bridge` / `direct_validation` / `direct_project_context` / 付费美术重生成) + 改读 Thread Manager 的活动回合身份,守卫连同它的测试一起删,不再有第二份进程内记录。 +- 删守卫的前置判据**已核实(2026-09-30)**,不需要新增"取消路径补无条件终态":`d833ca9d3` 的兜底路径本来就 + 同时做三件事——释放占用、往事件流补一条 `turn.completed{aborted}`(否则前端会永远停在运行中)、调 + `complete_direct_thread_turn` 解除占用。删掉的只有 `DirectTaonierActiveInvocationGuard` 那张进程内表; + 唯一独有的东西是 60 秒启动窗口闸门,改挂在 Thread Manager 的占用登记年龄上 + (`direct_stale_turn_for_release` 的 `NeverReachedExecutor` 分支)。连接死亡那条失败事实由 app-server + 连接层自己落地(`ab970b9fd`),与守卫无关。 ## 备选方案与取舍 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index b8089f910..66dc906c3 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -9614,3 +9614,24 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 失去"真实二进制驱动执行层生产验证"这条手工路径。 - 验证方式(设计稿,尚未实施):`docs/adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md`、 `docs/technical/【实施计划】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md`。 + +## 2026-09-30 守卫清理前置判据核实:取消兜底不需要新增无条件终态,60 秒闸门改挂占用登记年龄 + +- 背景:按 09-24 ADR §9 的计划,删 `DirectTaonierActiveInvocationGuard` 前必须确认"回合任务 park / 泄漏时, + 取消仍能清空占用并让队列继续放行"。查 git 历史(`d833ca9d3` / `ab970b9fd`)与当前实现后确认: + `cancel_direct_codex_turn_at` 的兜底路径本来就同时做三件事——释放占用、往事件流补一条 + `turn.completed{aborted}`(否则前端会永远停在运行中)、调 `complete_direct_thread_turn` 解除占用并踢队列。 + 所以删守卫**不需要**新增"取消路径补无条件终态",那是当时就有的东西,不是守卫带来的。 +- 决策:守卫只在两件事上是独有的——(1) 它是"这一轮是谁"的第二份进程内记录(与 Thread Manager 的 + `active_turn.turn_id` 同源),CLI 退役后已无第二个入口,按"同一件事只许有一处真相"删; + (2) 它的 60 秒启动窗口闸门(防"刚放行、还在本地准备的回合被终止误伤")保留,改挂在 Thread Manager + 的占用登记年龄上:`direct_stale_turn_for_release(root, expected, reason)` 只在 + `NeverReachedExecutor` 时校验年龄(`DIRECT_STALE_TURN_RELEASE_MIN_AGE_MS`),返回身份;释放本身仍由 + 既有的 `complete_direct_thread_turn` + `kick_direct_queue_dispatch` 完成(天然幂等)。 +- 决策:连接死亡的失败事实由 app-server 连接层自己落地(`ab970b9fd`),与守卫无关,删除守卫不动那条链路。 +- 影响范围:`agent/direct_runtime/mod.rs`(删守卫表 / 只读探测,改为 `direct_active_turn_id_at`)、 + `agent/direct_thread_manager.rs`(`DirectTurnIdentity` + `read_direct_turn_identity`)、 + `agent/codex_app_server/mod.rs`(兜底目标改 `DirectStaleTurnReleaseReason`)、五个身份读者与相关测试、 + 删 `DirectTurnError::TurnAlreadyRunning` 与前端对应分支 / 生成绑定。 +- 验证:`cargo test --bin genarrative-ai-game-creator-shell -- agent::direct`;`npx vitest run tests/appSurface.test.ts`; + `npm --workspace apps/ai-game-creator-shell run typecheck`;`npm run check:encoding`;`git diff --check`。 diff --git a/docs/technical/【实施计划】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md b/docs/technical/【实施计划】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md index bfae9ff8c..8a7dc67f6 100644 --- a/docs/technical/【实施计划】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md +++ b/docs/technical/【实施计划】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md @@ -2,7 +2,7 @@ 更新时间:`2026-09-24` -状态:**实施中**(第 0–3 步已落地;第 4、5 步待做) +状态:**实施中**(第 0–4 步已落地;第 5 步进行中) 设计口径见 [`【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24`](../adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md)。 本文件只排实施顺序、不变式与验收,不重复设计理由。 @@ -16,7 +16,7 @@ | 第 2 步 队列归 Thread Manager | 已落地 | `StoredEvent.pending` 产物字段 + `enqueue_pending_turn` / `remove_pending_turn` / `claim_pending_turn`;放行在 `agent/direct_turn_dispatch.rs`(`kick_direct_queue_dispatch` + `DirectTurnReservation`) | | 第 3 步 前端收口 | 已落地 | 见下面「第 3 步的落地细则」 | | 第 4 步 CLI 与夹具退役 | 已落地 | 删 `CliCommand::DirectCodexChat`(变体 / `project_path_mut` / 解析 / 派发)、`run_direct_game_creator_turn_at` 一对包装函数与夹具脚本;顺带删掉只剩测试在用的 `direct_turn_error_boundary_text`(判据只剩 `direct_turn_enqueue_failure` 一处),三条边界测试改打 `direct_turn_enqueue_failure(...).message`。**`TurnAlreadyRunning` 挪到第 5 步**(它最后一个生产点在调用身份守卫里) | -| 第 5 步 守卫清理 | 待做 | | +| 第 5 步 守卫清理 | 进行中 | 前置判据已核实;改读 Thread Manager 活动回合身份、删调用身份守卫与其测试,并删 `TurnAlreadyRunning` | ## 第 0 步:词表切换(与代码同批,不单独提交) @@ -155,14 +155,21 @@ kick 幂等(并发两次只认领一次);队首在放行后被移除、rem ## 第 5 步:守卫清理(CLI 退役之后) -- 五个身份读者(`agent/direct_execution.rs`、`agent/direct_tool_bridge.rs`、`agent/direct_runtime/mod.rs` 的付费美术重生成、 - `agent/direct_validation.rs`、`agent/direct_project_context.rs`)改读 Thread Manager 的活动回合身份 - (新增一个按项目路径取 `active_turn.turn_id` 的只读入口)。 -- 删 `DirectTaonierActiveInvocationGuard` 与 `release_stale_direct_taonier_active_invocation` 及其测试 - (`direct_runtime/mod.rs` 的守卫用例、`direct_project_context.rs`、`direct_tool_bridge.rs`、`direct_tools_mcp.rs`、 - `user_input.rs`、`codex_app_server/mod.rs` 的集成用例),取消路径改为直接走无条件的 `complete_direct_thread_turn("aborted")`。 -- 前置判据:补一条测试证明"回合任务被 park / 泄漏时,取消仍能清空占用并让队列继续放行"——那次 60 秒兜底来自真实事故 - (`d833ca9d3`),删它必须有等价保证。 +> 前置判据**已核实(2026-09-30)**:`d833ca9d3` 的取消兜底本来就同时做三件事——释放占用、往事件流补一条 +> `turn.completed{aborted}`(否则前端永远停在运行中)、解除 `complete_direct_thread_turn` 占用;所以删守卫 +> **不需要**新增任何"取消路径补无条件终态"。连接死亡那条失败事实由 app-server 连接层自己落地(`ab970b9fd`), +> 也与守卫无关。守卫唯一独有的东西是 60 秒启动窗口闸门,改挂在 Thread Manager 的占用登记年龄上。 + +- 五个身份读者(`agent/direct_execution.rs`、`agent/direct_tool_bridge.rs`、`agent/direct_validation.rs`、 + `agent/direct_project_context.rs`、`agent/direct_runtime/mod.rs` 的付费美术重生成)改读 + `direct_active_turn_id_at`(Thread Manager 的活动回合,只读、不改占用)。 +- 删 `DirectTaonierActiveInvocationGuard`、`DirectActiveTurnView`、`read_direct_taonier_active_invocation_at` 与 + `release_stale_direct_taonier_active_invocation` 及它们的测试;`codex_app_server` 的兜底目标改 + `DirectStaleTurnReleaseReason`,释放本身仍由既有的 `complete_direct_thread_turn` + `kick_direct_queue_dispatch` 完成。 +- 删 `DirectTurnError::TurnAlreadyRunning`(最后一个生产点在守卫里)与前端"同一轮消息仍在处理中"文案、专属分支、 + 生成绑定与 `tests/appSurface/project-conversation.suite.ts` 的对应断言。 +- 前置判据测试:`cancel_direct_codex_turn_at` 在"回合任务泄漏、app-server 侧没有可中断句柄"时仍能解除占用, + 并把队首放行出去(`codex_app_server` 的兜底用例 + `direct_stale_turn_for_release` 的单测)。 ## 验收与证据