AGC Direct 回合:一条原生命令挂死后把写通道锁死到回合结束(direct-execution-draining) #529

Open
opened 2026-09-29 17:35:58 +08:00 by suzmii · 1 comment
Member

背景

AGC 的 Direct 回合有一套宿主执行账本(租约 + 预算 + 封口),任何"新副作用"必须先 admit 拿到租约才能写文件 / 跑命令 / 发起付费调用。当前实时观测到:一条原生命令挂死 → 租约永不结算 → 整轮所有写入被 100% 拒绝 → 交付根本无法开始。这不是模型不肯干活,也不是构建/环境问题。

现象(2026-09-29 真实回合,可复核)

项目 gameagent-cfc64e7f81114787819ae39c10cbc2c2,用户诉求是"做一个飞鸟(Flappy Bird 风格)竖版飞行躲避游戏"。

时刻(UTC+8) 事件
16:37:21 回合开始,clientTurnId=783175b6-007f-4017-9dc4-f49431e5176d,账本 createdAtMs=1790671041149
16:37:59→16:40:06 taonier_prepare_game_art 失败(2m07s):spritesheet 请求 4 片、只识别到 2 片 → 已由 #525 跟踪
16:40:46→16:42:08 改走 3 张独立图,agc_generate_image 全部成功并登记
16:42:19 起了一条原生命令:"…\WindowsApps\Microsoft.PowerShell_7.6.6.0_x64__8wekyb3d8bbwe\pwsh.exe" -Command "node -e \"…PNG 透明度探测…\"";tool-call call_01_Jy0Q2FXDFOD5YBcJYRcc3886,status=running 直到回合结束(5m40s 无终态、无任何输出)
16:42:33 / 16:42:36 agc_remove_background ×2 失败:remote-terminal-failed: 资源编辑生成失败(远端 task-2b69dd51677e890e53e04bd8052504a0 / task-cd6ce6758adbfdbe143cb24c6d9b1d98,请求到失败仅 3 秒;账本落 phase=remote-failed、terminalFailureCode=remote-generation-failed)
16:44:18→16:47:48 13 次 agc_write_file/agc_apply_patch + 1 次 agc_remove_background 全部被拒,同一句话:direct-execution-draining: 当前批次正在收束,请等待在途操作结束
16:47:55 模型放弃并结束回合(其推理原文:"the gate requires the batch to end, which means ending my turn")
16:47:59 连接终止:Codex app-server 已退出;exitStatus=unknown;stderrClass=nonempty;stderrBytes=1944;stderrSha256=758997fa88ed53ba2745c3e94276b0885c3bd916675bdddfe8a965e5c9f36548;账本落 phase=interrupted、active=[]

结果:game/game.js 仍是 351 B 占位("还没有生成游戏…"),npm run build、双端视觉、generic-v1 试玩三项验收全部无法执行;交付合同 4 项 passed:false。

证据路径

  • %APPDATA%\world.genarrative.ai-game-creator\projects\gameagent-cfc64e7f\.agent\conversations\tool-calls.jsonl(挂死命令原文、13 次拒绝原文、逐条 startedAt/updatedAt)
  • 同目录 .agent\runtime\errors\error-1790671518931262000-4.json(draining 归类,metadata.tool=agc_apply_patch)
  • .agent\resource-edits\operations\{2c08714d-4e6d-4acc-9337-43a09cb8f0eb,6d9ae085-8f39-4b99-8c07-2e43fde9fda4}.json(远端失败终态)
  • %APPDATA%\world.genarrative.ai-game-creator\direct-executions\1519caa29489b16ecaab01bbaf39adbbcc49d8e14aadb145978fb07724876cc5\f1eaefb90fb6a3fcd6972af4a45dccfe43e0d36e8a9560b05f61f9a84a1cc97d.json(账本终态)
  • %APPDATA%\world.genarrative.ai-game-creator\diagnostics\application.log(16:47:59 连接终止行)

根因

  1. Draining 一经置上不会自退:付费类副作用失败时 finish_lease 命中 (!passed && kind != Write) → phase = Draining(agent/direct_execution.rs:1153-1157)。agc_remove_background 属 EffectKind::Paid(agent/direct_tool_bridge.rs:2717-2718),两次失败即完成这一步。
  2. 原生命令租约永不结算:命令项只有到 item/completed(拿到 exitCode)才 finish_lease;这条命令走的是 codex 原生统一执行入口(常驻会话,二进制里可见 Identifier of the running unified exec session.、"Bytes to write to stdin…"),只要会话不结束就永远停在 running,active 一直非空。
  3. 闸门是"全局拦新副作用":admit_with_key 里 phase == Draining && !active.is_empty() → 直接返回 direct-execution-draining,写 / 补丁 / 付费 / 验证一律进不来(agent/direct_execution.rs:1055-1067)。只读工具不取租约,于是表现成"只读正常、写入全废"。
  4. 没有任何兜底:账本没有单租约墙钟;MAX_ACTIVE=64 只管容量(:14,1043);tick_locked 只比总预算(:925-938,执行 900s / 整轮 1800s)。唯一出路是等整轮 30 分钟 Exhausted,或模型自己放弃——本轮是后者。

