---
title: tier2 富游戏 n=5 收敛环 go/no-go 细化执行 plan
status: 已执行(2026-06-28/29 真跑 · U1–U4 交付 + U5 收敛环)· go/no-go = conditional(agent 层 win-balance 自调可靠性,见「执行发现」)· §6.8 双评审已过 · 剩余见「后续工单(2026-07-02 落账)」
date: 2026-06-28
topic: tier2-富游戏生成线
canonical: false # tier2 canonical 在 plan①(统领)与 06-22-003(复杂档子计划);本档是「切片二·收敛环」的可跑 runbook 细化,不另立 SoT
上级: docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md
承接:
- docs/plans/2026-06-25-生成引擎统一执行计划-AgentScope三档-plan.md # plan① · 切片二统领(§切片二 121-140)
- docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md # 复杂档子计划 · U1-U7 主体已落
- tier2/HANDOFF.md # 权威基准 · spike feie-005 实证
- docs/architecture/架构/生成引擎/agentic运行时架构图说.md # 运行时 SoT · §4.2/§5.1/§5.3/§5.4
---
# tier2 富游戏 n=5 收敛环 go/no-go 细化执行 plan
> **执行者须知**:本档是 plan① 切片二「收敛环跑作一份细化执行」的兑现,不重排 06-22-003 已跑通的 runbook,只把"开跑前要补齐什么、怎么跑、怎么判"写到可照做的颗粒度。代码改动单元(U2/U3/U4)按 TDD 走:先写失败测试、看它失败、再最小实现。真跑(U1 smoke、U5 收敛环)只在 mini-desktop(chrome+esbuild;6c6g 禁 chrome、exit 144)。
## Summary(结论先行)
复杂档(Phaser·tier2)的核心生成线、九门+富游戏三门+四收敛契约门、批跑底座(`batch_run`/`aggregate`/`RunRecord`/`fallback_tree`)、成本观测、服务化已全部落在 `dev/2.0.0`;0 号 spike `feie-005` 已 accept(MiniMax-M3 自治写出过全门的面包店富游戏,n=1 机制验证)。**剩下的唯一悬念是:中等模型 M3 能否跨多款稳定收敛——这要用 n=5 收敛环来定 go/no-go。**
本 plan 把开跑收敛环拆成五个单元:三个前置准备(批级护栏 / 退路树 n=5 适配 / 偏脆门结构化)、一个前置阻断(harness 在当前工作树可跑的复现 smoke + DockerWorkspace 托管小验)、一个核心(跑环 + 判 go/no-go + 退路树分流)。**执行口径(创始人 2026-06-28 定):串行跑(底座现状,不改成并发)、只跑 MiniMax-M3(不引 deepseek 便宜档、不引 GLM-5.2——后者是切片一 cheap-worker 的线)。** 验收走 n=5 收敛环(并发语义按"一批 5 款/轮"理解),绝不滑向 n≥30 统计批跑。
go/no-go 判据离散:某轮 5 款里失败 ≤1 即收敛 → **go**;失败 >1 就读日志、补机器门或收窄 agent 可写面(小修,不调参)、再追加一轮;最多追加 3 轮(累计 n≤20)仍不收敛,按失败模式转退路树——表现层崩→更模板化(R1)、某系统装不出→补骨架(R2)、收敛需微调→留观(KEEP)。R3(便宜档全线垮而强档能过→换档)因网关现无强档(Opus/Fable/GLM 调用返 503)当前不可测,M3 全线垮只能记 conditional no-go、待强档上线复测。
## Problem Frame(问题框定)
```mermaid
flowchart LR
spike["spike feie-005
M3 写面包店富游戏
9门+三门过·finished·n=1"] -->|"机制已证
但只跑通一款"| Q{"M3 能否
跨多款
稳定收敛?"}
Q -->|"n=5 收敛环"| go["go
(收敛即止)"]
Q -->|"不收敛"| R["退路树
按失败模式分流"]
subgraph 底座["批跑底座(已落 · dev/2.0.0)"]
br["batch_run.py
串行 model×variant×n"]
rr["RunRecord(jsonl)
含 fail_system 分流键"]
ag["aggregate.py
过门率/¥/收敛中位/fail_system 分布"]
ft["fallback_tree.py
五出口自动判定器"]
br --> rr --> ag --> ft
end
Q -.读取.-> 底座
```
spike 证明的是"M3 一旦被机器门挡在正确契约上,有能力自治写出过全门的多系统富游戏"——但它是 n=1,且收敛强依赖那四道为收敛而补的机器门。**收敛环要回答的不是"能不能写出一款",而是"换了题材、重跑几遍,这套机器门 + M3 这条路是否稳定收敛"。** 这是全线决断价值最高、但代价不低(每款 ~610s、~¥1.3)的一步,所以判据要离散、上限要钉死、退路要预设,不能滑成无限调参或统计批跑。
## 起点与边界
**已落(基准 = `tier2/HANDOFF.md` + 底座代码侦察 2026-06-28):**
- 核心生成线 U1-U7:种子 fork + 有界外层 resume(`agent_loop/studio.py`,上限 6 次)+ Phaser `validate`/`run`/`prompt`/`roles` 重写。
- Phaser CDP 探针 + 九门(A_boot/B_uncaught/C_frame/D_render/E_live/F_wiring/G_input/H_progress/I_control)+ 富游戏三门(三联动/经济/latch)。
- 四道收敛契约机器门:`finish 门`、`LOCKED_PLATFORM_FILES`、`validate_datatable`、`validate_play_scene`(前三已结构化,第四仍偏脆——见 U4)。
- 批跑底座:`tier2/gen-worker/batch_run.py`(CLI+import 双形态)、`aggregate.py`、`worker/run_record.py`(`@dataclass RunRecord` → jsonl)、`worker/fallback_tree.py`(`decide(stats)` 五出口自动判定器 + 6 自测)。
- 成本观测:`RecordingChatModel(Anthropic)` + `observability/newapi_pricing.py`(活读 new-api 倍率)+ `Tier2TraceMiddleware`→TraceAdapter(真跑 647 事件 dropped=0、cost_rmb=1.29)。
- 服务化 @8200(`service/app.py`)——**收敛环不用它**,走本地 runner(`run_engine._run` in-process,决策②「本地 runner·不上 Agent Service」)。
- 模型矩阵(`tier2/config/generation.yaml` + `worker/config.py`):中等档默认 `MiniMax-M3`(Anthropic 原生 `/v1/messages`、thinking 分离);便宜档 `deepseek-v4-flash/pro`(new-api OpenAI 兼容);base_url=`http://100.64.0.8:3000`。**网关现仅 MiniMax + deepseek-v4-pro,Opus/Fable 返 503。**
**边界(不在本 plan):**
- Phase B 重机器(工作室 Agent Team / 第二装载分支 / 配置外置 / 控制面 / ReMe)——gate 在 B 门(收敛环 go)之后。
- 便宜档 ≥80% 达标门——那是切片一 cheap-worker 的统计批跑口径,与 tier2 的 n=5 收敛环是两套口径,别混。
- feed→play 第二装载分支 + tier2 源项目契约七要素落库取回——go/no-go 之后交后端。
- deepseek 便宜档对照、GLM-5.2——本环 M3-only,不引(创始人 2026-06-28 定)。
## 执行决策(创始人 2026-06-28)
| 决策点 | 现状/冲突 | 裁定 | 影响 |
|---|---|---|---|
| **执行模式** | 底座 `run_matrix` 是串行(三层 for 顺序 await,作者注明"真跑烧 token+占 chrome/端口"故意不并发);plan① 写"并发跑 5" | **串行跑**(底座现状,零并发改动) | "并发 5"按"一批 5 款/轮"语义落;避免 mini-desktop 多个 Phaser gen 同时占 chrome/端口/内存导致 harness 判定失真 |
| **收敛环模型** | 底座 tier2 矩阵 = M3(中等)+ deepseek 便宜档;memory 的 M3/GLM-5.2 是切片一另一条线;强基线 Opus/Fable 网关缺失 | **M3-only n=5** | 最直接回答"中等模型能否稳定收敛";R3 换档因无强档对照当前不可测,M3 全线垮记 conditional no-go |
## Key Technical Decisions
- **KTD1|收敛判据 = 末轮失败 ≤1 且已跑 ≥2 轮**。一轮 5 款里失败(decision≠accept 或 未 finished 或任一门未过)≤1 = 该轮收敛;但**单独的首轮 ≤1 不直接判 go**——n=5 下"≤1"=过门率 ≥80% 观测,真实 p≈0.6 的模型首轮裸蒙 ≥4/5 概率约 1/3,故要么是"修复后的轮 ≤1"(修复本身即收敛证据)、要么"首轮 ≤1 再跑一确认轮(同 5 变体不改)仍 ≤1",才判收敛。收敛后:末轮 0 错=`go`、1 错=`KEEP_留观`(都是 go-family);末轮 >1 进入修复-追加循环。这把 plan① "有错(超过 1)就…再追加一次 5,收敛即 go" 钉成可机器判、且压住首轮运气的离散阈值。
- **KTD2|轮间修复只许"小修",不许调参,更不许结构级改动**。允许:补一道机器门(把新出现的契约违规变成响亮可修反馈)、收窄 agent 可写面。禁止:改 prompt 平衡参数(那是调参)、改骨架/换模板/换模型(那是退路,触发即停环转退路树)。
- **定性边界闸(防"补门+收窄"累积漂移成无限调参换皮)**:小修 = **不减少 agent 必须自治写的核心系统数量、不把任一系统从"agent 写"降级为"平台预置"**。一旦某轮修复触及系统核心逻辑(而非表现层胶水/数据表 schema 校验),或单轮需补 ≥2 道新门,即判**结构信号 → 停环转 R1/R2**。"可写面只能微收、不能搬走系统"——否则得到的 GO 反映的是"把契约面削到 M3 能过",不是"M3 在原契约面上能收敛"。这道纪律是收敛环不滑向无限调参的闸。
- **KTD3|上限 fail-closed,达限即停批并保全已跑台账;跨轮上限也编译成机器门**。三层上限都 fail-closed、非人工纪律(§6.10 规则必须编译成机器门):① 单 gen 级底座已有(`rmb_hard_limit=3.0`/`wall_timeout_s=1800`/四熔断);② 单轮(批级)总闸(批 ¥/批墙钟/token,U2);③ **跨轮闸**——开跑前读台账 distinct round-prefix 数 / 总记录数,`>20`(即已达 n≤20)即 fail-closed 拒跑、提示"应已停环转退路树",`decide_n5` 在 `len(rounds)>4` 时也拒裁报"超上限"(U2/U3)。达限是硬停、非优雅降级,已落盘 RunRecord 不丢(断点续跑可读)。
- **KTD4|退路树判定 M3-only 适配,但不重建底座判定器**。复用 `fallback_tree.decide()` 的 fail_system 集中度分流(R1/R2);在其上加一层 n=5 收敛 GO gate(KTD1)与 R3 conditional 分支(无强档时不误判 KEEP)。
- **KTD5|harness 复现 smoke 是真·可开跑闸**。feie-005 在 `feat/tier2-phaser-engine` 分支跑、当前 `dev/2.0.0` 工作树无台账(无 `spike-runs.jsonl`)。开收敛环前必须在 mini-desktop 用 dev/2.0.0 代码 M3 跑通一款、复现 accept,证明这棵树的 harness+生成链确实可跑九门可判——否则收敛环的失败可能来自环境而非模型,go/no-go 不可信。
## High-Level Design(单元与依赖)
```mermaid
flowchart TD
U1["U1 前置阻断
dev/2.0.0 复现 smoke + DockerWorkspace 托管小验"]
U2["U2 批级护栏 fail-closed
批¥/批墙钟/token 总闸"]
U3["U3 退路树 n=5 适配
收敛 GO gate + R3 conditional + n=6→n=5"]
U4["U4 validate_play_scene 结构化
正则→AST(护环判定可信)"]
U5["U5 跑 n=5 收敛环
串行·M3-only·go/no-go·退路树分流·证据落盘"]
U1 --> U5
U2 --> U5
U3 --> U5
U4 --> U5
```
U1 是前置阻断(其 smoke 段 blocking);U2/U3/U4 是可并行的前置准备(各自独立 TDD、独立可评审);U5 是核心,依赖 U1-U4 全部就绪。U2/U3/U4 都是本机(6c6g 或 Mac)纯逻辑改动、可单测,不需 chrome;U1/U5 必须 mini-desktop。
---
## U1 · 前置阻断:dev/2.0.0 复现 smoke + DockerWorkspace 托管小验
**目标**:坐实"当前工作树的 harness+生成链在 mini-desktop 真能跑九门可判"(复现 smoke,blocking),并就"收敛环用哪个沙箱跑 harness"出结论(DockerWorkspace 托管小验,timeboxed、in-process 兜底)。
**文件/产物**:
- 跑:`tier2/gen-worker/batch_run.py`(已存在,不改)
- 落盘:`tier2/gen-worker/results/smoke-dev2.jsonl`(复现 smoke 台账)
- 小验脚本:`tier2/gen-worker/spikes/dockerworkspace_harness_probe.py`(新建,一次性探针,非生产路)
- 结论落档:本 plan「执行发现」节 + `tier2/HANDOFF.md` 追加一行
**步骤**:
- [ ] **S0(硬前置闸 · 不满足即阻塞 U1/U5、不开跑)** mini-desktop 可达 + 环境就绪:`ssh root@100.64.0.7 'echo ok'` 通;`/root/.venvs/agentscope-tier2/bin/python -c "import agentscope; print(agentscope.__version__)"` == `2.0.2`;chrome 可用(非 exit 144);new-api `http://100.64.0.8:3000` quota 够(M3 路活);**`node_modules/esbuild` 可解析**(`build-phaser.mjs` 依赖;**fresh git worktree gitignored 不带 node_modules、须 symlink 既有树或 npm install——2026-06-28 实证漏此即 build 失败、M3 全程 thrash**,phaser 走 shim external 不需)。任一不满足 → 见 Risks 行,不进 S1。
- [ ] **S1(复现 smoke · blocking)** 在 mini-desktop 用 dev/2.0.0 代码跑一款 M3 面包店:
```bash
ssh root@100.64.0.7 # 真 IP,绕系统代理 fake-ip(系统代理是 198.18.x fake-ip)
ls ~ && git -C rev-parse --abbrev-ref HEAD # 先确认 dev/2.0.0 工作树路径与分支
cd /tier2/gen-worker
/root/.venvs/agentscope-tier2/bin/python batch_run.py --out results/smoke-dev2.jsonl \
--models MiniMax-M3 --variants 面包 --n 1 --template business-sim
```
(venv 路径 = `/root/.venvs/agentscope-tier2/bin/python`,已知;`` 在 mini-desktop 上以 S1 第二行确认的真实路径替换。)
预期:落一条 RunRecord,`decision=accept`、九门 9/9、富游戏三门 3/3、`finished=true`、无熔断。墙钟应在 spike 量级(~610s±)。**这只证 harness 不误杀金标(不假阴)。**
- [ ] **S1b(反向 smoke · 不假阳)** 在 dev/2.0.0 用一个**已知坏产物**跑 harness(spike 第 4 bug 的 `class extends Phaser.Scene` 形态、或故意删 `tick` 的空转产物),断言产出 `decision!=accept`。**金标过(S1)+ 坏游戏拒(S1b)两条都绿,才坐实当前工作树 harness 双向可判、KTD1 失败计数可信**——否则 harness 若假阳(把空转判 accept),失败计数系统性偏低 → 直接制造假 GO。
- [ ] **S2** 若 S1 未 accept 或 S1b 未拒:这是环境/分支差异信号(非模型问题)。读 `results/smoke-dev2.jsonl` + game-log,定位 dev/2.0.0 与 `feat/tier2-phaser-engine` 的 tier2 代码差异(对账 HANDOFF:73 列五个 commit b43d59c/f8ec30c/0fd0d4f/f64dcc6/8f2bbce 是否都已并入)。修平再回 S1/S1b。**S1+S1b 不双绿不得进 U5。**
- [ ] **S3(DockerWorkspace 托管小验 · timeboxed 2h)** 写一次性探针 `spikes/dockerworkspace_harness_probe.py`:用 agentscope 2.0.2 的 `Workspace`(DockerWorkspace 形态)托管一次 CDP 九门 harness(把现有 in-process harness 的 `page.evaluate`/CDP 调用搬进 Workspace 沙箱跑一个金标 fixture),验它能否暴露足够 CDP 让九门可判。
- [ ] **S4** 据 S3 结论定收敛环沙箱:
- DockerWorkspace 能托管九门 harness(CDP 够用)→ 收敛环用它(利跨机扩容、隔离干净);
- 不够/timebox 内跑不起来 → **回落 in-process(spike-proven,S1 已证)**,收敛环就用本地 runner 直跑;跨机沙箱选型(§二补⑥(c))降为 go/no-go 之后的 Phase B 项,不阻塞本环。
- [ ] **S5** 把 S1 的复现结果 + S4 的沙箱裁定写进本 plan「执行发现」节与 `tier2/HANDOFF.md`。
> **设计说明(非元叙述,是边界声明)**:plan① 把 DockerWorkspace 小验列为"前置于收敛环"。但 spike 已证 in-process harness 可跑九门,故本环的真·阻塞项是 S1 复现 smoke,而非 DockerWorkspace。S3/S4 honor plan① 的小验要求、同时用 in-process 兜底解掉它的阻塞性:沙箱选型可有结论,收敛环不被它卡住。
---
## U2 · 批级护栏 fail-closed
**目标**:补齐 plan① 强制的"开跑前上限"——单 gen 级底座已有,缺的是批级总闸。达限硬停、保全已跑台账。
**文件**:
- 改:`tier2/gen-worker/batch_run.py`(`BatchConfig` 加批级上限字段;`run_matrix` 跑前/每格后检查)
- 测:`tier2/gen-worker/tests/test_batch_guardrails.py`(新建)
**接口(Produces)**:① `BatchConfig` 新增 `batch_rmb_cap/batch_wall_cap_s/batch_tokens_cap: float|int|None`;`run_matrix` 本次调用累计超任一上限抛 `BatchBudgetExceeded`、先 flush 已跑 RunRecord;② **跨轮闸 `assert_under_round_cap(out_path, *, max_total=20)`**:开跑前读台账总记录数,`>=max_total` 抛 `RoundCapExceeded`、拒跑;③ `main()` catch 这两个异常,打印"已落 N 条、断点可续"并**非 0 退出**(M-a)。
> **三层作用域**:单 gen 级(底座已有)→ 单轮=单次调用(批级闸 ①)→ 跨轮(②③ 机器闸,**非人工数 prefix**,§6.10)。单轮闸管一次调用的 ¥/墙钟/token;跨轮闸管累计 n≤20、`decide_n5` 另在 `len(rounds)>4` 拒裁(U3)——两道把"不滑向无限追加"编译成门。
**默认值(单轮口径 · directional v1,U5 跑前据 S1 实测单 gen 墙钟微调)**:
- `batch_rmb_cap = 20.0`(单轮 5 款 × 单 gen ¥3 硬限 = ¥15 上界,留余量;spike 实测 ~¥1.3/款、典型轮 ~¥6.5)
- `batch_wall_cap_s = 7200`(2h;串行 5 款 × ~610s ≈ 51min,留 ~2× 余量,且低于 5×单 gen 1800s 超时的绝对最坏 2.5h)
- `batch_tokens_cap = 600_000`(单轮 5 款 × ~50K out + 缓存余量;防失控)
**步骤(TDD)**:
- [ ] **S1(RED)** 写 `test_batch_rmb_cap_fails_closed`:mock 单格 run-one seam 返回 run-result 形状 `{"cost": {"cost_rmb": 30}, "tokens": {...}, ...}`(成本经 `result_to_record` 取 `cost.cost_rmb`→`RunRecord.cost_yuan`,batch_run.py:243),`batch_rmb_cap=80` 跑 3 格;断言第 3 格累计 90>80 抛 `BatchBudgetExceeded`,且前两格 RunRecord 已落盘(读 out_path 得 2 条)。
- [ ] **S2** 跑测试看失败(`BatchBudgetExceeded`/字段未定义)。
- [ ] **S3(GREEN)** `BatchConfig` 加三字段(默认 None=不限,保持旧行为);`run_matrix` 每格 `append_record` 后累加 `cost_yuan`/`wall_seconds`/`tokens_out`,超任一非 None 上限即抛 `BatchBudgetExceeded`(已落盘的不回滚)。加 `assert_under_round_cap()` 跨轮闸 + `main()` catch 两异常非 0 退出。
- [ ] **S4** 跑测试看过;补 `test_batch_wall_cap`、`test_batch_tokens_cap`、`test_caps_none_means_unlimited`(旧行为不破)、`test_round_cap_blocks_21st`(台账已 20 条 → `assert_under_round_cap` 抛 `RoundCapExceeded`)、`test_main_catches_and_exits_nonzero`(M-a)。
- **M-d(意图确认,非 bug)**:token 闸只计 `tokens_out`(in/cached 由 ¥ 闸覆盖、影响小);`batch_wall_cap=7200` < 5×单 gen 1800s 绝对最坏 2.5h,意味"慢轮即停"——这是**有意**(一轮 5 款全逼近单 gen 超时即异常,该停),非误配;真实墙钟据 S1 微调。
- [ ] **S5** 跑全套 `tier2/gen-worker/tests/` 确认无回归。
- [ ] **S6(commit)** `feat(tier2): 收敛环批级+跨轮护栏 fail-closed(批¥/墙钟/token/n≤20 闸)(切片二 U2)`
---
## U3 · 退路树 n=5 适配
**目标**:`fallback_tree.py` 的阈值是为 n≥30 设的统计触发线、且硬编码不读 YAML、默认 n=6。把判定适配成 n=5 M3-only 口径:加收敛 GO gate(KTD1)、R3 conditional(无强档不误判 KEEP)、扩自测;复用底座的 fail_system 分流(R1/R2)不重建。
**文件**:
- 改:`tier2/gen-worker/worker/fallback_tree.py`(加 `EXIT_R3_CONDITIONAL` 常量 + `decide_n5(rounds, *, has_strong_baseline)` + 一个 `__main__`/CLI 入口 `decide_n5_from_jsonl(path)`:读全轮台账、按 `--prefix` 分轮、调 `decide_n5`、打印裁决——供 U5 S6 取 go/no-go,不依赖 `aggregate.py` 的旧 `decide()`)
- 改:`tier2/gen-worker/batch_run.py` 注释澄清(**不动 `BatchConfig.n` 默认值**——它语义 = 每 (model,variant) 格重复次数,与"收敛环一轮 5 款 = 5 变体 × `--n 1`"是两个 n;改默认值会让"默认 5 变体 × `--n 5`=25 格"的陷阱出现。只加注释:"收敛环的一轮 5 款靠 `--n 1` + 5 变体,与 `BatchConfig.n` 无关")
- 测:`tier2/gen-worker/tests/test_fallback_tree_n5.py`(新建)
- 顺带(M-f · 既有失真,非本 plan 引入):`generation.yaml` 的 `gates` 区注释称阈值"消费方 fallback_tree.py、import 时读",但代码是模块级硬编码常量、不读 YAML。改 fallback_tree.py 时顺手对齐该 YAML 注释或留一行 TODO,别让那份不被读的配置看着像生效(防阈值双写漂移)。
**接口(Produces)**:`decide_n5(rounds: list[list[RunRecord]], *, has_strong_baseline: bool) -> {exit: str, reasons: list[str]}`(返回形状对齐底座 `decide()` 的 `{exit, reasons[]}`,fallback_tree.py:172,**复数 reasons**)。出口**沿用 fallback_tree.py:79-87 既有常量值**(`EXIT_GO="go"` / `EXIT_R1_TEMPLATE="R1_退模板化"` / `EXIT_R2_SCAFFOLD="R2_补骨架"` / `EXIT_KEEP="KEEP_留观"`)**+ 新增 `EXIT_R3_CONDITIONAL="R3_conditional"`**(底座原 `EXIT_R3_CEILING="R3_天花板"` 是"有强档对照下的真换档",与无强档的 conditional 语义不同,故另立)。
> **关键(`decide_n5` 是对 `decide()` 的覆写、非包装)**:`decide()` 是**便宜档对照导向**——fallback_tree.py:188-200 入口 Q1 先找 `STRONG_CHEAP_KEYS`(deepseek-v4-pro),M3-only 的 `by_model` 无此键 → `return EXIT_KEEP`(数据不足),根本走不到 Q2/Q3 的集中度分流(R1/R2);且其 `EXIT_R3_CEILING` 触发是"便宜档全崩**且强基线过**",与本环"无强基线"的 `R3_conditional` 互斥。故 `decide_n5` **覆写 Q1 入口(M3 不走便宜档 GO 门、改走 KTD1 失败计数)与 R3 语义**,R1/R2 分流**直接复用本模块纯 helper `_system_concentrated()`/`_bucket_share()` + 常量 `CONCENTRATION_RATIO`/`PRESENTATION_BUCKET`/`SYSTEM_BUCKETS`**(同模块直接调、不调 `decide()`、不 import aggregate 防环)。
**判定逻辑(M3-only · `has_strong_baseline=False`)**:
1. 数每轮失败 `fails[i]`(失败 = `decision!='accept'` ∨ `not finished` ∨ `not pass_gate`)。
2. **收敛 GO 需"末轮 ≤1 且已跑 ≥2 轮"**(防首轮裸运气——真实过门率 p≈0.6 的模型,二项分布下首轮 ≥4/5 概率 ≈0.337,即 ~1/3 会裸蒙假 GO;见 M1/KTD1):
- `len(rounds)>=2 and fails[-1]==0` → **`go`**;
- `len(rounds)>=2 and fails[-1]==1` → **`KEEP_留观`**(收敛·go·留观该款微调,对应 plan① "收敛需微调→留观");
- `len(rounds)<2`(只跑了 Round1 即 ≤1)→ **`INCONCLUSIVE_需确认轮`**(非终判;runbook 应再跑一确认轮,见 U5 S3)。
3. 末轮 `fails[-1]>1`(全程不收敛)→ 遍历全部失败款的 `fail_system` 分布,**集中度优先于换档**:
- `_bucket_share(presentation) ≥ CONCENTRATION_RATIO`(0.50)→ **`R1_退模板化`**;
- `_system_concentrated()`(resource/merge/order 任一 ≥0.50)→ **`R2_补骨架`**;
- 无任何集中(失败分散/全线垮)且 `has_strong_baseline=False` → **`R3_conditional`**(reasons:"M3 全线不收敛、网关无强档对照,待强档上线复测")。
- (对照:`has_strong_baseline=True` + 无集中 + 强档过 → 底座 `R3_天花板` 真换档;M3-only 不走此支。)
> **R1/R2(有集中)先于 R3 判**:有系统/表现层集中会先命中、不被 R3 吞。**表现层全崩时 R1(更模板化)优先于 R3(换档)**——spike 已证 M3 有表现层能力,故先收窄模板、而非断言模型不行就换档。
> **GO 与 KEEP_留观 都是"tier2 go"**(KEEP = go·留观该款微调);R1/R2/R3_conditional 是"未收敛"的退路。
**步骤(TDD)**:
- [ ] **S1(RED)** 写 `test_round1_le1_alone_inconclusive`:`rounds=[[4 accept+finished、1 fix]]`(单轮、失败=1)→ 断言 `exit=='INCONCLUSIVE_需确认轮'`(防裸首轮);再写 `test_two_rounds_clean_returns_go`(2 轮均 0 错 → `'go'`)、`test_repair_then_clean_returns_go`(Round1 失败 2、Round2 失败 0 → `'go'`)。
- [ ] **S2** 跑测试看失败(`decide_n5` 未定义)。
- [ ] **S3(GREEN)** 实现 `decide_n5`(放 `fallback_tree.py`,**不 import aggregate**[防环]、**不调 `decide()`**[它无 deepseek 强便宜档即 fallback_tree.py:195 `return EXIT_KEEP`、吞掉 M3-only]):① 数每轮失败 `fails[i]`(`decision!='accept' or not finished or not pass_gate`);② **GO/KEEP 需 `len(rounds)>=2 and fails[-1]<=1`**(末轮 0→`go`、1→`KEEP_留观`);`len(rounds)<2 and fails[-1]<=1`→`INCONCLUSIVE_需确认轮`;③ `fails[-1]>1`(不收敛)→ 复用本模块 `_bucket_share()`/`_system_concentrated()` + `CONCENTRATION_RATIO`:presentation≥0.50→`R1_退模板化`、系统桶≥0.50→`R2_补骨架`、无集中且 `has_strong_baseline=False`→`R3_conditional`。
- [ ] **S4** 跑测试看过;补:
- `test_presentation_concentrated_routes_R1_退模板化`(全轮失败 >1、presentation 占比 0.6)
- `test_resource_concentrated_routes_R2_补骨架`(order 桶 0.6)
- `test_diffuse_fail_no_strong_baseline_routes_R3_conditional`
- `test_strong_baseline_passes_routes_R3_天花板`(`has_strong_baseline=True` 的对照,确认无集中+强档过 → 真换档而非 conditional)
- `test_default_n_is_5_in_docstrings`(口径对账,防回退 n≥30)
- `test_decide_n5_from_jsonl_cli`(喂一份多轮台账 jsonl,断言 CLI 入口出正确出口)
- [ ] **S5** 跑底座自带 6 自测 + 新测,确认 `decide()` 旧出口不破。
- [ ] **S6(commit)** `feat(tier2): 退路树 n=5 M3-only 适配(收敛 GO gate + R3 conditional)(切片二 U3)`
---
## U4 · validate_play_scene 结构化
**目标**:四道契约门里唯一仍偏脆的一道(`validate_play_scene`,run.py:401-460 用正则/子串扫源码文本)做成结构化校验,防它假过/假失败污染收敛环 verdict。
**文件**:
- 改:`tier2/gen-worker/worker/run.py`(`validate_play_scene`)
- 新建:`tier2/gen-worker/tools/validate-play-scene-ast.mjs`(node 侧 AST 校验,复用已有 esbuild/node 环境)
- 测:`tier2/gen-worker/tests/test_validate_play_scene.py`(新建,喂对抗样本)
**接口(保持兼容)**:**公开签名 `validate_play_scene(game_id)` 不变**(`build()` 在 run.py:196 以 `game_id` 调它、内部按 game_id 解析文件路径,run.py:401-420)。抽出纯函数 **`validate_play_scene_src(src: str) -> {ok: bool, violations: list}`** 承载结构化校验:`validate_play_scene(game_id)` 读完文件后调它,测试直接喂源码给它。`validate_play_scene_src` 内部 shell out `node tools/validate-play-scene-ast.mjs`(stdin 喂源码、stdout 返 `{ok, violations}`);AST 校验 `createPlayScene` 是工厂函数(收 deps、return 配置对象)、真实调用 `bindInput(`/`tick(`(AST CallExpression 节点、非文本出现)、暴露 `handleNormalizedInput`。
**步骤(TDD)**:
- [ ] **S1(RED)** 写 `test_comment_only_bindInput_rejected`:喂 `validate_play_scene_src(src)` 一段 `bindInput` 只在注释(`// call bindInput(core) later`)、无真实调用的源码 → 当前正则会假过;断言 `ok==False`、violations 含 "bindInput 未真实调用"。再写 `test_string_literal_handleNormalizedInput_rejected`(标识符只在字符串里)。
- [ ] **S2** 跑测试看失败(现实现假过 → 测试红)。
- [ ] **S3(GREEN)** 写 `validate-play-scene-ast.mjs`(用 node 内置或 esbuild 的 parser 出 AST、遍历查工厂结构 + CallExpression);新增纯函数 `validate_play_scene_src(src)` 调它;把 `validate_play_scene(game_id)` 改为"读文件 → `validate_play_scene_src(读到的源码)`",公开签名不变。
- [ ] **S4** 跑测试看过;补 `test_valid_play_scene_passes`(金标 play-scene 源码仍过)、`test_missing_tick_rejected`、`test_not_factory_rejected`(写成 `class extends Phaser.Scene` → 拒,正是 spike 第 4 个 bug 形态)。
- [ ] **S5** 跑金标 fixture 装配链冒烟,确认门一道没放松、fixture 仍 ACCEPT(防过度收紧误杀合法)。
- [ ] **S6(commit)** `feat(tier2): validate_play_scene 正则→AST 结构化(切片二 U4)`
> **务实回落**:若 S3 的 node AST 基建在 mini-desktop 出现集成摩擦(parser 依赖/超时),降级为"多信号硬化"(要求 `bindInput(`/`tick(` 以 `identifier(` 形态出现且不在注释/字符串区间——剥注释与字符串后再匹配),并在 U5 跑中对 M3 的 5 款 play-scene 人工抽检一次假判;完整 AST 作 go/no-go 后的 robustness follow-up。无论哪条,U4 不得放松金标 fixture 的 ACCEPT。
---
## U5 · 跑 n=5 收敛环 + go/no-go + 退路树分流
**目标**:在 mini-desktop 串行跑 M3-only n=5 收敛环,据 KTD1 判 go/no-go,不收敛按 U3 退路树分流,全程证据落盘。
**前置**:U1 S1 复现 smoke 绿 + U2/U3/U4 已 commit。
**runbook**:
- [ ] **S1** mini-desktop 跑前自检:venv 的 agentscope==2.0.2、chrome 可用(非 exit 144)、new-api quota 够(M3 路活)、U2 批级护栏已生效(`BatchConfig` 三上限非 None)、results/ 目录可写。
- [ ] **S2(Round 1)** 跑一批 5 款(5 题材 × n=1;题材键须与 `BRIEF_VARIANTS` 字面一致——是 `糖水店`/`面包`,不是 `糖水`/`面包店`,否则 batch_run.py:423 直接 `raise ValueError`):
```bash
cd /tier2/gen-worker
/root/.venvs/agentscope-tier2/bin/python batch_run.py --out results/tier2-converge.jsonl \
--models MiniMax-M3 --variants 美食 水果 咖啡 面包 糖水店 --n 1 \
--prefix cv-r1 --template business-sim
# aggregate 只取描述性三图(过门率/¥/收敛中位);它内置的 decide() 对 M3-only 会 Q1 早退"无法判定",
# 不作本环裁决——裁决在 S6 走 decide_n5。
/root/.venvs/agentscope-tier2/bin/python aggregate.py --in results/tier2-converge.jsonl
```
- [ ] **S3** 数本轮失败(`decision!='accept' or not finished or 任一门未过`)。**收敛需"末轮≤1 且≥2轮"(KTD1,压首轮运气)**:
- **本轮失败 >1 → 进 S4(修复)。**
- **仅 Round1 就 ≤1(冷首轮、未经修复)→ 不直接判 go,跑一确认轮(同 5 变体、不改任何代码,`--prefix cv-r2`)再回 S3。**
- **修复后的轮 ≤1,或确认轮也 ≤1(此时已 ≥2 轮)→ 收敛 → 跳 S6 判 go(末轮 0 错)/ KEEP_留观(末轮 1 错)。**
- [ ] **S4(轮间修复 · KTD2)** 读失败款的 game-log + verdict,分类失败模式:
- 新出现的契约违规(M3 又自创某 schema/碰某锁)→ 补一道机器门 或 微收 agent 可写面(小修,守 KTD2 定性边界闸:不搬走系统、单轮 <2 道新门),commit。
- 已知系统反复装不出 / 表现层反复崩 / 触及系统核心逻辑 / 需骨架级或换模板级改动 / 单轮要补 ≥2 道门 → **结构信号,不在环内修**:停环,跳 S6 走退路树。
- [ ] **S5(追加轮)** 改 `--prefix cv-r{N}` 重跑 S2(同 5 题材,带 S4 的修复),回 S3。**累计 ≤4 轮、n≤20;跨轮闸(U2 `assert_under_round_cap`)每轮开跑前自动拦 ≥20、`decide_n5` 在 `len(rounds)>4` 拒裁(机器门、非人工自觉)。超上限 → 停环转 S6 退路树(不再追加、不再调参)。**
- [ ] **S6(判定 + 落盘)** 取裁决走 `decide_n5` 的 CLI 入口(**不用 aggregate 的旧 `decide()`**;判据由 `decide_n5` 自数失败、含 `finished`,不复用 aggregate 只看 `pass_gate` 的过门率——见 m-1):
```bash
/root/.venvs/agentscope-tier2/bin/python -m worker.fallback_tree \
--n5 --in results/tier2-converge.jsonl --no-strong-baseline
```
出口处置:
- `go` / `KEEP_留观` → **tier2 富游戏线 go**(`KEEP_留观` = go·留观该款微调);
- `R1_退模板化` / `R2_补骨架` / `R3_conditional` → 未收敛退路,按出口记结论与下一步。
落盘证据:`results/tier2-converge.jsonl`(全轮台账)+ aggregate 三图 JSON/txt(描述性)+ `decide_n5` 裁决 + go/no-go 一句话结论,写进本 plan「执行发现」与 `tier2/HANDOFF.md`。
**验收口径**:九门 + 富游戏三门(三联动/经济/latch)+ 四收敛契约门;n=5 收敛环 go/no-go;**绝不 n≥30 统计批跑**。
## 退路树(go/no-go 的离散出口)
```mermaid
flowchart TD
R1R["跑一轮 5 款"] --> E{"本轮失败数?"}
E -->|"仅 Round1 ≤1
(冷首轮)"| CONF["跑确认轮
(同变体·不改码)"] --> R1R
E -->|">1"| FIX["S4 修复(小修·守 KTD2 闸)
或结构信号→停环"] --> R1R
E -->|"末轮 0 · ≥2 轮"| GO["✅ go
tier2 富游戏线走"]
E -->|"末轮 1 · ≥2 轮"| KEEP["✅ KEEP_留观
go · 留观该款微调"]
E -->|"累计 20 仍 >1"| C{"失败模式集中度
(fail_system)"}
C -->|"presentation ≥0.50"| RR1["R1_退模板化
表现层收窄/加骨架壳"]
C -->|"resource/merge/order ≥0.50"| RR2["R2_补骨架
该系统补平台侧脚手架"]
C -->|"无集中 + 无强档对照"| RR3["R3_conditional
no-go · 待强档上线复测换档"]
```
退路按失败模式转、不再调参(KTD2)。R3 是 conditional——网关补上强档(GLM-5.2/Opus)后,跑一次便宜/中等档 vs 强档对照才能真判"是否模型天花板、是否该换档"。
## Risks(风险与缓解)
| 风险 | 影响 | 缓解 |
|---|---|---|
| **mini-desktop 不可达 / venv 缺 / chrome exit 144 / `node_modules` esbuild 缺**(U1·U5 真跑全压它;**fresh worktree 必缺 esbuild → build 失败 → M3 全程 thrash**,2026-06-28 实证) | U1/U5 全阻塞 或 假性 M3-不收敛 | U1 **S0 硬前置闸**(可达+agentscope 2.0.2+chrome+quota+**esbuild 可解析**),不满足即停;诊断"不收敛"先排 build/env(read 产物 + run_gates 取真九门),别据低 CPU 误判卡死 |
| dev/2.0.0 tier2 代码与 feie-005 分支有差异,S1 smoke 不复现;或 harness 假阳把坏游戏判 accept | 收敛环失败归因不清(环境 vs 模型);失败计数偏低制造假 GO | U1 **S1(金标过·不假阴)+ S1b(坏游戏拒·不假阳)双绿**为可开跑闸;不双绿不进 U5 |
| mini-desktop 串行跑 4 轮墙钟长(~3.4h)+ 烧 token/¥ | 资源/成本 | U2 批级 fail-closed 兜底;串行本就为省资源争用;单 gen ¥3 硬限 |
| `repairs` 字段是 `model_calls` 近似、非真自纠轮数 | 收敛中位图偏差 | 仅作辅助观测;go/no-go 主判据是 KTD1 失败计数,不依赖 repairs |
| R3 无强档对照不可测 | 便宜档天花板结论缺一块 | 明确记 conditional no-go、待强档;不强行判 KEEP 掩盖 |
| U4 AST 基建在 mini-desktop 摩擦 | U4 拖累开跑 | U4 务实回落(多信号硬化 + 人工抽检),完整 AST 作 follow-up |
| 轮间"修复"滑成调参/结构改动 | 收敛环失去离散性、无限调 | KTD2 钉死小修边界;结构级即退路信号、停环 |
## Verification(验证计划)
- **U2/U3/U4**:本机 `/bin/python tests/test_*.py`(底座无 pytest,逐文件跑),每单元红→绿→全套无回归。
- **U1**:S0 环境闸通过;S1 金标款 RunRecord 断言 accept/9门/三门/finished(不假阴)+ S1b 坏产物断言 `decision!=accept`(不假阳)——**harness 双向可判才放行 U5**。
- **U5**:收敛环台账 + aggregate + `decide_n5` 裁决三件齐,go/no-go 有据可查、可复算。
- **门不放松证据**:U3/U4 改动后金标 fixture 装配链冒烟仍 ACCEPT(九门 9/9 + 三门 3/3)。
## 迁移对账(跨设计面归属 · 防越位/揽活)
按 plan① 切片二跨设计面(§切片二 138)逐单元归属,确认零越位、零揽他 WU 的活:
| 单元 | 归属(plan① WU / 设计 SoT) | 性质 |
|---|---|---|
| U1 复现 smoke + DockerWorkspace 小验 | WU-D 的 DockerWorkspace harness 小验(ADR-6 子项)+ WU-C 5.1 运行时 | 本切片自有(前置阻断) |
| U2 批级护栏 | WU-F(预算闸/运行安全) | 本切片自有 |
| U3 退路树 n=5 适配 | WU-F(go/no-go 判定) | 本切片自有 |
| U4 validate_play_scene 结构化 | WU-C 5.4(结构化门) | 本切片自有 |
| U5 跑环 + go/no-go | WU-B(复杂档实例)+ WU-F(判定) | 本切片自有(核心) |
| feed→play 第二装载 / 源项目落库 | WU-C 5.6 | **移交后端**(go/no-go 后) |
| 工作室 Agent Team / 控制面 / ReMe | Phase B(决策⑤) | **gate 在 B 门后**,不在本 plan |
成功定义对齐本切片本职(用 n=5 收敛环定 M3 走不走),不拔高成"tier2 全部上线"。
## Sources(事实来源)
- plan① 切片二:`docs/plans/2026-06-25-生成引擎统一执行计划-AgentScope三档-plan.md` §切片二 121-140。
- 复杂档子计划:`docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md`(U1-U7 主体已落)。
- 权威基准 + spike 实证:`tier2/HANDOFF.md`(feie-005 accept;tokens~36K、墙钟~610s、cost_rmb=1.29、四道为收敛补的机器门)。
- 运行时 SoT:`docs/architecture/架构/生成引擎/agentic运行时架构图说.md` §4.2/§5.1/§5.3/§5.4。
- 底座代码事实(2026-06-28 侦察):`tier2/gen-worker/batch_run.py`(串行 run_matrix、CLI)、`worker/run_record.py`(RunRecord jsonl)、`aggregate.py`、`worker/fallback_tree.py`(五出口 decide、阈值硬编码不读 YAML)、`config/generation.yaml`(budget/model/iteration)、`worker/middleware.py`(四熔断)、`worker/run.py`(validate_play_scene:401-460 偏脆)、`worker/config.py`(M3 Anthropic / deepseek OpenAI、网关无 Opus/Fable)。
## 双评审发现与处置
**Codex(`codex-rescue`)· 2026-06-28 · 6 条全经代码核验属实、已全部在文档内修:**
- **B-1(blocker)** U5 题材键 `糖水` 非法(`BRIEF_VARIANTS` 是 `糖水店`,未知变体 batch_run.py:423 `raise ValueError`)→ 改 `糖水店`(并核出 `面包` 是对的、非 `面包店`)。
- **B-2(blocker)** U3 原拟调底座 `decide()` 做 R1/R2 分流,但 `decide()` 在 fallback_tree.py:195 无 deepseek 强便宜档即 Q1 早退、M3-only 被吞 → 改 `decide_n5` 自算 fail_system 集中度、直接用 `CONCENTRATION_RATIO` 判,不调 `decide()`。
- **B-3(blocker)** U4 改 `validate_play_scene(src)` 与现有 `build()` 调用 `validate_play_scene(game_id)`(run.py:196)冲突 → 公开签名不变,抽纯函数 `validate_play_scene_src(src)` 承载 AST、测试喂它。
- **M-1(major)** U2 测试 mock 成本字段错(`result_to_record` 取 `cost.cost_rmb`,batch_run.py:243)→ mock 改 run-result 形状 `{"cost":{"cost_rmb":30}}`。
- **M-2(major)** U3 出口名与既有常量不一致(`go`/`R1_退模板化`/`R2_补骨架`/`R3_天花板`/`KEEP_留观`,fallback_tree.py:79-87)→ 出口对齐既有值 + 新增 `R3_conditional`;U5 分支与退路树 mermaid 同步。
- **M-3(major)** U5 原用 `aggregate.py` 取裁决,但它固定调旧 `decide()`(aggregate.py:289)、对 M3-only 无效 → 加 `decide_n5` 的 CLI 入口,S6 走它取 go/no-go。
- **m-1(minor)** aggregate 过门率只看 `pass_gate`、不含 `finished` → `decide_n5` 自数失败(含 finished),不复用 aggregate 的 pass_count;aggregate 三图仅作描述性。
- 总评:**方向可执行,U3/U5 接缝需先修** —— 已修。
**Opus 对抗式评审 · 2026-06-28 · 2 blocker + 5 major + 6 minor,全经代码核验、全部已折入:**
- **B1(blocker)** 深化 Codex B-2:`decide()` 不仅 Q1 早退,且 `aggregate.py:32` 也调 `decide()`、对 M3-only 落档矛盾 KEEP → `decide_n5` 改为**覆写**(非包装)、复用本模块 helper `_system_concentrated`/`_bucket_share`;aggregate 三图描述性、裁决唯一以 `decide_n5` 为准并显式声明其 KEEP 系档名缺失。出口名对齐既有常量。
- **B2(blocker)** = Codex B-1(糖水店),已修。
- **M1(major)** KTD1"末轮"歧义 + **首轮裸过假 GO**(p≈0.6 模型首轮 ≥4/5 概率 ~1/3)→ 收敛改"末轮≤1 **且 ≥2 轮**":修复后轮≤1 或冷首轮需确认轮复现;KTD1/U3/U5/mermaid 全同步。
- **M2(major)** U1 只验金标过(不假阴)、未验坏游戏拒(不假阳)→ 加 **S1b 反向 smoke**,双向绿才放行 U5。
- **M3(major)** KTD2"收窄可写面"会累积漂移成无限调参换皮 → 加**定性边界闸**(不搬走系统、单轮 <2 道新门;触系统核心即结构信号停环)。
- **M4(major)** 跨轮 n≤20 靠人工数 prefix、与 KTD3 fail-closed + §6.10 矛盾 → 加**跨轮机器闸**(`assert_under_round_cap`>20 拒跑 + `decide_n5` len>4 拒裁)。
- **M5(major)** 改 `BatchConfig.n` 默认 6→5 制造两义性(它是每格重复次数,非"一轮5款")→ **不改默认值**、只加注释澄清。
- **minor(6)**:M-a `main()` catch `BatchBudgetExceeded` 非 0 退出;M-b U1 加 chrome/venv 自检(并入 S0);M-c mini-desktop 可达列硬前置(S0 + Risks 行);M-d token/wall 闸"慢轮即停"标为有意;M-e `decide_n5` 返回 `{exit, reasons[]}` 复数对齐底座;M-f 对齐 generation.yaml 失真注释。
- **正面确认**(无需改):越位/揽活 check 通过、self-surfaced 点处置得当、U4 偏脆诊断准确、R3 不会误吞(R1/R2 先判)。
- 总评:**有 blocker,修后可收口**——B1/B2 + M1/M2/M4 关乎 go/no-go 可信度与防无限调参,已全修。
**处置小结**:Codex 6 + Opus 13(去重后)发现已全部在文档内修毕,无跨文档遗留 TODO。**双评审收口。**
## 执行发现
**2026-06-28 执行(本机 + mini-desktop tier2-cv worktree):**
- **U2/U3/U4 本机 TDD 全绿、已 push(`eed34d0a`)**:U3 fallback_tree 自测 9 + U2 批级护栏 3 + U4 play-scene 门 4。
- **U1 关键发现 —— 漏掉的 env 闸(plan 自我修正)**:首跑 fresh M3 面包 smoke「FAIL」(852s、撞 max_iters=40+step_cap>60、没 finish),一度误判为 M3 不收敛 / caps 太紧 / dev 回归。**真因是环境**:新建的 `tier2-cv` git worktree **没有 `node_modules/esbuild`**(gitignored、不随 checkout 来),`build-phaser.mjs`(`import esbuild`,phaser 走 shim external)构建失败 → gen 的 build 步拿不到 bundle → M3 收到的是 build 错误而非九门反馈 → 写一堆探针脚本(`_probe_runtime.mjs`/手搓 build.sh `find / -name esbuild`)瞎试 → churn 撞熔断。
- **铁证(env 修后)**:`ln -s` 接上 esbuild(`tier2-run/game-runtime/node_modules`)→ `build-phaser.mjs` 立即产 bundle(42KB)→ 在 M3 那份「失败」产物上 `run_gates`:**九门 7/9 过**(只 H_progress ❌ progress=false)+ **富三门 2/3 过**(只 经济 ❌ profitPathWin✗ 盈利路赢不了),decision=fix。即 **M3 本就写出接近过关的多系统富游戏、只差 2 道可修平衡门**(spike 式可据反馈自修),「M3 不收敛」是 build 坏导致拿不到反馈的假象。
- **教训(已补 U1 S0 + Risks)**:fresh worktree 必须先备 `node_modules/esbuild`;低 CPU(3s/12min, STAT=Sl)是 I/O-bound 假象、不能据此判"卡死"——要读工作区产物 + run_gates 取真九门。
- **U1 沙箱裁定**:收敛环走 in-process 本地 runner(spike-proven);DockerWorkspace 托管小验作 go/no-go 后 Phase B 项(in-process 兜底已足,不阻塞)。
**U5 收敛环 go/no-go(2026-06-28/29 真跑,M3-only):**
- **数据(均带 confound)**:smoke 面包 accept;Round1(chrome 泄漏未修+live 争用):水果 accept/美食·咖啡 fail;iso(chrome 泄漏已修+逐款隔离,但 live 争用未除):糖水店 accept、美食·水果·咖啡·面包 fail(1/5)。
- **关键发现 ① 收敛是 run-dependent(随机)非干净 variant-dependent**:同款 水果(Round1 accept→iso fail)、面包(smoke accept→iso fail)在不同跑里 accept↔fail 翻转。
- **关键发现 ② 失败模式集中「进度/经济平衡门」**:seven_gate(H_progress·progress=false)+ 经济门(profitPathWin✗ 盈利路赢不了)。M3 写得出结构完整多系统富游戏(过大多数九门 + 三联动/latch),但**不可靠地调好「赢的条件/经济平衡」**(spike feie-005 靠 verdict 反馈自调 patience 修通过、这里常 thrash 到 step_cap 没修通)。
- **confound 未尽除**:chrome 泄漏已修(生效、款间回落不累积);但 live worker:9501/@8200 争 4 核未除(不碰生产)→ iso 跑慢、fail 或被资源饿死 inflated。真 pass 率需暂停 live 干净跑才准。
- **退路判定(创始人 2026-06-29 纠正:退路在 agent 层、不在工具层)**:瓶颈 = M3 不可靠地做好**自己的玩法职责——自调「赢的条件/经济数值平衡」**(经济门 profitPathWin / H_progress)。**经济/数值平衡是 agent 的玩法职责,不该在基础工具层做静态模板/补骨架**:① 静态验证调不动动态平衡(平衡靠真玩 + agent 推理判);② 每游戏经济数值本就会改、预制是浪费。故**真退路在 agent 层**:① **verdict 反馈质量**(把"盈利路为何赢不了"讲清让 M3 像 spike 那样自修平衡)② **模型能力**(中等档 M3 推理不动这类平衡 → 换档)。**非工具层加固、非换档以外的 R1/R2。**
- **引申(待生成线后续重审,非本切片投)**:`经济门`/`H_progress` 这类对**玩法平衡**的静态门该不该这么判、还是交真玩 + agent 自己负责——属生成线门设计问题。
- **go/no-go = conditional**:tier2 卡在「agent 能否可靠自调 win-balance」这个 **agent 层问题**(现 M3 不稳、~1-2/5 confounded),**不卡工具层**;随 agent/反馈层演进会改善。**非 no-go**(M3 demonstrably 能过全门)、**非干净 go**(收敛太不稳)。
**切片二执行收口**:U1-U4 + env 修 + chrome 泄漏修全交付(commits eed34d0a/8d4654ab/55a213e7);U5 go/no-go = conditional,瓶颈是 **agent 层 win-balance 自调可靠性**(非工具层)。**不投工具层模板化/补骨架(创始人纠:错层)、不现在测精确率(率随 agent 演进会变)。** 改善随 agent/反馈层 + 模型能力演进,或后续重审平衡门的判法。
## 后续工单(2026-07-02 落账 · opus 自治工单)
收敛环已跑、结论 conditional。本节把剩余工作落成可自治领取的工单(六要素:目标 / 事实指针 / 边界红线 / 验收门 / 坑 / 自审)。执行模式 = opus 会话 plan 模式领单 → goal/ultracode 自治执行 + 自审;没全绿报 BLOCKED,不谎报 DONE。
### F-1 · verdict 反馈质量改进(agent 层 · 可立即动)
- **目标**:把「盈利路为何赢不了」讲清楚喂回 M3,让它像 spike feie-005 那样据 verdict 自修 win-balance——治「经济门 profitPathWin✗ / H_progress progress=false 时 M3 thrash 到 step_cap 没修通」。
- **事实指针**:本档「执行发现」U5 段(失败模式集中两门);`tier2/gen-worker/worker/run.py`(verdict 组装)+ `worker/agent_loop/studio.py`(resume 回喂);spike 自修实证 `tier2/HANDOFF.md` feie-005。
- **边界红线**:只改 verdict 反馈的表达与回喂(agent 层);**不做工具层静态模板 / 补骨架**(创始人 2026-06-29 纠正:经济 / 数值平衡是 agent 玩法职责);不动九门判定逻辑;不动便宜档。
- **验收门**:用 win-balance 失败产物(构造或取 iso 台账)→ 新反馈回喂 → M3 自修通过实证 ≥2 例;金标 fixture 装配链冒烟仍 ACCEPT(门不放松);相关单测绿。
- **坑**:fresh worktree 缺 `node_modules/esbuild`(symlink 或 install,S0 闸);mini-desktop 用真 IP 绕系统代理;别据低 CPU 判卡死(读产物 + run_gates 取真九门)。
- **自审**:附真实 commit hash + 实证 game-log/verdict 路径;测试输出原样贴。
### F-2 · 干净复跑(需创始人窗口)
- **目标**:去除 live 争用 confound,取真实 pass 率台账,供 go/no-go 终审(fable)复核。
- **做法**:暂停 live worker:9501/@8200 的窗口内,按本档 U5 runbook 照跑(`--prefix cv-clean-r{N}`);推荐顺序 F-1 → F-2(带上改进后的反馈更有信息量)。
- **验收**:无争用环境 ≥2 轮台账 + `decide_n5` 裁决 + 三件证据落盘;结果回写本档「执行发现」与 HANDOFF;**窗口结束必须恢复 live 进程并验证健康**。
### F-3 · 强档对照(解锁:网关已补 glm-5.2)
网关补上 GLM-5.2 / Opus 强档后,跑中等 vs 强档对照,把 R3_conditional 判成真结论(天花板 or 换档)。2026-07-04 前置已解:new-api `/v1/models` 实测在列名=`glm-5.2`。**执行口径(fable 定)**:强档批必须落**独立台账**(如 `results/tier2-f3-strong.jsonl`)——`decide_n5_from_jsonl` 按 run_id 里 `r{N}` 分轮,`f3-strong-r1` 前缀会被吸进 r1 轮、污染收敛判据,不许与收敛环共账。
### F-2 执行记录(2026-07-04 窗口 · fable 主持,创始人过程中两次落令)
- **R1(cv-clean-r1,无争用干净环境)= 3/5**:水果/咖啡/面包过,美食/糖水店败。台账 fail_system 空 → `decide_n5` 判「失败分散」出 R3_conditional;**逐局对日志推翻「分散」**:两败局同构——设计团队 3/4 专家已产出、末段调用/leader 撞 240s 墙钟 → 降级单 agent 设计 → writer 40 轮不收敛 → step_cap 熔断;三过门局设计团队全部正常收敛,**超时↔失败 5/5 完美相关**(集中于编排层「设计超时降级路」,树看不见=台账归因盲区,工单 k 由此立)。咖啡局撞过 writer 40 轮墙被续修环救回 = 续修机制本身健康的同批对照。
- **修一(KTD2 · `0a9504e4`)**:`design_team.timeout_s` 240→420(M3 thinking 慢尾单调用 120–180s,与 step_timeout_s 同刻度;YAML+builtin 双改)。
- **R2(cv-clean-r2)首局即给出分层证据后中止**:美食局设计团队正常收敛(修一咬合、全轮 0 超时),但 writer 仍 40 轮不绿、resume 后快撞 60 步顶(502s/¥0.32 快败 vs R1 1464s churn)——**40/60 是预算墙不是能力墙**。创始人落令「提高轮数阈值、超时阈值」→ 中止 R2(其美食败局留账为史),**修二(`bfeaea2d`)**:writer_max_iters 40→60 / step_cap 60→100 / max_model_calls 80→120 / 墙钟 1800→2700s(¥ 两段式 50/75 与墙钟接管失控保护;cheap Service 显式传 150 不受影响)。
- **R3(cv-clean-r3,新阈值)= 产品口径 3/5**:美食 ✅(¥0.47——R1 churn 熔断、R2 快败的同款,阈值修后一次走通,全轮 0 设计超时)/ 咖啡 ✅ / 面包 ✅(¥1.51/4700s 重但收敛)/ 水果 ✗(economy/resource,¥0.94/2552s)/ 糖水店 ✗(seven_gate,99 步烧至新步顶附近)。批级墙钟护栏在五款全落账**之后**触发硬停(10864s≥10800),fail-closed 正确、零丢失。
- **两处判定器口径发现(fable 终审拆解)**:① 咖啡实为 accept+pass 但 `finished=False`——agent 不知道自己已经绿了,磨到 100 步被熔断、熔断后跑门才定 accept;树的 `_is_fail` 三条件严口径把它记败(各轮失败数=[2,1,3]),产品口径它是过门好游戏。引出新观测项「**早收敛探测**」:门绿即催 finish/截停,别让已完工的局烧到步顶(与工单 c 的软停升压同族、但触发器是门绿非预算)。② `decide_n5` 本轮出口 **R2_补骨架不可采信**:全 6 次失败只有 1 次带 fail_system 归因(水果 resource),`_fail_system_dist` 只在有归因者内归一 → n=1 撑起「resource 100% 集中」;r3 还跑在 pre-infra_flags 代码上无编排层佐证。**fable 裁定:不据此启动平台侧补骨架**;真实图景=阈值修复干掉设计超时族(美食 ✗✗→✅),糖水店持续硬(两轮步数区败),水果抖动(r1 过、r3 经济门败)。
- **末轮 >1 败=未收敛坐实 → 按树的 R3 分支语义进 F-3 强档对照**(部署树已升 `9a64b3a3`,含 infra_flags 穿线与软停升压;工单 h 的 apscheduler 集成测在 mini-desktop 真环境 5/5)。F-3 = glm-5.2 同五变体同 caps、独立台账 `results/tier2-f3-strong.jsonl`;若 GLM 过 水果/糖水店 → M3 天花板证据(R3_ceiling 换档/混档判给创始人);若 GLM 同败 → 题面/门侧工艺问题(归 F-4 质量轴),非模型档位问题。终判回填本节。
- **R4(cv-clean-r4,writer 真 60)= 4/5,收敛环收口**:美食 ✅28 步/¥0.50、咖啡 ✅22 步/¥0.33、面包 ✅23 步/¥0.35、糖水店 ✅**17 步/¥0.39(r1/r3 两轮的顽固户全场最快过门)**——四款全自然 finish(finished=True),r3 的「门绿不自知烧到步顶」在本轮消失;唯一败=水果(presentation@步顶,infra=step_cap_tripped)。**`decide_n5` 正式出口 = KEEP_留观(收敛·go)**:各轮失败数=[2,1,3,1],末轮 1 且 4 轮,KTD1 成立;r4 infra 分布行由工单 k 穿线在判定 reasons 生产生效。
- **fable go/no-go 终判(2026-07-04)**:**tier2 收敛环 = go(KEEP_留观)**,留观件=水果(三轮三种失败模式:r1 过/r3 经济门/r4 表现层——抖动画像非系统性梗阻,归品类微调与 F-4 质量轴,不阻 go)。**Δ3 升档梯子结论:M3 保持 tier2 中档默认,glm-5.2 记升档选项、不换档**——F-3 实测强档同率不同谱(3/5)、单价 2-3×,而 M3 在 writer-60 下 4/5 且 ¥0.33-0.50/款;诚实注记:F-3 跑在 40/100 制、r4 跑在 60/100 制,严格同制对照未做,但 GLM 成本画像已足以支撑「不换档」;若后续要混档路由再补 F-3'(60 制)。**r3 时点的 R2_补骨架出口被 r4 推翻**(顽固户糖水店在阈值真达 writer 后 17 步过门=当初是预算墙非骨架缺口)。归因诚实口径:r4 大幅改善与「writer 60 消除 40 轮强制截断」一致,亦不排除 M3 采样日方差贡献;n=5 方差纪律下 KTD1 的多轮判据已按协议压制单轮运气。残余观测三件:①早收敛探测(门绿即催 finish/截停——r3/F-3 咖啡两档同现、r4 未现,结构性缺口仍在)②水果留观微调 ③fail_system 归因覆盖率低(6 败仅 1 归因;infra_flags 已补编排层、游戏系统层归因待 verdict 富化)。r3 日志 writer 仍打「max iteration numbers **40**」——阈值批把 writer_max_iters 提到 60,但 `batch_run.RunParams(40)`/`batch_run --max-iters default=40`/`run_engine --max-iters default=40` 三处写死默认显式透传,把 run_studio 的 None→genconfig 解析短路;middleware 系旋钮(step_cap>100 / 设计超时>420 / 墙钟 2700)经 genconfig 直读全部生效,唯 writer 没到位。三处已改 None 哨兵回归单源。**含义:r3 的 writer 实际仍 40**(美食走通主要吃 step 顶 100 给续修环让的量);「提轮数」真达 writer 要等 r4;F-3 与 r3 同为 40/100 组合 → 强档对照内部有效。另:糖水店 r3 设计团队在 **420s 下仍超时**(非边缘慢尾,团队内单调用病理性挂起),已非再抬墙钟能治,记观测。
- **同窗并行(创始人「不空转」令)**:残余工单五路 opus 并行开工——(c) 软停 finish 逼近强化 /(h) Service 工具面 Planning/Team/Schedule 收窄调研 /(f+i) 收口打印幂等+bake_off fail-fast /(k) 台账 infra_flags 归因穿线(observe-only,治本节盲区)/(j) studio 透传集扩 1003(+1005/1006 调研)。
### F-4 · 平衡门判法重审(设计问题 · 不在 opus 工单内)
「经济门 / H_progress 这类玩法平衡该静态判还是交真玩 + agent 自评」= 生成线门设计问题(「执行发现」引申项),随《游戏质量与爆火能力》SoT(质量模型,fable 主笔)一并裁。
## 状态
**§6.8 双评审已过** → **已执行(2026-06-28/29)**:U1–U4 交付(`eed34d0a`/`8d4654ab`/`55a213e7`)+ U5 收敛环真跑,go/no-go = conditional(当时口径;创始人过程中裁定执行口径并纠正退路层级 = agent 层)。→ **2026-07-04 终局:F-1–F-3 全执行完毕,`decide_n5` 出口 = KEEP_留观,fable 终判 = go(留观件=水果;M3 保持中档默认、glm-5.2 记升档选项不换档)**——全过程与终判依据见「F-2 执行记录」节;F-4 已归质量 canonical §8。四轮窗内落地的引擎修复:设计团队墙钟 420s(`0a9504e4`)/ 预算阈值批 writer60·step100·墙钟 2700(`bfeaea2d`)/ `max_iters` 双源默认值影蔽拆除(`7c379266`)/ 软停 finish 逼近升压(`9a64b3a3`)/ 台账 infra_flags 编排层归因(`bf00638c`)。plan① 切片二快照与各板 2026-07-04 已对账。