docs(gen): 便宜档死圈修复计划过双评审并校正(Codex+Opus 收敛修计划后执行)
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled

两审独立坐实根因链、收敛「修计划后执行」。纳入发现校正三处:
① fix② 前提错(实测 _template rng 本就合规 ctx?next():0、无 nowMs,违规是模型
   自造)→ 从「改模板」重定向到 check 报错点破规则+提示层克隆纪律+check-early(更上游);
② 阻塞判断纠正:实测真过门率 9/23≈39%(初版把「跑到门 83%」当过门),死圈只是多种
   失败模式之一,三修到不了 80%,80% 缺口(E/G/H 玩法正确性)属另一工作面;
③ fix① 边界钉死:edit 走 read→apply→cheap_run.write_file 继承 L1/L3+whitelist、
   不触发 check、加软停 blocked-tools;纠偏仅 schema 缺字段/不重置 stuck 计数/cheap-only
   别改坏共享 tier2 熔断;edit 自身错配纳入纠偏。fix③ 落 evidence 脱敏(源工程可交易)。
验收改 ok=True 收敛(非「跑到门」)+ 预置违规 fixture。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-07-08 01:08:46 -07:00
parent 47f0f404e1
commit d9660c8ef1
2 changed files with 37 additions and 28 deletions

View File

@ -114,4 +114,4 @@ SAA gameDefinition 路已随 reframe 废A-model 直写真 `src/`。Plan B
**附带的可追溯性缺口**:brief 原文在 worker/Service 任何日志与产物里都**没落盘**worker 只记 trace_id/gameId、Service 没记 input、agent 找的 `start.brief` 根本不存在)——生成的核心输入事后不可审计,该补落盘。
修法方向(不写代码,排期归便宜档生产 cutover 线):①给便宜档一条**可靠的增量 edit/apply-patch 工具** + 对「连撞同签名畸形参数」做定向纠偏(「你漏了 path,重发」)而非沉默累计到熔断——这是把「只差一个小修」的局从死圈救回的最高杠杆;②让 `_template` 的 rng/nowMs **默认合规**、check 红线报错指到行并点破「函数内回退也算裸调」;③补 brief 落盘
修法方向(不写代码,经 Codex+Opus 双评审校正,详见计划档):①给便宜档一条**可靠的增量 edit/apply-patch 工具**(read→精确替换→复用 `cheap_run.write_file` 的 L1/L3+whitelist 回写、不触发 check、加入软停 blocked-tools) + 对「连撞同签名畸形参数」做**窄口径**纠偏(仅 schema 缺字段类、不重置 stuck 计数、cheap-only 别改坏共享 tier2 熔断)——治死圈这一终止模式的最高杠杆;②**`_template` 的 rng 本就合规(`ctx?ctx.random.next():0`)、根本无 nowMs,违规是模型无视合规模板自造的**——不是"改模板",而是让 check 红线报错**指到行并点破「函数内回退也算裸调、正则按纯文本命中」**+ 提示层加「照模板逐字克隆、禁自造裸回退」与 check-early(写完即 check),比造工具更上游更省;③补 brief 落盘(落 `game_dir/evidence/` 脱敏限长、别落工程根——源工程可进素材市场交易)。**死圈只是失败模式之一**:实测 23 局真过门率仅 ~39%(跑到门 83% 但多为 E/G/H 玩法坏死挂门),三修消除死圈但到不了 MVP ≥80%,80% 缺口(玩法正确性)属质量线另一工作面

View File

