补两条踩坑:app-server 会 1:1 回显注入内容(读侧上限不得小于写侧允许量),以及“掉了就补读”的补扫不是修复

- 新增「Codex app-server 会 1:1 回显注入内容:读侧上限不得小于写侧允许量」:记录隔离探针实测(捆绑 codex-cli 0.147.0)——注入 5 MiB 的 item,app-server 接受并回一条 5 243 245 字节的 rawResponseItem/completed 单行,而读侧上限当时 4 MiB、写侧无上限,于是合法的注入被读行判成连接级故障,报错方向指向 app-server;同时记下 stderrBytes=405 那类摘要不能当证据(正常启动就有 275/452/546/5996 B 的插件/别名警告)、experimentalRawEvents 需要 initialize.capabilities.experimentalApi、以及探针必须用管道 stdin 与按 CommandLine 精确清理
- 新增「「掉了就补读」的补扫不是修复:自激回路会一路耗到内存」:明确标注**由并行工作线报告、本线未独立复核**,并给出取证据前不得引用的警示与“去掉补扫症状是否回来”的判据
- 门禁:npm run check:encoding exit 0(4399 files)
This commit is contained in:
2026-09-11 22:51:24 +08:00
parent 28781a420c
commit 3a850ee6e7
+17 -1
View File
@@ -5318,4 +5318,20 @@
- **同类文案坑**:拒收后的恢复阶段 `unresolved` 有三条进入路径,其中两条**重读是成功的**(读到的一对比手上旧 / 读到的是撕裂的一对)。文案不能写成"重新读取磁盘清单失败"——那是误报;写"未能按磁盘清单重新对齐"才对所有路径都成立。
- **验证**`projectResourceLiveUpdateModel.test.ts` 用假后端(上传时推进 revision)断言 merge 判 `accepted``projectManifestMergeRejectionDecision === null`,并附一条对照钉子显式写出旧形状、断言它必判 `revision-conflict`。变异验证:把修法还原成旧形状 → 配对用例变红。
- **覆盖边界(重要,别误读)**:上述断言打**在生产函数 `uploadProjectAssetFilesAndReadSnapshot` 上**,它同时拥有上传与配对读,所以整条配对语义被真测;但 **AGC 侧没有"资源面板上传 → 拒收提示条"的端到端 UI 用例**(现有 harness 只有聊天入口的 `/asset.upload` 通路,资源面板的 file input 没有用例,补一条要渲染整个 `ProjectDevelopmentView`)。因此「用户界面上不再出现拒收提示」这一层**未被端到端覆盖**,它是"配对正确 ⇒ merge 判 accepted ⇒ 不产生拒收决策 ⇒ 不渲染提示条"的推理结论。**不要把它当成端到端断言引用。**
- **关联**`apps/ai-game-creator-shell/src/view/project-development/projectResourceLiveUpdateModel.ts``index.tsx``uploadResourcePanelFiles``src/features/app-shell/WorkspaceLauncher.tsx`、提交 `006e9cc2f`
- **关联**`apps/ai-game-creator-shell/src/view/project-development/projectResourceLiveUpdateModel.ts``index.tsx``uploadResourcePanelFiles``src/features/app-shell/WorkspaceLauncher.tsx`、提交 `006e9cc2f`
## 2026-09-11 Codex app-server 会 1:1 回显注入内容:读侧上限不得小于写侧允许量
- **现象**Direct Codex 回合失败,用户只看到 `codex-app-server-terminal-unknown: Codex app-server JSON-RPC 单行超过大小上限;exitStatus=unknownstderrClass=nonemptystderrBytes=405stderrSha256=e807b4b3…`,并且带着「(可直接重试)」。报错看起来像 app-server 出了问题(`exitStatus=unknown`、stderr 非空、连接被判死),实际是**我方读行把连接判死**。
- **原因(实测,不是推断)**`experimentalRawEvents: true` 的线程收到 `thread/inject_items` 后,app-server 会**逐条原样回显** `rawResponseItem/completed`。隔离探针(AGC 捆绑的 `codex-cli 0.147.0`,隔离 `CODEX_HOME`,全部只落在 `%TEMP%`)实测:`thread/start{experimentalRawEvents:true}` 后注入**一个 5 MiB 的 item** → app-server **接受**`{"id":4,"result":{}}`20 B)→ stdout 回一条 **5 243 245 字节**的单行;注入小 item 同样回显(377 Bmarker 可见)。而 `read_bounded_game_creator_codex_app_server_line` 的上限当时是 **4 MiB**`write_message` **没有任何字节上限**(图片单张还允许 5 MiB、单次 16 MiB):于是「我们注入得进去」却「我们读不回来」,一次**合法**注入被读行判成连接级故障。**触发条件是「单个 item ≥ ~4 MiB」,不是「历史总量大」——23KB 的策划案从来不是元凶。**
- **处理**:读写两侧**共用同一个上限常量**`GAME_CREATOR_CODEX_APP_SERVER_LINE_MAX_BYTES`,现 32 MiB;依据:单张图 base64 上限 10 MiB、单次图片总量 16 MiB 折 base64 ≈21.3 MiB + JSON 信封);写侧加对称守卫(超限即失败关闭并报字节数,**不截断**);注入前对**单条 item**(扣掉回显信封余量)与**整份载荷**各做前置校验,超限指名 `itemId`/`type`/字节数并失败关闭;注入超限按**不可重试**处理(同一份历史每次读结论相同,且本轮用户消息已先追加进同一文件、载荷只会更大),并给专属 `recoveryHint` 与 public summary。
- **验证**`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- codex_app_server_write_guard_matches_the_read_cap direct_project_history_injection_rejects_oversized_items_and_payloads``2 passed`2257 filtered out)。变异验证:写侧守卫改成 `>=`、单条上限改成 `usize::MAX` → 两条用例各在自己那一处变红;还原后逐字节哈希一致并复跑绿灯。门禁:`npm run check:rustfmt` / `npm run typecheck` / `npm run check:encoding` 均 exit 0。
- **推广**:① **凡是「我们发出去、对面会带回来」的通道,两侧的界必须同源**,否则报错方向会指向对面、排查会跑偏(本次就差点按"对面超限"修);② 这类故障的**摘要字段不能当证据**:探针里 app-server 正常启动就产生 275/452/546/5996 B 的 stderrPATH 别名警告、插件缓存 401、插件 git sync 失败),`stderrBytes=405` 正落在同一量级,按 sha256 追它不会有结果;③ 用实验能力注意初始化声明:**不带 `initialize.capabilities.experimentalApi = true``thread/start{experimentalRawEvents:true}` 会被拒**`{"error":{"code":-32600,"message":"thread/start.experimentalRawEvents requires experimentalApi capability"}}`),AGC 的 initialize 已带该能力;④ 探针要复现 app-server 行为,`stdin` 必须用**管道**`-RedirectStandardInput` 传文件时它读完 EOF 会**什么都不输出就退出**),且 app-server 启动会 `git clone` 插件市场,**继承句柄的 git 子进程**会锁住临时目录,清理要按 `CommandLine` 精确匹配自己的探针路径再终止。
- **关联**`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs``apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs``docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`、提交 `3db6afa05`
## 2026-09-11 「掉了就补读」的补扫不是修复:自激回路会一路耗到内存
- **现象(据并行工作线报告)**:把"补扫"(发现漏掉就再补一轮读取)当作修复加回去后,worker 直接 `ERR_WORKER_OUT_OF_MEMORY`,回路不收敛到内存耗尽。
- **原因/处理**:补扫让"漏掉"这个**症状**立刻消失,但触发条件仍在,于是每次补扫又制造出新的待补项,形成自激回路;根因(第一次为什么会漏)始终没被定位。正确做法是回到触发条件本身(为什么同一份输入会漏),而不是在读取侧再叠一层重试/补扫。判断这类"修复"是否只是掩盖,可以问一句:**把补扫去掉,症状会不会回来?会回来就说明根因没动。**
- **验证**:⚠️ **本条由并行工作线报告,本排障线(AGC 资源画布 / Direct Codex)未独立复核**——没有拿到该线的 file:line、命令或 CI 输出。**引用本条前先向该线取证据**,不要把它当已复核结论使用。
- **关联**:待并行线回填(提交/文件/命令)。