已在本机验证的机制:Windows 上 pwsh -NoProfile -Command -(脚本来自 stdin)在 stdin 保持打开时无限挂住(实测 >12s 不返回);命令串里用的是 POSIX 风格 \" 转义去嵌 pwsh -Command "node -e \"…\"",在 PowerShell 语义下不成立,shell 一旦等 stdin 就再也不会结束。

为什么现在没解(可观测性缺口)

  • 拒绝文案只有"正在收束,请等待在途操作结束"——不告诉模型是谁在途、跑了多久、怎么解。模型只能反复重试(本轮重试 14 次,耗时 3.5 分钟),且它并不知道这条命令是自己起的、可以用 write_stdin 收尾。
  • AGC 工具表(27 个 agc_*)里没有任何"停掉在途命令/放弃本批次"的动作。
  • app-server 的 stderr 只留 class/bytes/sha256,1944 B 内容不落盘,事后无法定性它为什么退出。

拟改动(分三步,先落失败用例再改实现)

P0-a 租约墙钟兜底(宿主侧)

  • 给原生命令租约加最大存活时长(建议取会话自身的 timeout_ms,缺省 3–5 分钟);到期由宿主主动按"失败 + needsReconciliation"结算该租约并落证据,不再让它继续拦新副作用。
  • 判据:挂死租约超过上限后,phase 允许回到 Working,后续 EffectKind::Write 可受理;未成功结算的事实必须写进 evidence,不能静默当成功。

P0-b 拒绝文案带出阻塞方

  • direct-execution-draining 必须带上阻塞项的 kind / tool / 会话或 item id / 已运行时长,并给出可执行解法(向该会话写入收尾、或显式放弃本批次)。

P1 Draining 作用域收窄

  • 当前"任何在途租约 → 拦掉所有新副作用"把互不相关的付费失败与一条长命令耦合成全局锁;应至少让 Write 不被无关的长 Execute 租约拦住,或提供一个显式的"放弃批次"动作让模型自救。

P2 stderr 留证

  • app-server stderr 头部(如 4 KB)落到 .agent/runtime/errors/,供事后定性。

验收判据

  • 新增真机夹具用例(在 apps/ai-game-creator-shell/scripts/direct-execution-production-fixture.mjs 增加例如 native-hung,并加进 scripts/run-agc-direct-execution-fixture.mjs 默认用例表;现有 11 例 completed/passes/mcp/mcp-write/native/native-resources/patch/deadline/native-session/restart/running 必须继续全绿):
    1. 起一条永不结束的原生命令 + 一个失败付费动作 → 断言在租约上限内后续 agc_write_file 能被受理;
    2. 断言超时结算写入了 needsReconciliation 事实,且账本没有把该轮记成成功。
  • 单元回归:direct_execution/tests.rs 增"挂死租约不永久拦写",且既有 parallel_validation_shares_one_batch_and_failure_drains_before_repair、sealing_blocks_new_writes_and_requires_drain_and_process_proof 不得被放松。
  • 定向:npm run check:agc-direct-execution-fixture、npm run check:encoding、git diff --check。