@ -1,7 +1,7 @@
---
title: "fix: 便宜档生成死圈修复 — 增量修改路径 + 模板合规 + brief 落盘"
title: "fix: 便宜档生成死圈修复 — 增量修改路径 + 规则纪律 + brief 落盘"
date: 2026-07-08
status: 草案 · 待评审(执行前过 Codex+Opus 双评审 · 归便宜档生成质量线排期)
status: 已过 Codex+Opus 双评审(2026-07-08,两审独立收敛「修计划后执行」,发现已纳入本版)· 待执行 · 归便宜档生成质量线
topic: 便宜档生成死圈修复
上级: docs/plans/2026-06-30-便宜档M3-生产cutover-plan.md
关联:
@ -13,49 +13,58 @@ topic: 便宜档生成死圈修复
## 为什么做这件事
2026-07-08 对一次真实失败运行做了第一性根因分析(全程与证据见 [`cheap-model-game-generation.md §11`](../../.agents/skills/cheap-model-game-generation.md))。结论不是"模型造不出游戏",而是工具面缺一条增量修改路径,把"可恢复的小瑕疵"推成了"整局失败":agent 写出的产物带一个小红线违规(`rng()/nowMs()` 回退用了 `Math.random()/Date.now()`),`check` 判 FAIL,agent 正确地想去修,但便宜档六工具面只有整体覆盖的 `write_file`、没有增量 edit,于是"改三行"变成"重写 22KB",MiniMax 吐这么长的 tool-call 参数时反复漏掉 `path` 字段,连撞四次触发 `stuck` 熔断,回合被强杀,九门收口从未运行,`ok=False`。这条死圈一旦产物带个需小修的红线就高概率复现,是当前便宜档成功率的一个结构性拖累
2026-07-08 对一次真实失败运行做了第一性根因分析(全程与证据见 [`cheap-model-game-generation.md §11`](../../.agents/skills/cheap-model-game-generation.md)),结论经 Codex + Opus 双评审逐环坐实:一次便宜档生成(brief=躲避掉落街机)最终 `ok=False`,死于一条工具面死圈——agent 写出的 `game-logic.js` 带一个红线违规(`rng()/nowMs()` 回退裸用 `Math.random()/Date.now()`),`check` 判 FAIL,agent 正确地想去修,但便宜档六工具面只有整体覆盖的 `write_file`、没有增量 edit,于是"改三行"变成"重写 22KB",MiniMax 吐这么长的 tool-call 参数时反复漏掉 `path` 字段(先还幻觉调了个不存在的 `edit`),连撞四次触发 `stuck` 熔断强杀回合,九门收口从未运行,`ok=False`
本计划把根因收敛出的三处修复排进便宜档生成质量线,按杠杆高低排序,并给出各自的验证口径。它不改生成主线的分档架构,只补工具面与模板的两处结构缺陷加一条可追溯性缺口
双评审同时纠了本计划初版两处错,已在本版改掉:其一,**违规不来自模板**——当前 `_template/src/game-logic.js:66-68``rng()` 回退本就是 `ctx ? ctx.random.next() : 0`(合规),模板根本没有 `nowMs()` 函数;违规是**模型无视合规模板、按训练直觉自造防御性裸回退**,还用错误注释("check 不拦函数内部")合理化。所以真正上游根因是**规则理解缺口 + 不照模板克隆的纪律缺口**,比"缺增量 edit"更靠上游——agent 一开始就不写违规,整条死圈不会触发。其二,死圈只是**多种失败模式之一**,不是全部(见下阻塞判断)
## 是否阻塞当前计划
## 是否阻塞当前计划(纠正初版)
**不阻塞已交付的内测闭环 A 段(登录/额度/dify/facade)。** 那条线与生成质量正交,已合并 dev/2.0.0 并逐环真机验证。
**不阻塞已交付的内测闭环 A 段**(登录/额度/dify/facade,与生成质量正交,已合并 dev/2.0.0 并逐环真机验证)
**也不构成内测(beta)启动的硬阻塞,但影响生成成功率。** 判据三条:其一,这条死圈是少数失败模式而非多数——九发同期运行对照里只有本局没跑到九门,其余都进了收口门;其二,内测阶段验收口径是"能用就行",且数据飞轮本就设计来持续改进生成质量,不要求上线即完美;其三,内测硬闭环此前已真机产出过可发布游戏(studio 路 quered→发布,quality=60)。所以内测可以在带已知生成质量注记的前提下启动,这三处修复是把成功率往 MVP ≥80% 门抬的改进项,归便宜档生产 cutover 线(本计划上级)排期,fix① 优先。
**生成质量对内测是真实短板,但不是闭环管路的硬阻塞。** 初版说"死圈是少数失败模式、九发对照只有本局没跑到九门"——这条证据被双评审驳回:**"跑到收口门"≠"过门"**。实测 23 局便宜档生成,跑到九门 19 局(83%),但 `verdict.pass=True` 只有 **9 局(≈39%)**,其余多为与死圈无关的玩法坏死(`_wg1-gen/{80020,80021,80027}/verdict-feedback.json``E_live/G_input/H_progress`,输入不驱动状态)。所以真实过门率 ~39%,远低于 MVP ≥80% 门。据此纠正:
## 三处修复(按杠杆排序)
- 死圈(本计划治的)是**一种终止模式**(熔断在门前),约占失败的一小部分;fix① 消除它,但**修完到不了 80%**——80% 缺口的大头是玩法正确性(E/G/H 门),属便宜档质量线另一条工作面,不在本计划。
- 放行内测靠的是"内测容忍 + 数据飞轮迭代生成质量 + 已真机产出过可发布局",不靠"生成质量已达标";本计划不夸大三修把成功率抬向 80%,只声明它消除死圈这一结构性拖累。
- 建议:**内测放量前完成 fix①**(免得种子用户高频撞死圈),但内测启动不以本计划完成为门。
### fix① 增量修改路径 + 畸形参数定向纠偏(最高杠杆)
## 修复(按杠杆排序,含双评审钉死的执行级边界)
问题的要害是"改一处小函数在便宜档只能表达为重写整文件",而重写整文件恰好落在 MiniMax tool-call 参数最不稳的超长区间。两个动作:
### fix① 死圈治理:增量修改工具 + 窄口径畸形参数纠偏(最高杠杆,治死圈这一种终止模式)
- **给便宜档一条可靠的增量修改工具**(edit / apply-patch,只传一段 diff 或"旧串→新串"替换)。agent 本能就在调不存在的 `edit`(trace 里幻觉调用坐实),说明模型预期存在增量工具;补上它,让"改 rng 三行"表达为几行 diff,不再触发 22KB 整体重写的畸形高发区。
- **对"连撞同签名畸形参数"做定向纠偏而非沉默熔断**。现在 `stuck` 熔断对"参数畸形类"失败过于刚性:agent 每次都在犯同一个 MiniMax 参数病(漏 `path`),被沉默累计到第四次直接强杀。检测到连续同签名工具失败时,给 agent 一条结构化的强提示("你漏了 path 参数,请重发"),很可能一次就破圈。这需要在 tier2 `CircuitBreakerMiddleware` 的 stuck 判定前插一层"畸形参数可纠偏"分支,便宜档复用。
**fix①-A 增量 edit 工具**(`cheap-worker/cheap_toolkit.py` + `cheap_run.py`)。给便宜档加一个 `edit_file(path, old, new)`(或 apply-patch)工具,让"改 rng 三行"表达为短参数替换,不再触发 22KB 整体重写的 MiniMax 漏参高发区。执行级边界(双评审 I3 钉死):
边界与风险:增量工具的写保护(L1 plumbing 仍不可改)与 `check` 触发口径要与现 `write_file` 对齐,别开出一条绕过校验的旁路;纠偏提示要有次数上限,防止把"真死圈"拖成无界重试烧钱。
- 实现走 **read 现文件 → 内存里精确替换 old→new → 经 `cheap_run.write_file` 回写**同一条路,天然继承 `write_file` 的 L1 plumbing 写保护、L3 边界、`write_whitelist`(M4"只改玩法"边界不被打穿)。不新开任何绕过校验/写保护的旁路。
- **edit 不触发 check**(核实:`write_file` 本就不触发 check,唯一强制点是 `finish` 门复跑 check+build——`cheap_toolkit.py:96-116`;"edit 要对齐 check 触发"是不存在的对齐点)。
- **加入软停 blocked-tools 集**(`middleware.py:178``DEFAULT_SOFT_STOP_BLOCKED_TOOLS`),否则越软停线后 edit 成为新的绕过面。
- edit 有 write_file 没有的**自身失败面**:old 串不精确匹配→edit 失败,MiniMax 也可能吐坏 diff、成新 stuck 源。这条纳入 fix①-B 的纠偏覆盖(匹配失败给结构化提示"未找到待替换串,请附完整旧串或改用 write_file")。
### fix② 模板默认合规 + check 报错无歧义(中杠杆)
**fix①-B 窄口径畸形参数纠偏**(`tier2/gen-worker/worker/middleware.py``CircuitBreakerMiddleware`,cheap 与 tier2 **共享同一类**——改动必须不误伤 tier2)。执行级边界(双评审 I1/I2 钉死):
`rng()/nowMs()` 的"ctx 不可用回退 `Math.random()/Date.now()`"是模型反复犯的同一个错——本局 agent 甚至自信到把"check 不拦函数内部"写进注释,而这理解是错的。两个动作:
- **仅匹配 schema/参数校验类失败**——错误头含 `Input validation failed` / `is a required property`(漏必填字段)才注入一次结构化纠偏("你漏了 `path` 参数,请带完整参数重发")。**绝不对所有同签名失败放行**:反复 build 同一编译错、反复 check 同一红线、L1 拒写,这些是**该熔断的真死圈**,纠偏不得放行。
- **纠偏不重置 `_fail_repeat` 计数**:只在 `_fail_repeat ∈ [1, stuck_threshold)` 时注入提示,达 `stuck_threshold` 照常 `_trip(stuck)`。即"纠偏至多 threshold-1 次,仍同签名失败就熔断"——上界由现有 stuck 阈值(默认 4)天然封死,零新增无界重试面(否则 1→纠偏→1→纠偏…无界烧到 ¥ 硬地板 ¥15 才停,把最省的死圈变最贵)。
- **cheap-only 开关或子类**:便宜档启用纠偏、tier2 默认关(tier2 走 Anthropic 原生、基本不犯漏 path,纠偏对它是死代码但共享类风险共担);别裸改共享类默认行为。改完 **tier2 的 stuck 用例必须回归**(`tier2/gen-worker/tests/test_circuit_stuck_signature.py`)。
- 在 `_template``rng()/nowMs()` 起点就钉死合规写法(回退也走注入的确定性源,或直接不给裸 `Math.random`/`Date.now` 的写法示范),让合规成为 agent 克隆的默认。
- 让 `check` 的红线报错**指到行号**并点破"函数内回退也算裸调、正则按纯文本命中"——消除 agent 对规则的误解,减少它写出违规再去修、进而落入 fix① 死圈的概率。
### fix② 上游纪律:check 报错点破规则 + 提示层克隆纪律 + check-early(比造工具更上游更省)
这一处只影响质量与死圈触发频率、不影响 `ok` 判据本身,杠杆低于 fix①,但它减少 fix① 的触发面,两者叠加收益更大。
初版"改模板 rng 默认合规"对 rng 是空操作(模板已合规)、对 nowMs 是无中生有——删掉。真正杠杆在让 agent 一开始就不写违规、写了立刻自己发现:
### fix③ brief 落盘补可追溯性(低杠杆但必要)
- **check 红线报错点破规则**(`game-runtime/tools/amodel-gen/tools.mjs`):现红线只输出正则 source、无行号,且 `stripCode()` 把块注释整体替换成单空格会致行号漂移(`tools.mjs:136`)。改为**逐行 strip 后逐行匹配**保留行号,报 `红线 game-logic.js 第 N 行: 禁用 Math.random()`,文案加一句"函数内回退也算裸调、正则按去注释后纯文本命中"——直接消解 agent"封装进函数就不拦"的错误心智模型(这句比行号更治本)。
- **提示层克隆纪律 + check-early**(`cheap-worker/cheap_roles.py`):加一条"rng/nowMs 照 `_template` 逐字克隆,禁自造 `Math.random()/Date.now()` 防御回退";再加一条 check-early 纪律"每写完一个 `game-logic.js` 就立即 `check`,红线修到 PASS 再扩玩法"——文件小时发现违规→小修→写参数短,天然避开 MiniMax 长 write 漏参高发区,与 fix① 叠加、且更省(fix① 只救终局那一下,救不回前面的长空转成本)。
- **模板补合规 nowMs 示范**(`game-runtime/games/_template/src/game-logic.js`,minor):当前模板缺时间源范例(用 dt 累加/timerScheduler),若确需 nowMs 就补一个走 `ctx.time.nowMs()`、裸回退给确定性值的示范,补上范例缺口——不是"改默认写法"。
生成的核心输入 brief 原文在 worker 与 Service 的任何日志、产物里都没落盘(worker 只记 trace_id/gameId,Service 没记 input,agent 找的 `start.brief` 根本不存在)。这让"生成为什么跑偏、有没有完整拿到 brief"事后无法审计——本次根因分析里"brief 是否完整传入 agent"就是唯一的观测盲区。把 brief 原文落进 job 目录(如 `game_dir/brief.txt` 或 evidence 摘要),成本极小、对每一次失败复盘都有用。
### fix③ brief 落盘补可追溯(落 evidence、脱敏限长)
## 验证
生成核心输入 brief 原文在 worker/Service 任何日志与产物里都没落盘,失败复盘无法审计"是否完整拿到 brief"。补落盘,但双评审 M1 钉死落点:**落 `game_dir/evidence/brief.json`,不落 `game_dir` 根**——因资产面决策源工程可进素材市场交易,brief 是用户私有输入,落工程根会随发行外泄;evidence 是取证目录、不随发行源打包。字段 `brief / bytes / sha256 / source=job.brief / truncated`,文本按前端 `PROMPT_MAX=1000` 限长、必要时只留 hash + 截断预览,避免无界膨胀。
按便宜档生成线口径,用 **n=5 收敛环**验证,不做 n≥30 统计批:
## 验证(双评审校正:收敛判据落在 ok=True,不用"跑到门")
- fix① 后,构造/复现"产物带一个需小修红线"的局,验证 agent 能经增量工具或纠偏提示把红线修掉、跑到九门收口,而不是熔断——n=5 里死圈复发率显著下降即收敛。
- fix② 后,验证新生成的产物默认不带 `Math.random/Date.now` 裸调、check 首过率上升。
- fix③ 后,验证每次运行 `game_dir` 落有 brief 原文,失败复盘可读到输入。
- 三处都跑真环境(cheap Service :8300 真 agent loop),不用桩;收口把成功率变化记进便宜档质量线的达标账,判是否够 MVP ≥80% 门另走统计样本。
按便宜档生成线口径,**n=5 收敛环做修复回归、不做 n≥30 统计批**;拆两层:
- **fix① 回归(可复现构造)**:预置一个带 `Math.random()` 违规的 `game-logic.js` fixture 让 agent 只修红线,验"经 edit/纠偏修掉红线 → 跑到九门 → **ok=True 过门**"而非熔断;收敛判据 = 5/5 不因缺 path stuck、且过门(**ok=True,不是"跑到门"**)。tier2 stuck 用例回归绿。
- **fix② 回归**:新生成产物默认不带 `Math.random/Date.now` 裸调、check 首过率上升、check 报错带行号且不误报(函数内/注释/字符串不误报、多行块注释后行号不漂,配单测)。
- **fix③ 回归**:每次运行 `game_dir/evidence/brief.json` 落有原文,失败复盘可读到输入。
- 全跑真环境(cheap Service :8300 真 agent loop),不用桩。**统计达标率(是否够 MVP ≥80%)另开分层样本,不在本计划**——本计划只证死圈这一终止模式被消除。
## 归属与排期
归便宜档生产 cutover 线(本计划上级 [`2026-06-30-便宜档M3-生产cutover-plan.md`](2026-06-30-便宜档M3-生产cutover-plan.md))。执行前按 AGENTS.md §6.8 过 Codex+Opus 双评审。fix① 优先,fix②/③ 随后。不在内测启动的关键路径上,但建议内测放量前完成 fix①,以免种子用户高频撞死圈
归便宜档生产 cutover 线(本计划上级 [`2026-06-30-便宜档M3-生产cutover-plan.md`](2026-06-30-便宜档M3-生产cutover-plan.md))。已过双评审,进入执行。fix① 优先(治死圈)、fix② 随后(降触发面,更上游)、fix③ 并入。不在内测启动关键路径上,但建议内测放量前完成 fix①。80% 达标缺口(玩法正确性 E/G/H)属质量线另一工作面,本计划不覆盖