games-development-ai/docs/plans/2026-06-28-002-feat-tier2-n5收敛环-go-no-go-plan.md
lili 455d45be88
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
docs(tier2): 收敛环终局落档——r4 4/5 收敛·decide_n5=KEEP_留观·fable 终判 go(留观=水果;Δ3 梯子=M3 保持中档默认);板对账+§13 三条蒸馏(双源默认影蔽/worktree 基线必验/pgrep 探针自匹配)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 06:52:39 -07:00

428 lines
53 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: tier2 富游戏 n=5 收敛环 go/no-go 细化执行 plan
status: 已执行(2026-06-28/29 真跑 · U1U4 交付 + 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<br/>M3 写面包店富游戏<br/>9门+三门过·finished·n=1"] -->|"机制已证<br/>但只跑通一款"| Q{"M3 能否<br/>跨多款<br/>稳定收敛?"}
Q -->|"n=5 收敛环"| go["go<br/>(收敛即止)"]
Q -->|"不收敛"| R["退路树<br/>按失败模式分流"]
subgraph 底座["批跑底座(已落 · dev/2.0.0)"]
br["batch_run.py<br/>串行 model×variant×n"]
rr["RunRecord(jsonl)<br/>含 fail_system 分流键"]
ag["aggregate.py<br/>过门率/¥/收敛中位/fail_system 分布"]
ft["fallback_tree.py<br/>五出口自动判定器"]
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)。
- **KTD5harness 复现 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 前置阻断<br/>dev/2.0.0 复现 smoke + DockerWorkspace 托管小验"]
U2["U2 批级护栏 fail-closed<br/>批¥/批墙钟/token 总闸"]
U3["U3 退路树 n=5 适配<br/>收敛 GO gate + R3 conditional + n=6→n=5"]
U4["U4 validate_play_scene 结构化<br/>正则→AST(护环判定可信)"]
U5["U5 跑 n=5 收敛环<br/>串行·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 <dev2树> rev-parse --abbrev-ref HEAD # 先确认 dev/2.0.0 工作树路径与分支
cd <dev2树>/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`,已知;`<dev2树>` 在 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。**
- [ ] **S3DockerWorkspace 托管小验 · 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)**:
- [ ] **S1RED**`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`/字段未定义)。
- [ ] **S3GREEN** `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/` 确认无回归
- [ ] **S6commit** `feat(tier2): 收敛环批级+跨轮护栏 fail-closed(批¥/墙钟/token/n≤20 闸)(切片二 U2)`
---
## U3 · 退路树 n=5 适配
**目标**:`fallback_tree.py` 的阈值是为 n30 设的统计触发线且硬编码不读 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.pyimport 时读",但代码是模块级硬编码常量不读 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 轮"**(防首轮裸运气——真实过门率 p0.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)**:
- [ ] **S1RED** `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 失败 2Round2 失败 0 `'go'`)。
- [ ] **S2** 跑测试看失败(`decide_n5` 未定义)。
- [ ] **S3GREEN** 实现 `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`:presentation0.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()` 旧出口不破。
- [ ] **S6commit** `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)**:
- [ ] **S1RED**`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** 跑测试看失败(现实现假过 → 测试红)。
- [ ] **S3GREEN**`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(防过度收紧误杀合法)。
- [ ] **S6commit** `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/ 目录可写。
- [ ] **S2Round 1** 跑一批 5 款(5 题材 × n=1;题材键须与 `BRIEF_VARIANTS` 字面一致——是 `糖水店`/`面包`,不是 `糖水`/`面包店`,否则 batch_run.py:423 直接 `raise ValueError`):
```bash
cd <dev2树>/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<br/>(冷首轮)"| CONF["跑确认轮<br/>(同变体·不改码)"] --> R1R
E -->|">1"| FIX["S4 修复(小修·守 KTD2 闸)<br/>或结构信号→停环"] --> R1R
E -->|"末轮 0 · ≥2 轮"| GO["✅ go<br/>tier2 富游戏线走"]
E -->|"末轮 1 · ≥2 轮"| KEEP["✅ KEEP_留观<br/>go · 留观该款微调"]
E -->|"累计 20 仍 >1"| C{"失败模式集中度<br/>(fail_system)"}
C -->|"presentation ≥0.50"| RR1["R1_退模板化<br/>表现层收窄/加骨架壳"]
C -->|"resource/merge/order ≥0.50"| RR2["R2_补骨架<br/>该系统补平台侧脚手架"]
C -->|"无集中 + 无强档对照"| RR3["R3_conditional<br/>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**:本机 `<venv>/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 慢尾单调用 120180s,与 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)**:U1U4 交付(`eed34d0a`/`8d4654ab`/`55a213e7`)+ U5 收敛环真跑,go/no-go = conditional(当时口径;创始人过程中裁定执行口径并纠正退路层级 = agent 层)。→ **2026-07-04 终局:F-1F-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 已对账。