非目标

  • 不放松封口/排空协议、不改预算数值、不引入绕过门禁的旁路;
  • 不改公开 API / SpacetimeDB schema;
  • 顺带修美术侧(#525 切片、抠图远端失败语义)不在本 issue 范围。

关联

  • #525 AGC 标准美术包硬编码四张切片并按顺序误判用途(本回合首次失败即此条)
  • #495 External API 抠图幂等重放误报 409 / #429 重写客户端工具并发限制
  • 相邻但不同症状:kdletters 2026-09-28 三条提交(drain() 把模型报的 failed 改写成 interrupted、app-server 关闭原因可观测、阶段回归断言)——修的是终态与文案,不是"长租约永久拦写"
## 背景 AGC 的 Direct 回合有一套宿主执行账本(租约 + 预算 + 封口),任何"新副作用"必须先 `admit` 拿到租约才能写文件 / 跑命令 / 发起付费调用。当前实时观测到:**一条原生命令挂死 → 租约永不结算 → 整轮所有写入被 100% 拒绝 → 交付根本无法开始**。这不是模型不肯干活,也不是构建/环境问题。 ## 现象(2026-09-29 真实回合,可复核) 项目 `gameagent-cfc64e7f81114787819ae39c10cbc2c2`,用户诉求是"做一个飞鸟(Flappy Bird 风格)竖版飞行躲避游戏"。 | 时刻(UTC+8) | 事件 | |---|---| | 16:37:21 | 回合开始,`clientTurnId=783175b6-007f-4017-9dc4-f49431e5176d`,账本 `createdAtMs=1790671041149` | | 16:37:59→16:40:06 | `taonier_prepare_game_art` 失败(2m07s):spritesheet 请求 4 片、只识别到 2 片 → 已由 #525 跟踪 | | 16:40:46→16:42:08 | 改走 3 张独立图,`agc_generate_image` 全部成功并登记 | | **16:42:19** | 起了一条原生命令:`"…\WindowsApps\Microsoft.PowerShell_7.6.6.0_x64__8wekyb3d8bbwe\pwsh.exe" -Command "node -e \"…PNG 透明度探测…\""`;tool-call `call_01_Jy0Q2FXDFOD5YBcJYRcc3886`,`status=running` **直到回合结束(5m40s 无终态、无任何输出)** | | 16:42:33 / 16:42:36 | `agc_remove_background` ×2 失败:`remote-terminal-failed: 资源编辑生成失败`(远端 `task-2b69dd51677e890e53e04bd8052504a0` / `task-cd6ce6758adbfdbe143cb24c6d9b1d98`,请求到失败仅 **3 秒**;账本落 `phase=remote-failed`、`terminalFailureCode=remote-generation-failed`) | | **16:44:18→16:47:48** | **13 次 `agc_write_file`/`agc_apply_patch` + 1 次 `agc_remove_background` 全部被拒**,同一句话:`direct-execution-draining: 当前批次正在收束,请等待在途操作结束` | | 16:47:55 | 模型放弃并结束回合(其推理原文:"the gate requires the batch to end, which means ending my turn") | | 16:47:59 | 连接终止:`Codex app-server 已退出;exitStatus=unknown;stderrClass=nonempty;stderrBytes=1944;stderrSha256=758997fa88ed53ba2745c3e94276b0885c3bd916675bdddfe8a965e5c9f36548`;账本落 `phase=interrupted`、`active=[]` | 结果:`game/game.js` 仍是 351 B 占位("还没有生成游戏…"),`npm run build`、双端视觉、`generic-v1` 试玩**三项验收全部无法执行**;交付合同 4 项 `passed:false`。 ### 证据路径 - `%APPDATA%\world.genarrative.ai-game-creator\projects\gameagent-cfc64e7f\.agent\conversations\tool-calls.jsonl`(挂死命令原文、13 次拒绝原文、逐条 startedAt/updatedAt) - 同目录 `.agent\runtime\errors\error-1790671518931262000-4.json`(draining 归类,`metadata.tool=agc_apply_patch`) - `.agent\resource-edits\operations\{2c08714d-4e6d-4acc-9337-43a09cb8f0eb,6d9ae085-8f39-4b99-8c07-2e43fde9fda4}.json`(远端失败终态) - `%APPDATA%\world.genarrative.ai-game-creator\direct-executions\1519caa29489b16ecaab01bbaf39adbbcc49d8e14aadb145978fb07724876cc5\f1eaefb90fb6a3fcd6972af4a45dccfe43e0d36e8a9560b05f61f9a84a1cc97d.json`(账本终态) - `%APPDATA%\world.genarrative.ai-game-creator\diagnostics\application.log`(16:47:59 连接终止行) ## 根因 1. **Draining 一经置上不会自退**:付费类副作用失败时 `finish_lease` 命中 `(!passed && kind != Write)` → `phase = Draining`(`agent/direct_execution.rs:1153-1157`)。`agc_remove_background` 属 `EffectKind::Paid`(`agent/direct_tool_bridge.rs:2717-2718`),两次失败即完成这一步。 2. **原生命令租约永不结算**:命令项只有到 `item/completed`(拿到 exitCode)才 `finish_lease`;这条命令走的是 codex 原生统一执行入口(常驻会话,二进制里可见 `Identifier of the running unified exec session.`、"Bytes to write to stdin…"),只要会话不结束就永远停在 `running`,`active` 一直非空。 3. **闸门是"全局拦新副作用"**:`admit_with_key` 里 `phase == Draining && !active.is_empty()` → 直接返回 `direct-execution-draining`,写 / 补丁 / 付费 / 验证一律进不来(`agent/direct_execution.rs:1055-1067`)。只读工具不取租约,于是表现成"只读正常、写入全废"。 4. **没有任何兜底**:账本没有单租约墙钟;`MAX_ACTIVE=64` 只管容量(`:14,1043`);`tick_locked` 只比总预算(`:925-938`,执行 900s / 整轮 1800s)。唯一出路是等整轮 30 分钟 `Exhausted`,或模型自己放弃——本轮是后者。 已在本机验证的机制:Windows 上 `pwsh -NoProfile -Command -`(脚本来自 stdin)在 stdin 保持打开时**无限挂住**(实测 >12s 不返回);命令串里用的是 POSIX 风格 `\"` 转义去嵌 `pwsh -Command "node -e \"…\""`,在 PowerShell 语义下不成立,shell 一旦等 stdin 就再也不会结束。 ## 为什么现在没解(可观测性缺口) - 拒绝文案只有"正在收束,请等待在途操作结束"——**不告诉模型是谁在途、跑了多久、怎么解**。模型只能反复重试(本轮重试 14 次,耗时 3.5 分钟),且它并不知道这条命令是自己起的、可以用 `write_stdin` 收尾。 - AGC 工具表(27 个 `agc_*`)里**没有任何"停掉在途命令/放弃本批次"的动作**。 - app-server 的 stderr 只留 `class/bytes/sha256`,1944 B 内容不落盘,事后无法定性它为什么退出。 ## 拟改动(分三步,先落失败用例再改实现) **P0-a 租约墙钟兜底(宿主侧)** - 给原生命令租约加最大存活时长(建议取会话自身的 `timeout_ms`,缺省 3–5 分钟);到期由宿主主动按"失败 + `needsReconciliation`"结算该租约并落证据,**不再让它继续拦新副作用**。 - 判据:挂死租约超过上限后,`phase` 允许回到 Working,后续 `EffectKind::Write` 可受理;未成功结算的事实必须写进 evidence,不能静默当成功。 **P0-b 拒绝文案带出阻塞方** - `direct-execution-draining` 必须带上阻塞项的 `kind / tool / 会话或 item id / 已运行时长`,并给出可执行解法(向该会话写入收尾、或显式放弃本批次)。 **P1 Draining 作用域收窄** - 当前"任何在途租约 → 拦掉所有新副作用"把互不相关的付费失败与一条长命令耦合成全局锁;应至少让 `Write` 不被无关的长 `Execute` 租约拦住,或提供一个显式的"放弃批次"动作让模型自救。 **P2 stderr 留证** - app-server stderr 头部(如 4 KB)落到 `.agent/runtime/errors/`,供事后定性。 ## 验收判据 - 新增真机夹具用例(在 `apps/ai-game-creator-shell/scripts/direct-execution-production-fixture.mjs` 增加例如 `native-hung`,并加进 `scripts/run-agc-direct-execution-fixture.mjs` 默认用例表;现有 11 例 `completed/passes/mcp/mcp-write/native/native-resources/patch/deadline/native-session/restart/running` 必须继续全绿): 1. 起一条永不结束的原生命令 + 一个失败付费动作 → 断言**在租约上限内**后续 `agc_write_file` 能被受理; 2. 断言超时结算写入了 `needsReconciliation` 事实,且账本没有把该轮记成成功。 - 单元回归:`direct_execution/tests.rs` 增"挂死租约不永久拦写",且既有 `parallel_validation_shares_one_batch_and_failure_drains_before_repair`、`sealing_blocks_new_writes_and_requires_drain_and_process_proof` 不得被放松。 - 定向:`npm run check:agc-direct-execution-fixture`、`npm run check:encoding`、`git diff --check`。 ## 非目标 - 不放松封口/排空协议、不改预算数值、不引入绕过门禁的旁路; - 不改公开 API / SpacetimeDB schema; - 顺带修美术侧(#525 切片、抠图远端失败语义)不在本 issue 范围。 ## 关联 - #525 AGC 标准美术包硬编码四张切片并按顺序误判用途(本回合首次失败即此条) - #495 External API 抠图幂等重放误报 409 / #429 重写客户端工具并发限制 - 相邻但不同症状:kdletters 2026-09-28 三条提交(`drain()` 把模型报的 failed 改写成 interrupted、app-server 关闭原因可观测、阶段回归断言)——修的是终态与文案,不是"长租约永久拦写"
suzmii added the Kind/Bug
Priority
High
2
labels 2026-09-29 17:35:58 +08:00
Member

I will solve this

I will solve this
lhk229 self-assigned this 2026-09-30 15:21:48 +08:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GenarrativeAI/Genarrative#529