docs(design): A-model 生成"更好玩" 评审版+Phase1执行版+图说(§6.8 评审版收口)

评审版(review-closed,§6.8 Opus 全量 + Codex 半程独立印证 + 代码核实):
- ① 策划 agent(升级 designNode)= 低风险、大半已通(design 经 K_ENRICHED 喂 amodel);
- ② 音频/资产线 + ③ modify 双路 = **净新建**(双评审纠正原稿"复用"误框:amodel 路
  validate→play 跳 scaffold/asset/build,SAA asset 机器与 modify 生命周期均 gamedef 专用);
- 质量门三类(API-存在静态门[玩法无关] / per-game gatespec 断言[语义·复用 H] / 好玩 N-of-8[设计时]),
  **通用九门保持玩法无关、零品类语义**(创始人纠正);3 阶段分期。

Phase 1 执行版(execution-pending,待 §6.8 双评审):6 单元
  = 设计 agent harness(JS tool-using,读真 sim skill)/ 薄 prompt / designNode amodel shell-out
  / API-存在静态门 / 扩 sim skill(进货+节奏+可机检 N)/ 好玩-经济 per-game 断言接线。

图说更新(519→999 行,11 内联 SVG,自包含):纠正现状(amodel 跳 scaffold/asset/build、九门玩法无关、
  gatespec→play_spec→H)+ 新增新设计 6 图 + 3 阶段演进(三色:复用/净新建/零触碰)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-23 09:29:25 -07:00
parent 88d3a23787
commit 68472bb560
3 changed files with 1365 additions and 0 deletions

View File

@ -0,0 +1,202 @@
---
topic: 让 A-model 生成"更好玩"——游戏策划 agent + 音频/资产线 + modify 双路
type: review
date: 2026-06-23
status: review-closed(§6.8 双评审已收口:Opus 全量 + Codex 半程独立印证 + 代码就地核实)
owner: 创始人 + 6c6g 文档/设计线
related:
- .agents/skills/agentic-amodel-generation.md(A-model 生成 harness 现行形态)
- .agents/skills/sim-business-game-design.md(经营玩法设计 skill,design 层知识库)
- .agents/skills/littlejs-game-dev.md(code 层作业手册 + 6 footgun)
- game-cloud/.../saa/SaaStudioGraph.java / SaaStudioNodes.java(拓扑 / designNode / modify / amodel 短路)
- memory: amodel-generation-build-state(2026-06-23:root-fix A+B、6 footgun、v2/v3)
---
# 评审版:让 A-model 生成"更好玩"——策划 agent + 音频/资产线 + modify 双路
> 评审版 = 结论先行、架构级、不含代码细节。批准后写执行版。**本版已过 §6.8 双评审收口(见 §0.1)。**
---
## 0. 结论先行
**核心判断**:九门是**运行时、玩法无关**的可玩性门(boot/活性/输入/有进展),它验"能玩"不验"好玩",**且故意不懂任何品类语义**——这是它的价值,不能破坏。生成"能玩"已稳(v3 干净 re-gen 8/9,6 footgun 仅剩 1 个 audio 方法名幻觉);"好玩"短板在 **design 层无策划脑 + 平台音频/资产能力缺口**。
**三项决策(求批)+ 双评审的一个重大纠正:**
1. **① 策划 agent(升级 designNode)= 低风险、且大半已通**——双评审实证:design 输出**已经 K_ENRICHED 富化-brief 喂到 amodel harness**(create 路)。本项主要是把 design 调强(prompt + 升 M3),新接线少。**建议批。**
2. **② 音频/资产线 + ③ modify 双路 = 净新建,不是"复用/补深"(双评审 BLOCKER,代码已核实)**——amodel 路 `validate-ok 短路到 play、跳 scaffold/asset/build`(SaaStudioGraph:502),现有 asset 机器(assetNode/sourceProject.assets/build-from-source)与 modify 生命周期(deterministic/regenerate-module)**都在 gamedef 分支,amodel 根本不走**;harness 每次从 `_template` 全新 scaffold、无"读旧项目"工具。**故 ②③ 必须在 amodel harness 内部新建**,执行版按此基线。
3. **质量门按"门各司其职"分三类,九门保持玩法无关(采纳创始人纠正)**:
- **API-存在静态门(玩法无关,新增)**:校验游戏代码调的插件方法在该插件 api.d.ts 真存在 → 廉价根除"方法名幻觉"类(defineScenes/stopBgm…),取代逐个 footgun-doc 的打地鼠。
- **per-game gatespec 断言(玩法语义,design agent 产)**:经济/玩法正确性(买扣钱/库存减)= design agent 给这一局产断言 `{path,op}` 喂 play-spec、复用 **H 门现成的 `assertAfterPlay`**(`score increased` 本就是这种断言),并保证该动作 driver 可达。**通用九门一行不改、绝不加品类语义。**
- **好玩 = design-output 机检(设计时)**:design 产出须含"反无趣 8 条"里可机检的 ≥N 条(动作有飘字+音效 / score-delta / 解锁阈值 / 经济扣减…)→ 给主观调参兜客观地板。
**分期(纠正后)**:Phase 1 = ① 策划 agent(**仅 create**)+ API 静态门 + 好玩机检 —— 不依赖 modify/资产线,可独立验"设计层补料能否更好玩";Phase 2 = ② 音频/资产线(amodel harness 内部);Phase 3 = ③ modify 双路(净新建)。
---
## 0.1 §6.8 双评审收口记录
- **Opus 全量对抗评审**(220s,完成):抓到 2 BLOCKER(②③ 误框为复用)+ 4 MAJOR + 8 遗漏面,见下逐条折叠。
- **Codex 对抗评审**(byv5smmau,**读完代码后挂死、未写收尾**,48min 停滞)——但死前 captured 的中途结论**独立撞到同一 BLOCKER**:「modify 现仍是 deterministic/regenerate-module 寻址、非'扩 design'」+「amodel 在 generate 直接 shell-out、不走 design/asset 节点」。**双盲一致 = 最强信号。**
- **代码就地核实**(我):B1(无 amodel modify 分支 + harness 无 load-base 工具)、B2(SaaStudioGraph:502 amodel 跳 scaffold/asset/build)、经济原语缺(插件清单无库存/经济原语)——**三条全属实**。
- **结论**:按 §6.8「Codex 不可用→回落 Opus」,实得 = Opus 全 + Codex 半程印证 + 代码核实,强于单评。发现项已全部折入本版。**跨文档/执行期 TODO 列 §10。**
---
## 1. 背景与问题
### 1.1 触发:创始人亲玩 v2 的三反馈 + 根因(已查证)
| 反馈 | 根因(锚) | 类别 |
|---|---|---|
| 没有进货环节 | brief 把成长设成"营业额被动解锁"(`core.js: computeUnlocked`),没要进货/库存环;**且 12 插件无经济原语** | 设计缺口 + 平台原语缺 |
| 顾客来得太快 | `balance.js` IDLE_SPAWN 1300 / RUSH_SPAWN 700 / RUSH_START 20s;brief 只说"渐入高峰"没给速率 → M3 拍激进 | 数值调参(设计未给目标) |
| 没有背景音乐 | (a) `audio-music` 仅 ZzFXM 合成、不支持 mp3 → 外采 ~9MB BGM 用不上;(b) M3 写 ZzFXM `SongData` shape 错 → loadSong 拒 → 静默 | 平台能力缺 + footgun |
### 1.2 现状基线(已验证)
- **"能玩"已稳**:root-fix A(门可信化 `cbe42933`)+ root-fix B(input→L1 `88d3a237`)+ 6 footgun 蒸馏 → v3 干净 re-gen **8/9**(H score 0→1202,5 footgun 全避开),**唯一栈错 = 第 6 个 audio 方法名幻觉 `audioMusic.stopBgm()`(真名 `stop()`)**。v2(hand-fix 5 处)已 studio 真玩。
- **"好玩"未达标**:design 层是一次便宜模型调用产设计稿+gatespec,无玩家/策划视角的机制规划。
- **关键路由真相(双评审揭出)**:amodel 路 `generate` 节点内直接 shell-out 到 harness,`validate-ok` **直奔 play、跳 scaffold/asset/build**(SaaStudioGraph:502);harness 产**单个自包含 `bundle.iife.js`**。→ 任何"扩 SAA 图 asset 节点"的方案都打不到 amodel。
### 1.3 已存在 vs 需新建(双评审纠正后)
| 件 | 状态 | 用于 |
|---|---|---|
| `designNode` + design→amodel(经 K_ENRICHED 富化-brief 文本) | **已通**(create 路) | ① 策划 agent(升级即可) |
| modify 生命周期(deterministic / regenerate-module) | **gamedef 专用,不调 amodel harness** | ③ 需**新建** amodel modify |
| asset 机器(assetNode / sourceProject.assets / build-from-source) | **gamedef 分支,amodel 跳过** | ② 需在 **harness 内部新建** |
| `loadHostAssets`(浏览器图片拉取) | 已有,**但明确不载音频** | ② 图片可借;音频全新 |
---
## 2. 目标 / 非目标
**目标**:G1 经营游戏自带核心经营环(进货/库存或等价资源循环);G2 节奏/数值舒适可调(design 给目标值);G3 有音乐(mp3 压缩+懒加载);G4 modify 路可用(对现有游戏按意图改);G5 studio 见水彩+听音乐。
**非目标(硬约束)**:
- **九门绝不加品类/玩法语义**(创始人定)——玩法正确性走 per-game gatespec 断言,不进通用门。
- 不做 3D / Tier2-3;不重写 code ReAct(只改它的输入=更好的设计);不引入 future-state 组件;不把进货硬编码进模板(玩法=生成域)。
- 不强制每款都有 mp3(无音乐资产→退 ZzFX/静音,容错)。
---
## 3. 推荐方案
### 3.1 ① 策划 agent(升级 designNode)——低风险,大半已通
- **定位**:design 节点 = 有游戏设计脑的策划 agent;知识源 = `sim-business-game-design` skill + `littlejs-game-dev` 可达能力子集。
- **create**:意图+品类 → 玩家视角完整设计(核心循环;经营必含资源循环;**数值/节奏目标值**;音频方案;juice;**可达性自检**=只用 12 插件+boot.ctx 能达成的机制)。
- **输出通道(双评审纠正)**:design 输出**当前以富化-brief 文本(K_ENRICHED)喂 amodel**——这条已通。**决策点 O4**:维持"更丰富的散文 brief"(今天就能用、零接线)vs 升级为"结构化设计 schema"(需改 harness prompt 去解析,且会扰动刚转绿的 amodel e2e)。**倾向先散文、后结构化。**
- **模型档(D1)**:倾向升 M3(策划要设计脑);design 每局一次、保持**在 repair 环之外**(m2:进 repair 环会乘成本)。
- **modify(归 Phase 3)**:见 §3.3。
### 3.2 ② 音频/资产线(amodel harness 内部新建,非扩 SAA asset 节点)
- **定位纠正**:asset/audio 必须做进 **`tools/amodel-gen/` harness 自己的 scaffold/build/stage**(amodel 不走 SAA assetNode)。
- **图片**:harness stage 已会拷 `assets/`(本会话已加),`loadHostAssets` 浏览器侧已载图(staged 真玩已验水彩)。缺口 = **studio 侧把资产随包可达**(studio 现只内联 engineBundle)。
- **音频(M1:比"下发"深)**:需一条**新运行时音频通道** —— (1) `audio-music` 插件加 `loadTrack(url)`/decode/loop(**插件契约变更,走 §6.6 contract-first**);(2) host 首手势解锁接到文件播放(现 `audioUnlocked`/`audioFactory` 喂的是合成器);(3) harness build 拷/引 mp3 + manifest;(4) studio serve mp3;(5) **`build-from-source` 现禁 `fetch`** → 要么开例外(懒加载 fetch),要么 base64 内联(则撞包体门,见 M2)。
- **体积(M2)**:首屏≤2MB/总≤10MB 门**目前在 amodel 路未真正布防**(publish-amgen 只记 bundleSize)。执行版须:(a) 明确该门在 amodel 哪里布;(b) 解 inline-vs-lazy-fetch 张力(懒加载需同源 served URL + fetch 例外;内联则进包撞门)。**倾向 mp3/opus 压缩(ffmpeg,~150–300KB)+ 懒加载 fetch + 首帧后**。
- SFX 留 ZzFX(短音效,几十字节)。
### 3.3 ③ modify 双路(净新建)
- **现状(B1)**:无 amodel modify;harness 从 `_template` 全新 scaffold、**无"读旧项目 src/"工具**。
- **需新建**:(a) harness 加"载入 base 项目 src/"的入口;(b) 新 `modifyRouter` 的 amodel 分支;(c) 决定 modify 语义 = **全量重生成-带意图**(便宜,近似现 regenerate-module→generate 退化)vs **真增量改 src/**(贵,新 harness 能力)。
- **第一靶纠正(m4)**:v2 是 hand-fix 过的产物,src/ 未必能干净 round-trip → **用干净 v3-类项目做 modify 首靶**,别用 v2。
### 3.4 质量门(三类各司其职——九门玩法无关不破)
- **(a) API-存在静态门(玩法无关·新增)**:check 阶段校验游戏源调的 `plugins.X.method()` / `ctx.method()` 在对应 api.d.ts 真存在 → 廉价拦"方法名幻觉"类(`defineScenes`/`stopBgm`/…),**比逐个 footgun-doc 可扩展**。注:参数-shape 错(loadSong 扁平数组 / dt 单位)静态难查,仍靠 footgun-doc + 运行时门。
- **(b) per-game gatespec 断言(玩法语义·design agent 产)**:经济/玩法正确性 → design 给这一局产 `assertAfterPlay` 断言(如 `{path:"currency",op:"decreased"}`)+ 保证动作 driver 可达;复用 H 门现成机制。**通用九门不变。**
- **(c) 好玩 = design-output 机检(设计时)**:design 产出须含"反无趣 8 条"可机检 ≥N 条 → 主观调参的客观地板,堵 M4 的"死循环"。
---
## 4. 关键决策与权衡(求拍板)
| # | 决策 | 倾向 + 理由 | 风险/依赖 |
|---|---|---|---|
| D1 | design 模型档 | **升 M3**(策划要设计脑;每局一次、可缓存) | 成本/延迟略增,需量测增益 |
| D2 | BGM 交付 | **mp3/opus 压缩 + 懒加载 fetch**(质量优先,顺带修 studio 水彩) | 依赖 §3.2 整条新音频通道(Phase 2);inline-vs-fetch + 禁fetch 待解 |
| D3 | 进货归属 | **skill 固化范式 + 策划 agent 实例化**(模板不绑玩法) | 无经济原语 → 裸 core.js;靠 (b) per-game 断言兜正确性 |
| D4 | 节奏数值 | **design 给目标值**(根治拍脑袋) | 仍可能不好玩 → modify 回环(Phase 3) |
| D5 | 分期 | **Phase1(① create)先独立交付** | Phase1 不含 modify/资产;BGM 暂静音/ZzFX |
| D6 | API 幻觉 | **加 API-存在静态门**(玩法无关) | 取代打地鼠 footgun-doc;arg-shape 仍靠 doc |
| D7 | "好玩"门 | **design-output 机检 N-of-8**(设计时,非通用门) | N 取值待定;只是地板非保证 |
| D8 | design 输出格式 | **先富化散文 brief(已通),后结构化 schema** | 结构化要改 harness、扰动刚转绿 e2e |
---
## 5. 架构与数据流(纠正后)
```mermaid
flowchart LR
A[用户意图] --> R[render·create/modify 共用入口]
R -->|create| C[classify] --> D[★策划 agent<br/>玩家视角完整设计<br/>进货/节奏目标/音频方案<br/>+可达性自检 +好玩 N-of-8 机检]
D -->|K_ENRICHED 富化brief| G[generate·shell-out<br/>M3 code ReAct]
R -->|modify·Phase3 新建| LB[★载入 base src/<br/>+意图→改动单]
LB --> G
G --> CK[check<br/>+★API-存在静态门]
CK --> V[validate]
V -->|amodel: 跳 scaffold/asset/build| P[play·通用九门<br/>玩法无关<br/>+per-game gatespec 断言]
subgraph H[★②音频/资产·harness 内部 + studio 下发]
AS[mp3/opus 压缩·懒加载<br/>图片随包可达]
end
G -.harness scaffold/build/stage.-> AS
P --> EM[emit / studio]
AS -.studio serve.-> EM
```
---
## 6. Blast Radius / 兼容(纠正后)
| 触及 | 改动 | 兼容 |
|---|---|---|
| `SaaStudioNodes.designNode` + `SaaPrompts` | 升级策划 agent(prompt + 模型档) | factory/gamedef design 保不回归 |
| `sim-business-game-design` skill | 扩进货/库存范式 + 节奏目标 + 反无趣机检项 | 纯增 |
| `tools/amodel-gen/`(check 加 API-存在门;新增 load-base;build 拷音频) | **②③ 主战场(harness 内部)** | raw-brief / 现 create 路保兜底 |
| `audio-music` 插件(+loadTrack/decode/loop) | **插件契约变更** → §6.6 contract-first | 现 ZzFXM 合成路不破 |
| host 音频解锁接文件播放 + studio inject/serve 资产 + build-from-source fetch 例外 | 新运行时音频/资产通道 | studio 现 engineBundle 内联路不破 |
| play-spec/gatespec(design 产 per-game 断言) | 复用 H assertAfterPlay | 通用九门**零改** |
**回归底线**:factory/gamedef 冻结路、studio 现播放路、**通用九门、SAA 83 确定性回归、刚转绿的 amodel e2e**(D8 结构化 schema 会扰动它→慎)全绿。
---
## 7. 风险
- **R1 设计↔实现脱节**(进货环尤甚:无经济原语→裸 core.js,九门验不了经济守恒)→ 缓解:可达性自检(降级为 advisory)+ **(b) per-game 经济断言** + 设计层把经济设计对。
- **R2 ②③ 是净新建、工作量被原稿低估** → 缓解:本版已重基线;分期(Phase2/3 各自独立)。
- **R3 "好玩"无客观门→主观调参死循环** → 缓解:**(c) design-output N-of-8 机检**做地板 + modify 回环(Phase3);A1/A2 不以"纯创始人亲玩"为唯一门。
- **R4 音频通道是兔子洞**(插件契约+host+harness+studio+fetch 五处)→ 缓解:Phase2 独立;先 opus 压缩到 ~KB 级 + 懒加载。
- **R5 D8 结构化 schema 扰动刚转绿 e2e** → 缓解:先散文 brief,结构化择机 + 回归门兜。
---
## 8. 验收(执行版细化,先立锚)
- A1(Phase1 ①·create):干净 re-gen 经营游戏,**自带进货/库存环 + 节奏舒适**(创始人亲玩 **+ N-of-8 机检过**),九门 ≥8/9,**经济 per-game 断言过**。
- A2(Phase3 ③·modify):对**干净 v3-类项目**下「加进货+放慢+BGM」→ 产出改好版本,九门不回归。
- A3(Phase2 ②·音频/资产):studio 该游戏**有水彩 + 有 BGM**(mp3 懒加载),首屏仍 <2MB / P75<3s。
- A4(质量门):API-存在静态门拦掉已知方法名幻觉(defineScenes/stopBgm);通用九门玩法无关、零品类语义。
- A5(不回归):factory/gamedef + 通用九门 + SAA 83 回归 + amodel e2e 全绿。
---
## 9. 分期(纠正后)
- **Phase 1 — ① 策划 agent(仅 create)+ API-存在静态门 + 好玩 N-of-8 机检**:独立可交付,先验"设计层补料能否更好玩"。**不依赖 modify/资产线**(BGM 暂静音或修对 ZzFXM)。
- **Phase 2 — ② 音频/资产线(harness 内部 + studio 下发)**:mp3/opus 压缩 + 懒加载 + audio-music 插件契约扩 + studio serve;顺带修 studio 水彩。
- **Phase 3 — ③ modify 双路(净新建)**:harness load-base + modifyRouter amodel 分支 + 语义(全量重生成带意图 / 真增量);首靶用干净 v3-类项目。
---
## 10. 待办 / Open Items(执行版前定)
- O1【D1】design 模型档量测(stage1 vs M3:设计质量增益 vs 成本)。
- O2【②/Phase2】新音频通道五处的确切范围 + inline-vs-lazy-fetch + `build-from-source` 禁fetch 例外。
- O3【D3】`sim-business-game-design` skill 固化经营子范式(放置/经营/养成)+ 各自资源循环 + 反无趣可机检项清单(定 N)。
- O4【D8】design 输出:散文 brief 维持 vs 结构化 schema(后者改 harness + 扰动 e2e)。
- O5【③/Phase3】amodel modify 语义(全量重生成带意图 / 真增量改 src/)+ harness load-base 工具设计。
- O6【D6】API-存在静态门实现位置(check 阶段)+ 覆盖面(插件方法 + ctx 面);arg-shape 类是否也尝试。
- O7【footgun】第 6 个 footgun(audio 方法名 `stopBgm`→`stop`)+ ZzFXM `SongData` shape:Phase1 若用 ZzFXM 兜底则必补,否则随 D6 静态门一并覆盖方法名类。
- O8【兼容】结构化 design / 新输入是否回归 SAA 83 + amodel e2e(每次执行前跑)。
---
> **下一步**:本评审版收口完毕。**创始人批准 → 写 Phase 1 执行版**(策划 agent create 路 + API 静态门 + 好玩机检的模块/契约/验收/回滚)。Phase 2/3 各自独立执行版。

View File

@ -0,0 +1,164 @@
---
topic: Phase 1 执行版 —— 策划 agent(create)+ API-存在静态门 + 好玩机检
type: execution
phase: 1 of 3
date: 2026-06-23
status: execution-pending(待 §6.8 双评审 + 创始人批准后执行)
origin: docs/agent-specs/2026-06-23-design-agent-audio-asset-line-review.md(review-closed)
owner: Mac 开发执行线
---
# Phase 1 执行版:策划 agent(create)+ API-存在静态门 + 好玩机检
> 承接 review 版(已 §6.8 收口)的 Phase 1。**仅 create 路**;不含 modify(Phase 3)/音频资产线(Phase 2)。
> 执行版 = 模块/文件/契约/步骤/失败路径/验收/回滚;不预写不可验证的代码细节。
---
## 1. 目标与范围边界
**目标**:让 amodel **create** 路的 design 节点变成"会读 skill 的策划 agent",产出含**进货/库存环 + 节奏目标值 + 好玩自检**的设计 → 喂现有 code ReAct;并加两道**玩法无关**的质量门(API-存在静态门 + 好玩机检),把"能玩"抬到"更好玩"。
**范围内**:
- 策划 agent = **tool-using JS agent**(复用 `tools/amodel-gen/` 的 m3.mjs + read_file 工具),**读真 sim skill**(不手抄),输出设计文本 + `gatespec`。
- designNode **amodel 路**改为 shell-out 到该 JS 设计 agent;**gamedef 路 design 不动**(零回归)。
- check 加 **API-存在静态门**(校验插件/ctx 方法真存在)。
- 扩 `sim-business-game-design` skill(进货/库存范式 + 节奏目标值 + §10 标注可机检项 + 定 N)。
- 好玩门 = design 产 per-game `gatespec` 断言(复用现成 `assertAfterPlay`→play_spec→H)+ 设计时 N-of-8 自检。
**范围外(硬边界)**:
- 不碰 **modify** 生命周期(Phase 3 净新建)。
- 不碰**音频/图片资产线**(Phase 2);Phase 1 BGM 维持现状(静音 / 修对 ZzFXM 兜底,见 U6 失败路径)。
- **通用九门零改、绝不加玩法语义**(创始人定)。
- 不碰 **gamedef/factory** design 路;不动 code ReAct harness 的生成逻辑(只改它的输入)。
- 不改 design 输出为结构化 schema(维持富化散文 brief,review D8)。
---
## 2. 前置条件
- mini staging repo 同步(本会话改动已 push,feat/mac-amodel-foundation @88d3a237)。
- NEWAPI_KEY / new-api 网关可达(`docs/内网凭据与端点.md`);M3 可调。
- 基线绿:v3 干净 re-gen 八/九门、SAA 83 确定性回归、amodel e2e(执行前各跑一次留底)。
---
## 3. 模块与文件(repo-relative)
| 模块 | 文件 | 改动 |
|---|---|---|
| 设计 agent harness | `game-runtime/tools/amodel-gen/gen.mjs` | 加 `--mode design`(或新 `design.mjs`):scaffold-free,read_file/list_dir + done(emit 设计文本+gatespec) |
| 设计 agent prompt | `game-runtime/tools/amodel-gen/prompt.mjs` | 加 `buildDesignSystemPrompt()`:薄指令指 read sim skill + 可达性自检 + 好玩 N-of-8 + 产 gatespec |
| API-存在静态门 | `game-runtime/tools/amodel-gen/tools.mjs`(`check`) | 新增:解析游戏源 `<plugin>.<m>(` / `ctx.<m>(` → 比对各 api.d.ts 方法集 → 未知方法报错 |
| SAA design 节点 | `game-cloud/.../saa/SaaStudioNodes.java`(`designNode`) | amodel 路 shell-out 到 JS 设计 agent(复用 `runProcessSplit` 范式);gamedef 路保 Java `DESIGN_SYSTEM` |
| 配置 | `game-cloud/.../saa/AigcExecutorProperties.java` / `SaaStudioGraph.Models` | design amodel 模型档(D1:M3)+ shell-out 开关(回滚用) |
| design 层知识 | `.agents/skills/sim-business-game-design.md` | 扩:进货/库存资源循环范式 + 节奏目标值指引 + §10 标注可机检 N 条 |
| 蒸馏 | `.agents/skills/agentic-amodel-generation.md` + `.agents/README.md` | 记设计-agent 形态 + API 静态门 + 第 6 footgun |
---
## 4. 数据流与依赖
```
brief ─▶ designNode(amodel) ─shell-out─▶ JS 设计 agent
read_file: sim skill + 用到的 api.d.ts
done ─▶ 设计文本 + ```gatespec
◀── 设计文本 ───────────────────────────────────────────────┘
designNode: enriched = brief + 设计文本 (现有逻辑不变)
gatespec → merge play_spec.assertAfterPlay/expectLatch/... (现有逻辑不变)
enriched ─▶ generate(code ReAct, 现有) ─▶ check(+API-存在静态门) ─▶ build ─▶ validate ─▶ play(通用九门 + per-game 断言)
```
**关键**:design→code 的喂法(K_ENRICHED 富化-brief)**已通,不改**;只是把 design 的**产出方**从"Java 单次调用"换成"会读 skill 的 JS agent"(amodel 路)。gatespec→play_spec 的 merge **已存在,不改**——per-game 好玩/经济断言顺这条现成路走。
---
## 5. 接口 / 数据契约
- **设计 agent CLI 契约**(对齐现有 `gen.mjs --mode saa` 范式):`node gen.mjs --mode design --brief "..." [--game-id <id>] --key <NEWAPI_KEY>` → **stdout 末行单行 JSON** `{ ok, designText, gatespec? }`(或落临时文件,designNode 读)。stderr/stdout 分流(沿用 `runProcessSplit`,末行 JSON 不被 stderr 污染)。
- **设计 agent 工具集**:`read_file` / `list_dir`(只读,repo 根)+ `done({designText})`;**无 write/无 shell**(design 不落文件,只产文本)。
- **设计文本契约**:紧凑中文设计稿(覆盖 sim skill §8 配方)+ 末尾一个 ```gatespec JSON 块(`expectLatch` + `assertAfterPlay[]`,经营含经济断言如 `{path:"currency",op:"decreased"}` 或营收 `{path:"score",op:"increased"}`)+ 一段「好玩自检:命中 §10 第 X/Y/… 条」。
- **API-存在静态门契约**:输入 = 游戏源 + 各插件 api.d.ts;规则 = 游戏调的 `plugins.<键>.<方法>` / `ctx.<方法>` 必须在对应 api.d.ts 的 interface 里声明;违者 check FAIL,报「<键>.<方法> 不存在,见 <api.d.ts> 真方法名」。**允许列表**:动态/非插件成员(如自有对象方法)不误伤——只校验已知插件键 + ctx 面。
---
## 6. 实现单元(依赖序)
### U1. 设计 agent harness(JS,tool-using)
- **目标**:`gen.mjs` 加 `--mode design`:不 scaffold、只给 read_file/list_dir/done,跑 ReAct 让 M3 读 sim skill+api.d.ts → 产设计文本+gatespec,stdout 末行 JSON。
- **文件**:`game-runtime/tools/amodel-gen/gen.mjs`、`game-runtime/tools/amodel-gen/prompt.mjs`(`buildDesignSystemPrompt`)。
- **依赖**:无(复用 m3.mjs / TOOLS 子集)。
- **失败路径**:M3 超时/失败 → 末行 JSON `{ok:false}`,designNode 回落 brief-only(沿用现有 degrade)。
- **验收**:本地 `--mode design --brief "经营 brief"` → 末行 JSON 含非空 designText + 合法 gatespec;run-log 显示真 read 了 sim skill。
### U2. 设计 agent 薄 prompt(指读 skill + 可达性 + 好玩 + gatespec)
- **目标**:`buildDesignSystemPrompt()`——薄指令:① read `.agents/skills/sim-business-game-design.md`(别手抄)② 按 §8 配方产设计稿 ③ **可达性自检**(只用 12 插件+boot.ctx 能达成的机制,落不到的剔除/简化,见 skill §9)④ **好玩自检**:过 §10、命中 ≥N 条并写明 ⑤ 末尾产 ```gatespec(expectLatch + assertAfterPlay,经营含经济/营收断言)。
- **文件**:`game-runtime/tools/amodel-gen/prompt.mjs`。**禁反引号**(模板字面量陷阱,node --check 每改必跑)。
- **依赖**:U1。
- **验收**:产的设计含进货/库存环 + 节奏目标值 + 好玩自检 N≥定值 + 合法 gatespec。
### U3. designNode amodel 路 shell-out(gamedef 不动)
- **目标**:`designNode` 加 amodel 分支:`saaSourceMode==amodel` → shell-out JS 设计 agent(复用 `runProcessSplit`),拿 designText 回填 → 现有 enriched/gatespec/play_spec merge **原样复用**;gamedef 路保 `callAndRecord(designModel, DESIGN_SYSTEM, brief)`。
- **文件**:`game-cloud/.../saa/SaaStudioNodes.java`、`AigcExecutorProperties.java`(shell-out 开关 + design amodel 模型档)。
- **依赖**:U1、U2。
- **失败路径**:shell-out 非零/末行 JSON ok:false → 落 `K_DESIGN_TEXT` 占位 + 回落 brief-only(对齐现有 design 失败 degrade);**绝不裸退图**。
- **验收**:amodel 路真走 JS 设计 agent(trace 可见);gamedef 路 design 行为字节不变(回归)。
### U4. API-存在静态门(check,玩法无关)
- **目标**:`check` 解析游戏源(game-logic/core/render)的 `plugins.<键>.<方法>(` 与 `ctx.<方法>(` → 比对各插件 `src/plugins/<name>/api.d.ts` + PluginContext 方法集 → 未知方法 FAIL。
- **文件**:`game-runtime/tools/amodel-gen/tools.mjs`。
- **依赖**:无(独立)。**先以 advisory 上线**(记录不阻断)跑几轮看误报,再转硬 FAIL。
- **失败路径/误伤防护**:只校验**已知插件键**(host-config 注入的 11 + ctx 面),非插件对象方法跳过;插件键→dir 映射坑(`juice`→`particles-juice`)写进映射表。
- **验收**:对 v3 `stopBgm` 与历史 `defineScenes` 报错;对 v3 其余真方法零误报;模板 _template 零误报。
### U5. 扩 sim-business-game-design skill
- **目标**:补 ① 进货/库存**资源循环**范式(进货扣币→库存→售卖→补货;§1/§3)② 节奏目标值指引(spawn 区间/rush 起点参考区间,治"太快")③ §10 标注哪几条**可机检** + 定 **N**(如飘字+音效/score-delta/解锁/经济扣减,N=4)。
- **文件**:`.agents/skills/sim-business-game-design.md`。
- **依赖**:无(U2 的 prompt 指它)。
- **验收**:skill 含进货范式 + 节奏区间 + 可机检 N 条标注;设计 agent 读后产出体现。
### U6. 好玩/经济 per-game 断言接线 + 蒸馏
- **目标**:① 确认 design 产的 gatespec 经济/营收断言经现成 merge 进 play_spec.assertAfterPlay、被 H 门校验(**无新管线**,仅验证链路通)② 蒸馏:`agentic-amodel-generation` 记设计-agent 形态 + API 门 + 第 6 footgun(`stopBgm`→`stop`)。
- **文件**:`.agents/skills/agentic-amodel-generation.md`、`.agents/README.md`;(经济断言驱动可达性靠 U2 设计 + driver,记 open 若 driver 不可达则降 advisory)。
- **依赖**:U2、U3、U4。
- **验收**:re-gen 经营游戏的 play_spec 含经济/营收断言且 H 门据其判定;skill 索引更新。
---
## 7. 边界 / 失败路径汇总
- 设计 agent 失败/超时 → brief-only degrade(不退图)。
- shell-out 末行 JSON 被 stderr 污染 → 沿用 `runProcessSplit` 分流(批 B 已验)。
- API-存在静态门误报 → advisory 先行 + 允许列表 + 键→dir 映射;确无误报再转硬门。
- 经济断言对应动作 driver 不可达 → 该断言降 advisory(记 open O,不阻断;Phase 3 modify/driver 增强再硬化)。
- BGM:Phase 1 不修音频通道;若 design 选 ZzFXM 兜底,必须连带修 `SongData` shape footgun,否则**显式接受静音**(写进验收,不假装有声)。
- 富化 design 扰动刚转绿的 amodel e2e → 每次执行前后跑 e2e + 83 回归,红即回滚(开关)。
---
## 8. 验证方法
- **单元**:U1 本地 `--mode design` 出 JSON;U4 对 v3/_template 跑 check(报 stopBgm、零误报);U2 设计含进货/节奏/好玩。
- **集成(端到端 re-gen v4)**:经现新设计 agent 干净 re-gen 一台经营游戏 → 看产出**有进货环 + 节奏舒适 + 好玩 N-of-8 命中** + 过通用九门 ≥8/9 + 过 API 静态门 + play_spec 经济断言被 H 校验。创始人亲玩判"更好玩"(对比 v3)。
- **回归**:gamedef design 字节不变;SAA 83 确定性回归全绿;amodel e2e 不回归。
## 9. 完成条件
- A1(review):干净 re-gen 经营游戏自带进货/库存 + 节奏舒适(亲玩 + N-of-8 机检过),九门 ≥8/9,经济断言过。
- A4(review):API 静态门拦掉 stopBgm/defineScenes 类、通用九门零品类语义。
- A5(review):gamedef + 通用九门 + SAA 83 + amodel e2e 全绿。
## 10. 回滚
- design shell-out 由 `AigcExecutorProperties` 开关控制 → 关之即回 Java `DESIGN_SYSTEM`(amodel 也回单次调用)。
- API-存在静态门 advisory→hard 两段;hard 出问题降回 advisory。
- skill/prompt 改动纯文本,git revert 即回。
- 各改动 amodel-gated,gamedef/factory/通用九门零触碰 → 回滚面小。
---
## 11. 待执行前定(origin Open Items 收敛)
- O1【D1】design amodel 模型档定 M3?(执行前一次小量测:M3 vs stage1 设计质量)。
- O3【U5】§10 可机检 N 值(建议 N=4)+ 进货范式具体子型。
- O6【U4】API 门覆盖面(插件方法 + ctx 面;arg-shape 类不在本门,留 footgun-doc)。
- O7【U6】Phase 1 BGM:修 ZzFXM SongData footgun 兜底 vs 显式静音——二选一写死验收。
> **下一步**:本执行版过 §6.8 双评审 → 创始人批准 → 按 U1→U6 序执行。

View File

@ -0,0 +1,999 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘境AI · SAA 游戏生成引擎 — 现状 + 演进(策划 agent / 3 阶段)</title>
<style>
:root{
--agent:#2563eb; --agent-bg:#dbeafe; --agent-ink:#1e3a8a;
--harness:#059669; --harness-bg:#d1fae5; --harness-ink:#064e3b;
--fixed:#6b7280; --fixed-bg:#f3f4f6; --fixed-ink:#374151;
--new:#7c3aed; --new-bg:#ede9fe; --new-ink:#4c1d95;
--reuse:#0891b2; --reuse-bg:#cffafe; --reuse-ink:#155e75;
--untouch:#9ca3af; --untouch-bg:#f1f5f9; --untouch-ink:#475569;
--warn:#b45309; --warn-bg:#fff7ed;
--ink:#0f172a; --muted:#64748b; --line:#e2e8f0; --bg:#f8fafc;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font-family:system-ui,-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
line-height:1.6;-webkit-font-smoothing:antialiased}
.wrap{max-width:1120px;margin:0 auto;padding:40px 28px 80px}
header.top{border-bottom:2px solid var(--ink);padding-bottom:20px;margin-bottom:8px}
header.top h1{font-size:29px;margin:0 0 6px;letter-spacing:.5px}
header.top .sub{color:var(--muted);font-size:14px}
header.top .tag{display:inline-block;background:var(--ink);color:#fff;font-size:12px;
padding:2px 10px;border-radius:20px;margin-right:8px;vertical-align:middle}
header.top .tag.new{background:var(--new)}
.toc{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 20px;margin:22px 0 8px;font-size:13.5px}
.toc b{color:var(--ink)}
.toc a{color:var(--agent);text-decoration:none;margin-right:6px}
.toc a:hover{text-decoration:underline}
.toc .sep{color:var(--line);margin:0 6px}
.legend{display:flex;gap:18px;flex-wrap:wrap;align-items:center;
background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 20px;margin:14px 0 8px}
.legend .lg{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600}
.legend .sw{width:18px;height:18px;border-radius:5px;border:2px solid}
.legend .note{color:var(--muted);font-weight:400;font-size:12.5px;margin-left:auto}
section{background:#fff;border:1px solid var(--line);border-radius:16px;
padding:26px 30px 30px;margin:30px 0;box-shadow:0 1px 3px rgba(15,23,42,.04);scroll-margin-top:20px}
section.evo{border-color:var(--new);box-shadow:0 1px 3px rgba(124,58,237,.10)}
section h2{font-size:21px;margin:0 0 4px;display:flex;align-items:baseline;gap:12px}
section h2 .n{color:var(--agent);font-weight:800;font-size:24px}
section.evo h2 .n{color:var(--new)}
section h2 .badge{font-size:11px;font-weight:700;padding:2px 10px;border-radius:20px;
background:var(--new-bg);color:var(--new-ink);align-self:center;letter-spacing:.3px}
section h2 .badge.now{background:var(--fixed-bg);color:var(--fixed-ink)}
section .lead{color:var(--muted);font-size:14px;margin:0 0 18px;border-left:3px solid var(--line);padding-left:12px}
section.evo .lead{border-color:var(--new)}
.divider{display:flex;align-items:center;gap:14px;margin:46px 0 6px}
.divider .bar{flex:1;height:2px;background:linear-gradient(90deg,var(--line),var(--new),var(--line))}
.divider .t{font-size:15px;font-weight:800;color:var(--new-ink);background:var(--new-bg);
padding:6px 16px;border-radius:20px;white-space:nowrap}
svg{display:block;width:100%;height:auto;margin:6px 0}
table{width:100%;border-collapse:collapse;font-size:13.5px;margin-top:8px}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top}
th{background:#f8fafc;font-weight:700;color:var(--ink);border-bottom:2px solid var(--line)}
td .pill,.pill{font-size:11.5px;font-weight:700;padding:2px 9px;border-radius:20px;white-space:nowrap}
.p-a{background:var(--agent-bg);color:var(--agent-ink)}
.p-h{background:var(--harness-bg);color:var(--harness-ink)}
.p-f{background:var(--fixed-bg);color:var(--fixed-ink)}
.p-new{background:var(--new-bg);color:var(--new-ink)}
.p-reuse{background:var(--reuse-bg);color:var(--reuse-ink)}
.p-untouch{background:var(--untouch-bg);color:var(--untouch-ink)}
code{background:#f1f5f9;padding:1px 6px;border-radius:5px;font-size:12.5px;
font-family:"SF Mono",ui-monospace,Menlo,Consolas,monospace}
.callout{background:var(--warn-bg);border:1px solid #fcd34d;border-radius:10px;
padding:12px 16px;margin:14px 0 0;font-size:13px;color:#78350f}
.callout b{color:#7c2d12}
.callout.q{background:#eff6ff;border-color:#bfdbfe;color:#1e40af}
.callout.q b{color:#1e3a8a}
.foot{color:var(--muted);font-size:12.5px;text-align:center;margin-top:40px;padding-top:18px;border-top:1px solid var(--line)}
</style>
</head>
<body>
<div class="wrap">
<header class="top">
<h1>绘境AI · SAA 游戏生成引擎 — 现状 + 演进(策划 agent / 3 阶段)</h1>
<div class="sub">
<span class="tag">现状 SoT</span>
<span class="tag new">新设计 · 求审</span>
上半 = 当前真实形态(代码核实);下半 = 「让生成更好玩」的新设计方向:策划 agent + 音频/资产线 + modify 双路,分 3 阶段演进。
</div>
</header>
<div class="toc">
<b>导读:</b>
<span>现状</span>
<a href="#s1">①全景</a><a href="#s2">②SAA 拓扑</a><a href="#s3">③generate 内核</a><a href="#s4">④三层代码</a><a href="#s5">⑤九门</a><a href="#s6">⑥边界总表</a>
<span class="sep">·</span>
<b style="color:var(--new-ink)">新设计</b>
<a href="#s7" style="color:var(--new)">⑦核心论点</a><a href="#s8" style="color:var(--new)">⑧三阶段演进图</a><a href="#s9" style="color:var(--new)">⑨P1 策划 agent</a><a href="#s10" style="color:var(--new)">⑩质量门三类</a><a href="#s11" style="color:var(--new)">⑪P2 音频/资产</a><a href="#s12" style="color:var(--new)">⑫P3 modify 双路</a>
</div>
<div class="legend">
<div class="lg"><span class="sw" style="background:var(--agent-bg);border-color:var(--agent)"></span>Agent · LLM 调用</div>
<div class="lg"><span class="sw" style="background:var(--harness-bg);border-color:var(--harness)"></span>Harness · 确定性</div>
<div class="lg"><span class="sw" style="background:var(--fixed-bg);border-color:var(--fixed)"></span>固定 plumbing / 契约</div>
<div class="lg"><span class="sw" style="background:var(--new-bg);border-color:var(--new)"></span>净新建(新设计)</div>
<div class="lg"><span class="sw" style="background:var(--reuse-bg);border-color:var(--reuse)"></span>复用/升级(大半已通)</div>
<div class="lg"><span class="sw" style="background:var(--untouch-bg);border-color:var(--untouch)"></span>零触碰</div>
<div class="note">SAA = Spring AI Alibaba 裸 StateGraph · 进程内编排 · new-api 网关 100.64.0.8:3000</div>
</div>
<!-- ============ 现状部分 ============ -->
<!-- ① 全景 -->
<section id="s1">
<h2><span class="n">①</span> 全景:一句话 → 一款可入库的游戏 <span class="badge now">现状</span></h2>
<p class="lead">最粗一层。用户一句 brief 进来,SaaGraphDispatcher 进程内提交 SAA 图,图跑完四个阶段(理解&amp;设计 / 生成 / 验收 / 出厂),终态回调入库,studio 前端消费。</p>
<svg viewBox="0 0 1060 360" role="img" aria-label="全景流程图">
<defs>
<marker id="ar1" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L8,3 L0,6 Z" fill="#334155"/>
</marker>
</defs>
<!-- 用户输入 -->
<rect x="20" y="150" width="150" height="64" rx="12" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="95" y="178" text-anchor="middle" font-size="14" font-weight="700" fill="var(--fixed-ink)">用户一句话</text>
<text x="95" y="198" text-anchor="middle" font-size="11.5" fill="var(--muted)">brief(玩法描述)</text>
<!-- dispatcher -->
<rect x="200" y="150" width="160" height="64" rx="12" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="280" y="174" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--harness-ink)">SaaGraphDispatcher</text>
<text x="280" y="193" text-anchor="middle" font-size="11" fill="var(--harness-ink)">提交进程内 SAA 图</text>
<text x="280" y="207" text-anchor="middle" font-size="10.5" fill="var(--muted)">job 留 RUNNING 等回调</text>
<!-- SAA 图四阶段 大框 -->
<rect x="392" y="44" width="430" height="276" rx="16" fill="#fbfdff" stroke="var(--agent)" stroke-width="2" stroke-dasharray="2 0"/>
<text x="607" y="70" text-anchor="middle" font-size="14" font-weight="800" fill="var(--agent-ink)">SAA StateGraph(进程内编排 · 见图②)</text>
<!-- 阶段1 理解&设计 -->
<rect x="410" y="90" width="180" height="70" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="1.6"/>
<text x="500" y="114" text-anchor="middle" font-size="13" font-weight="700" fill="var(--agent-ink)">① 理解 &amp; 设计</text>
<text x="500" y="132" text-anchor="middle" font-size="10.5" fill="var(--agent-ink)">classify · design</text>
<text x="500" y="148" text-anchor="middle" font-size="10" fill="var(--muted)">富化 brief → 设计稿 + gatespec</text>
<!-- 阶段2 生成 -->
<rect x="624" y="90" width="180" height="70" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="1.6"/>
<text x="714" y="114" text-anchor="middle" font-size="13" font-weight="700" fill="var(--agent-ink)">② 生成</text>
<text x="714" y="132" text-anchor="middle" font-size="10.5" fill="var(--agent-ink)">generate(A-model · M3)</text>
<text x="714" y="148" text-anchor="middle" font-size="10" fill="var(--muted)">agent 写 L3 游戏代码</text>
<!-- 阶段3 验收 -->
<rect x="410" y="208" width="180" height="74" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="500" y="232" text-anchor="middle" font-size="13" font-weight="700" fill="var(--harness-ink)">③ 验收</text>
<text x="500" y="250" text-anchor="middle" font-size="10.5" fill="var(--harness-ink)">validate · play(九门)</text>
<text x="500" y="266" text-anchor="middle" font-size="10" fill="var(--muted)">+ player 玩家评判(Agent)</text>
<!-- 阶段4 出厂 -->
<rect x="624" y="208" width="180" height="74" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="714" y="232" text-anchor="middle" font-size="13" font-weight="700" fill="var(--harness-ink)">④ 出厂</text>
<text x="714" y="250" text-anchor="middle" font-size="10.5" fill="var(--harness-ink)">emit / giveup</text>
<text x="714" y="266" text-anchor="middle" font-size="10" fill="var(--muted)">断 __GameBundle → 终态</text>
<!-- 内部阶段箭头 -->
<line x1="590" y1="125" x2="622" y2="125" stroke="#334155" stroke-width="1.6" marker-end="url(#ar1)"/>
<line x1="714" y1="160" x2="714" y2="206" stroke="#334155" stroke-width="1.6" marker-end="url(#ar1)"/>
<line x1="624" y1="245" x2="592" y2="245" stroke="#334155" stroke-width="1.6" marker-end="url(#ar1)" transform="translate(0,0)"/>
<text x="607" y="240" text-anchor="middle" font-size="9.5" fill="var(--muted)"></text>
<line x1="500" y1="208" x2="500" y2="162" stroke="#94a3b8" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#ar1)"/>
<text x="468" y="188" text-anchor="middle" font-size="9" fill="var(--muted)">救场回环</text>
<!-- callback -->
<rect x="844" y="90" width="196" height="70" rx="12" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="942" y="114" text-anchor="middle" font-size="13" font-weight="700" fill="var(--harness-ink)">handleCallback</text>
<text x="942" y="132" text-anchor="middle" font-size="10.5" fill="var(--harness-ink)">建版本→组包→落包→回填</text>
<text x="942" y="148" text-anchor="middle" font-size="10" fill="var(--muted)">DifyCallbackService</text>
<!-- 入库 -->
<rect x="844" y="186" width="196" height="58" rx="12" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="942" y="210" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">入库</text>
<text x="942" y="228" text-anchor="middle" font-size="10.5" fill="var(--muted)">game_source_project · engineBundle</text>
<!-- studio -->
<rect x="844" y="268" width="196" height="58" rx="12" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="942" y="292" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">studio 前端</text>
<text x="942" y="310" text-anchor="middle" font-size="10.5" fill="var(--muted)">玩家发现 → 即点即玩</text>
<!-- 主箭头 -->
<line x1="170" y1="182" x2="198" y2="182" stroke="#334155" stroke-width="2" marker-end="url(#ar1)"/>
<line x1="360" y1="182" x2="408" y2="125" stroke="#334155" stroke-width="2" marker-end="url(#ar1)"/>
<line x1="804" y1="125" x2="842" y2="125" stroke="#334155" stroke-width="2" marker-end="url(#ar1)"/>
<line x1="942" y1="160" x2="942" y2="184" stroke="#334155" stroke-width="2" marker-end="url(#ar1)"/>
<line x1="942" y1="244" x2="942" y2="266" stroke="#334155" stroke-width="2" marker-end="url(#ar1)"/>
</svg>
</section>
<!-- ② SAA StateGraph 全拓扑 -->
<section id="s2">
<h2><span class="n">②</span> SAA StateGraph 全拓扑(节点 + 边 + 救场回环) <span class="badge now">现状</span></h2>
<p class="lead">权威拓扑(来自 <code>SaaStudioGraph.assemble</code>)。蓝=Agent 节点(吃 LLM),绿=Harness 节点(确定性)。<b>amodel 主线:<code>validate</code>-ok 直接短路到 <code>play</code>(跳过 scaffold/asset/build)</b>(代码核实 SaaStudioGraph:502);factory/gamedef 旧路才走 scaffold→asset→build 链。</p>
<svg viewBox="0 0 1060 1000" role="img" aria-label="SAA StateGraph 拓扑">
<defs>
<marker id="ar2" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
<marker id="ar2r" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#b45309"/>
</marker>
</defs>
<!-- ===== 主脊(中列 x:255..495, center 375) ===== -->
<!-- START -->
<ellipse cx="375" cy="34" rx="40" ry="18" fill="#0f172a"/>
<text x="375" y="39" text-anchor="middle" font-size="12.5" font-weight="700" fill="#fff">START</text>
<!-- render H -->
<rect x="255" y="66" width="240" height="50" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="375" y="86" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--harness-ink)">render</text>
<text x="375" y="103" text-anchor="middle" font-size="10.5" fill="var(--muted)">入参规整 · brief 默认值齐全 · 图入口</text>
<!-- classify A -->
<rect x="255" y="148" width="240" height="50" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2"/>
<text x="375" y="168" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--agent-ink)">classify <tspan font-size="10" fill="var(--agent)">LLM·classify</tspan></text>
<text x="375" y="185" text-anchor="middle" font-size="10.5" fill="var(--muted)">玩法品类分类</text>
<!-- design A -->
<rect x="255" y="230" width="240" height="50" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2"/>
<text x="375" y="250" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--agent-ink)">design <tspan font-size="10" fill="var(--agent)">LLM·design</tspan></text>
<text x="375" y="267" text-anchor="middle" font-size="10" fill="var(--muted)">产 K_ENRICHED 富化-brief + gatespec</text>
<!-- generate A -->
<rect x="255" y="312" width="240" height="54" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2.4"/>
<text x="375" y="333" text-anchor="middle" font-size="13.5" font-weight="800" fill="var(--agent-ink)">generate <tspan font-size="10" fill="var(--agent)">A-model · MiniMax-M3</tspan></text>
<text x="375" y="351" text-anchor="middle" font-size="10.5" fill="var(--muted)">agentic 写 L3 游戏代码(见图③)· shell-out gen.mjs</text>
<!-- validate H -->
<rect x="255" y="398" width="240" height="54" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="375" y="419" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--harness-ink)">validate</text>
<text x="375" y="437" text-anchor="middle" font-size="10.5" fill="var(--muted)">查 staged bundle + __GameBundle · 写 play-spec.json</text>
<!-- play H -->
<rect x="255" y="556" width="240" height="54" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2.4"/>
<text x="375" y="577" text-anchor="middle" font-size="13.5" font-weight="800" fill="var(--harness-ink)">play <tspan font-size="10" fill="var(--harness)">九门 harness(见图⑤)</tspan></text>
<text x="375" y="595" text-anchor="middle" font-size="10.5" fill="var(--muted)">serve + Chrome CDP + play.cdp.cjs → verdict.json</text>
<!-- player A / nreview A -->
<rect x="150" y="660" width="200" height="56" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2"/>
<text x="250" y="681" text-anchor="middle" font-size="13" font-weight="700" fill="var(--agent-ink)">player <tspan font-size="9.5" fill="var(--agent)">LLM×2</tspan></text>
<text x="250" y="698" text-anchor="middle" font-size="10" fill="var(--muted)">文本位 + 视觉位(M3) 玩家评判</text>
<text x="250" y="711" text-anchor="middle" font-size="9.5" fill="var(--muted)">软门 · 有体验问题→repair</text>
<rect x="400" y="660" width="200" height="56" rx="10" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2"/>
<text x="500" y="681" text-anchor="middle" font-size="13" font-weight="700" fill="var(--agent-ink)">nreview <tspan font-size="9.5" fill="var(--agent)">LLM·narrative</tspan></text>
<text x="500" y="698" text-anchor="middle" font-size="10" fill="var(--muted)">叙事类游戏叙事审查</text>
<!-- emit H -->
<rect x="255" y="772" width="240" height="52" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="375" y="792" text-anchor="middle" font-size="13.5" font-weight="700" fill="var(--harness-ink)">emit</text>
<text x="375" y="809" text-anchor="middle" font-size="10.5" fill="var(--muted)">读 bundle.iife.js → 断 __GameBundle → status=succeeded</text>
<!-- END -->
<ellipse cx="375" cy="872" rx="42" ry="18" fill="#0f172a"/>
<text x="375" y="877" text-anchor="middle" font-size="12.5" font-weight="700" fill="#fff">END</text>
<!-- ===== 右支:factory/gamedef 旧路 scaffold→asset→build ===== -->
<rect x="630" y="392" width="170" height="40" rx="9" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="715" y="411" text-anchor="middle" font-size="12" font-weight="700" fill="var(--harness-ink)">scaffold</text>
<text x="715" y="425" text-anchor="middle" font-size="9.5" fill="var(--muted)">factory/gamedef 旧路</text>
<rect x="630" y="444" width="170" height="38" rx="9" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="715" y="468" text-anchor="middle" font-size="12" font-weight="700" fill="var(--harness-ink)">asset</text>
<rect x="630" y="494" width="170" height="40" rx="9" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="715" y="513" text-anchor="middle" font-size="12" font-weight="700" fill="var(--harness-ink)">build</text>
<text x="715" y="527" text-anchor="middle" font-size="9.5" fill="var(--muted)">esbuild</text>
<text x="715" y="552" text-anchor="middle" font-size="9" fill="var(--warn)">⚠ amodel 路对此三节点不可达</text>
<!-- ===== 右列:救场回环 repair / escalate / giveup ===== -->
<rect x="850" y="312" width="170" height="46" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.8"/>
<text x="935" y="332" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">repair</text>
<text x="935" y="348" text-anchor="middle" font-size="9.5" fill="var(--muted)">带反馈 → generate</text>
<rect x="850" y="372" width="170" height="46" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.8"/>
<text x="935" y="392" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">escalate</text>
<text x="935" y="408" text-anchor="middle" font-size="9.5" fill="var(--muted)">升 stage2 强档 → generate</text>
<rect x="850" y="770" width="170" height="48" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.8"/>
<text x="935" y="790" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">giveup</text>
<text x="935" y="806" text-anchor="middle" font-size="9.5" fill="var(--muted)">超 maxRepairs(5) → failed</text>
<!-- ===== modify 支(render 的另一入口) ===== -->
<rect x="40" y="148" width="170" height="50" rx="10" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.6"/>
<text x="125" y="168" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">modify</text>
<text x="125" y="185" text-anchor="middle" font-size="9.5" fill="var(--muted)">改既有游戏 · gamedef 专用</text>
<!-- ===== 主脊直边 ===== -->
<line x1="375" y1="52" x2="375" y2="64" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<line x1="375" y1="116" x2="375" y2="146" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<text x="392" y="135" font-size="9.5" fill="var(--muted)">create</text>
<line x1="375" y1="198" x2="375" y2="228" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<line x1="375" y1="280" x2="375" y2="310" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<text x="392" y="300" font-size="9" fill="var(--muted)">K_ENRICHED</text>
<line x1="375" y1="366" x2="375" y2="396" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<!-- validate → play (amodel 短路) -->
<line x1="375" y1="452" x2="375" y2="554" stroke="#475569" stroke-width="2.6" marker-end="url(#ar2)"/>
<text x="390" y="505" font-size="10.5" font-weight="800" fill="var(--harness-ink)">amodel 短路</text>
<text x="390" y="519" font-size="9" fill="var(--muted)">validate-ok → play</text>
<text x="390" y="531" font-size="9" fill="var(--muted)">跳过 scaffold 链</text>
<!-- play → player / nreview -->
<line x1="340" y1="610" x2="270" y2="658" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<text x="262" y="640" font-size="9.5" fill="var(--muted)">九门 pass</text>
<line x1="410" y1="610" x2="480" y2="658" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<text x="500" y="640" font-size="9.5" fill="var(--muted)">叙事类</text>
<!-- player/nreview → emit -->
<line x1="250" y1="716" x2="330" y2="770" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<text x="232" y="752" font-size="9.5" fill="var(--muted)">无问题/轮尽</text>
<line x1="500" y1="716" x2="420" y2="770" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<!-- emit → END -->
<line x1="375" y1="824" x2="375" y2="852" stroke="#475569" stroke-width="2" marker-end="url(#ar2)"/>
<!-- factory 旧路边 -->
<line x1="495" y1="430" x2="628" y2="412" stroke="#94a3b8" stroke-width="1.6" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
<text x="520" y="400" font-size="9" fill="var(--muted)">factory/gamedef · ok</text>
<line x1="715" y1="432" x2="715" y2="442" stroke="#94a3b8" stroke-width="1.6" marker-end="url(#ar2)"/>
<line x1="715" y1="482" x2="715" y2="492" stroke="#94a3b8" stroke-width="1.6" marker-end="url(#ar2)"/>
<line x1="630" y1="528" x2="496" y2="572" stroke="#94a3b8" stroke-width="1.6" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
<text x="556" y="556" font-size="9" fill="var(--muted)">build ok → play</text>
<!-- 救场回环边(橙) validate/build/play/player/nreview → repair/escalate → generate -->
<line x1="495" y1="338" x2="848" y2="335" stroke="#b45309" stroke-width="1.4" stroke-dasharray="5 3"/>
<path d="M495,415 C620,360 760,350 848,348" fill="none" stroke="#b45309" stroke-width="1.4" stroke-dasharray="5 3"/>
<path d="M495,585 C720,560 850,470 850,420" fill="none" stroke="#b45309" stroke-width="1.4" stroke-dasharray="5 3" marker-end="url(#ar2r)"/>
<text x="700" y="600" font-size="9.5" fill="#b45309">validate/build/play/player/nreview 失败 → 救场</text>
<!-- repair/escalate → generate (回 generate) -->
<path d="M850,340 C620,300 560,320 497,332" fill="none" stroke="#b45309" stroke-width="1.6" marker-end="url(#ar2r)"/>
<text x="600" y="300" font-size="9.5" font-weight="700" fill="#b45309">repair / escalate → generate</text>
<!-- giveup → END -->
<path d="M850,800 C700,840 560,872 419,872" fill="none" stroke="#b45309" stroke-width="1.4" stroke-dasharray="5 3" marker-end="url(#ar2r)"/>
<line x1="600" y1="700" x2="850" y2="790" stroke="#b45309" stroke-width="1.2" stroke-dasharray="5 3"/>
<!-- render → modify ; modify → generate/validate/END -->
<line x1="255" y1="100" x2="160" y2="146" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
<text x="150" y="120" font-size="9" fill="var(--muted)">modify 入口</text>
<path d="M125,198 C125,300 200,330 253,338" fill="none" stroke="#94a3b8" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#ar2)"/>
<text x="80" y="270" font-size="9" fill="var(--muted)">→ generate/validate</text>
</svg>
<p class="lead" style="margin-top:14px;border-color:var(--agent)">
<b>救场预算:</b> maxRepairs=5 · stage2ExtraRepairs=3 · maxPlayerRounds=1 · recursionLimit=120。任何失败门经 repair(带反馈)或 escalate(升强档)回到 generate 重试,耗尽 → giveup。
</p>
<div class="callout">
<b>现状修正点(双评审 + 代码核实):</b>amodel 路 <code>validate-ok</code> 直奔 <code>play</code>,<b>跳过 scaffold/asset/build</b>(SaaStudioGraph:502-503)。原图把 asset/scaffold 画进 amodel 主线是误框 —— 它们只在 factory/gamedef 旧路可达;<code>modify</code> 节点同理是 gamedef/factory 专用(regenerate-module→generate / deterministic→guard fail-loud),<b>无 amodel 分支</b>。
</div>
</section>
<!-- ③ generate 放大 = A-model agent 循环 -->
<section id="s3">
<h2><span class="n">③</span> generate 放大 = A-model agent 循环(「agent 里面做什么」) <span class="badge now">现状</span></h2>
<p class="lead">generate 节点 shell-out 到 <code>gen.mjs</code>。这是唯一「真 agent」的地方:M3 在一个 ReAct 循环里调 6 个工具,自己读手册、写代码、自检,绿了才 done。harness 终态产<b>单个自包含 <code>bundle.iife.js</code></b>。<b>check / build / done 门都是 Harness(确定性),M3 只负责思考与写 L3 代码。</b></p>
<svg viewBox="0 0 1060 470" role="img" aria-label="A-model agent 循环">
<defs>
<marker id="ar3" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
</defs>
<!-- scaffold 起点 -->
<rect x="30" y="200" width="170" height="64" rx="11" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="115" y="226" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">scaffold amgen-&lt;id&gt;</text>
<text x="115" y="244" text-anchor="middle" font-size="10" fill="var(--muted)">clone _template(含 L1+L2)</text>
<!-- M3 中心 -->
<circle cx="430" cy="232" r="78" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2.6"/>
<text x="430" y="222" text-anchor="middle" font-size="16" font-weight="800" fill="var(--agent-ink)">MiniMax-M3</text>
<text x="430" y="242" text-anchor="middle" font-size="11" fill="var(--agent-ink)">ReAct 循环</text>
<text x="430" y="258" text-anchor="middle" font-size="9.5" fill="var(--muted)">thinking 关 · maxSteps 70</text>
<!-- 6 工具 -->
<g font-size="11" font-weight="600">
<rect x="640" y="40" width="150" height="40" rx="8" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="1.6"/>
<text x="715" y="65" text-anchor="middle" fill="var(--fixed-ink)">read_file <tspan font-size="9" fill="var(--muted)">读手册/范例</tspan></text>
<rect x="640" y="90" width="150" height="40" rx="8" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="1.6"/>
<text x="715" y="115" text-anchor="middle" fill="var(--fixed-ink)">list_dir</text>
<rect x="640" y="140" width="150" height="40" rx="8" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="1.6"/>
<text x="715" y="165" text-anchor="middle" fill="var(--fixed-ink)">write_file <tspan font-size="9" fill="var(--muted)">整文件覆盖</tspan></text>
<rect x="640" y="225" width="150" height="44" rx="8" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.8"/>
<text x="715" y="244" text-anchor="middle" fill="var(--harness-ink)">check</text>
<text x="715" y="260" text-anchor="middle" font-size="9" fill="var(--muted)">node --check + 五法/红线 lint</text>
<rect x="640" y="280" width="150" height="44" rx="8" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="1.8"/>
<text x="715" y="299" text-anchor="middle" fill="var(--harness-ink)">build</text>
<text x="715" y="315" text-anchor="middle" font-size="9" fill="var(--muted)">esbuild(秒级)</text>
</g>
<!-- done 门 -->
<rect x="640" y="350" width="320" height="86" rx="11" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2.4"/>
<text x="800" y="374" text-anchor="middle" font-size="13" font-weight="800" fill="var(--harness-ink)">done 门(Harness 复核)</text>
<text x="800" y="394" text-anchor="middle" font-size="10.5" fill="var(--harness-ink)">复核 check + build 必须都绿</text>
<text x="800" y="410" text-anchor="middle" font-size="10.5" fill="var(--harness-ink)">SAA:stage → gameId 目录 + 同进程 smoke-boot</text>
<text x="800" y="426" text-anchor="middle" font-size="10" fill="var(--muted)">绿才接受 done,否则 M3 继续改</text>
<!-- 产物 -->
<rect x="30" y="370" width="190" height="60" rx="11" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="125" y="395" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">staged bundle.iife.js</text>
<text x="125" y="413" text-anchor="middle" font-size="10" fill="var(--muted)">→ validate 节点接手</text>
<!-- 箭头 -->
<line x1="200" y1="232" x2="350" y2="232" stroke="#475569" stroke-width="2" marker-end="url(#ar3)"/>
<!-- M3 ↔ tools (双向) -->
<line x1="508" y1="205" x2="638" y2="120" stroke="var(--agent)" stroke-width="1.5" marker-end="url(#ar3)"/>
<line x1="508" y1="232" x2="638" y2="160" stroke="var(--agent)" stroke-width="1.5" marker-end="url(#ar3)"/>
<line x1="508" y1="250" x2="638" y2="247" stroke="var(--agent)" stroke-width="1.5" marker-end="url(#ar3)"/>
<line x1="508" y1="265" x2="638" y2="300" stroke="var(--agent)" stroke-width="1.5" marker-end="url(#ar3)"/>
<!-- tools 反馈回 M3 -->
<path d="M638,70 C560,90 540,150 508,200" fill="none" stroke="#94a3b8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#ar3)"/>
<text x="548" y="120" font-size="9" fill="var(--muted)">结果回喂</text>
<!-- M3 → done -->
<path d="M470,300 C520,360 580,388 638,392" fill="none" stroke="var(--agent)" stroke-width="1.6" marker-end="url(#ar3)"/>
<text x="505" y="360" font-size="9.5" fill="var(--agent-ink)">声明 done</text>
<!-- done → 产物 -->
<path d="M640,410 C420,452 300,420 222,402" fill="none" stroke="#475569" stroke-width="2" marker-end="url(#ar3)"/>
<!-- 边界说明 -->
<rect x="250" y="350" width="360" height="70" rx="10" fill="#fff" stroke="var(--line)" stroke-width="1.4" stroke-dasharray="3 3"/>
<text x="430" y="372" text-anchor="middle" font-size="11" font-weight="700" fill="var(--ink)">边界:M3 只写 L3(game-logic.js …)</text>
<text x="430" y="390" text-anchor="middle" font-size="10" fill="var(--muted)">L1 plumbing write 会被拒 · L2 插件只用不写</text>
<text x="430" y="406" text-anchor="middle" font-size="10" fill="var(--muted)">单局墙钟上限 20min</text>
</svg>
</section>
<!-- ④ 3 层代码架构 -->
<section id="s4">
<h2><span class="n">④</span> 三层代码架构(谁写 / 谁不写 — 这是 agent 的硬边界) <span class="badge now">现状</span></h2>
<p class="lead">每个游戏 = 三层。L1 固定 plumbing 与 L2 能力库由仓库提供、agent 不碰;agent 只写 L3 游戏本体。入口契约把嵌套摊平成 <code>createGame({plugins,bundle,viewport})</code>,L3 见不到宿主细节。</p>
<svg viewBox="0 0 1060 430" role="img" aria-label="三层代码架构">
<!-- L1 -->
<rect x="30" y="30" width="1000" height="104" rx="12" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="52" y="58" font-size="15" font-weight="800" fill="var(--fixed-ink)">L1 · 固定 plumbing</text>
<text x="52" y="77" font-size="11.5" fill="var(--muted)">agent 不碰(write 被拒)</text>
<g font-size="11.5" font-weight="600" fill="var(--fixed-ink)">
<rect x="290" y="46" width="160" height="34" rx="7" fill="#fff" stroke="var(--fixed)"/><text x="370" y="68" text-anchor="middle">index.html</text>
<rect x="466" y="46" width="170" height="34" rx="7" fill="#fff" stroke="var(--fixed)"/><text x="551" y="68" text-anchor="middle">entry-bundle.js</text>
<rect x="652" y="46" width="180" height="34" rx="7" fill="#fff" stroke="var(--fixed)"/><text x="742" y="68" text-anchor="middle">game.js(薄 wrapper)</text>
<rect x="848" y="46" width="166" height="34" rx="7" fill="#fff" stroke="var(--fixed)"/><text x="931" y="68" text-anchor="middle">host-config.js</text>
</g>
<text x="290" y="102" font-size="10.5" fill="var(--muted)">host-config.js:注入全 11 插件 + viewport 390×844 + 工厂 wiring</text>
<text x="290" y="119" font-size="10.5" fill="var(--muted)">game.js:吃 opts.runtime.{plugins,bundle,viewport} → 调 createGame(扁平)</text>
<!-- L2 -->
<rect x="30" y="150" width="1000" height="104" rx="12" fill="#ecfdf5" stroke="var(--harness)" stroke-width="2"/>
<text x="52" y="178" font-size="15" font-weight="800" fill="var(--harness-ink)">L2 · 标准能力库(11 插件)</text>
<text x="52" y="197" font-size="11.5" fill="var(--muted)">agent 用、不写</text>
<g font-size="10.5" font-weight="600" fill="var(--harness-ink)">
<rect x="290" y="164" width="118" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="349" y="184" text-anchor="middle">sceneFsm</text>
<rect x="416" y="164" width="118" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="475" y="184" text-anchor="middle">sessionScore</text>
<rect x="542" y="164" width="100" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="592" y="184" text-anchor="middle">hudUi</text>
<rect x="650" y="164" width="140" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="720" y="184" text-anchor="middle">timerScheduler</text>
<rect x="798" y="164" width="90" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="843" y="184" text-anchor="middle">save</text>
<rect x="896" y="164" width="118" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="955" y="184" text-anchor="middle">gamefeel</text>
<rect x="290" y="202" width="100" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="340" y="222" text-anchor="middle">juice</text>
<rect x="398" y="202" width="118" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="457" y="222" text-anchor="middle">palettePost</text>
<rect x="524" y="202" width="118" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="583" y="222" text-anchor="middle">audioMusic</text>
<rect x="650" y="202" width="108" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="704" y="222" text-anchor="middle">collision</text>
<rect x="766" y="202" width="100" height="30" rx="6" fill="#fff" stroke="var(--harness)"/><text x="816" y="222" text-anchor="middle">physics</text>
</g>
<!-- L3 -->
<rect x="30" y="270" width="1000" height="128" rx="12" fill="var(--agent-bg)" stroke="var(--agent)" stroke-width="2.6"/>
<text x="52" y="298" font-size="15" font-weight="800" fill="var(--agent-ink)">L3 · 游戏本体</text>
<text x="52" y="317" font-size="11.5" fill="var(--agent)">agent(M3)真正写的</text>
<rect x="290" y="286" width="300" height="96" rx="9" fill="#fff" stroke="var(--agent)" stroke-width="1.8"/>
<text x="440" y="308" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--agent-ink)">game-logic.js(必写)</text>
<text x="440" y="328" text-anchor="middle" font-size="10.5" fill="var(--agent-ink)">export createGame({plugins,bundle,viewport})</text>
<text x="440" y="345" text-anchor="middle" font-size="10" fill="var(--muted)">五法 init/update/render/destroy</text>
<text x="440" y="361" text-anchor="middle" font-size="10" fill="var(--muted)">+ 必须 _forensicsView()(可测性红线)</text>
<text x="440" y="376" text-anchor="middle" font-size="9.5" fill="var(--muted)">update 内必须 bundle.tick(dt) · 零引擎 import</text>
<rect x="610" y="286" width="404" height="96" rx="9" fill="#fff" stroke="var(--agent)" stroke-width="1.4" stroke-dasharray="4 3"/>
<text x="812" y="308" text-anchor="middle" font-size="12" font-weight="700" fill="var(--agent-ink)">可选拆分(按需)</text>
<text x="812" y="330" text-anchor="middle" font-size="11" fill="var(--agent-ink)">core.js · render.js · balance.js · assets.js</text>
<text x="812" y="352" text-anchor="middle" font-size="10" fill="var(--muted)">极小游戏可全内联进 game-logic.js</text>
<text x="812" y="370" text-anchor="middle" font-size="10" fill="var(--muted)">受控面 ctx 仅在 init(boot).boot.ctx</text>
</svg>
</section>
<!-- ⑤ play 九门 harness -->
<section id="s5">
<h2><span class="n">⑤</span> play 放大 = 九门验收 harness(全 Harness,无 LLM) <span class="badge now">现状</span></h2>
<p class="lead">play 节点跑 <code>serve-and-play.sh</code>:静态 serve + Chrome headless(CDP)+ <code>play.cdp.cjs</code>。boot-game-host 把 bundle 启动、注入插件、桥接输入、挂 recHook,然后逐门取证写 verdict.json。<b>九门 = 运行时 + 玩法无关,只验「能玩」(boot/活性/输入/有进展),故意不懂任何品类语义</b>—— 这是它的价值,新设计绝不破坏它。<b>未驱动时 E_live / H 降为 advisory;driven(spec 有 driver/inputs)时九门全硬。</b></p>
<svg viewBox="0 0 1060 360" role="img" aria-label="九门验收 harness">
<defs>
<marker id="ar5" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
</defs>
<rect x="30" y="40" width="180" height="80" rx="11" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="120" y="68" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">serve-and-play.sh</text>
<text x="120" y="88" text-anchor="middle" font-size="10" fill="var(--muted)">静态 serve :4320</text>
<text x="120" y="104" text-anchor="middle" font-size="10" fill="var(--muted)">Chrome headless CDP :9222</text>
<rect x="250" y="40" width="200" height="80" rx="11" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="350" y="64" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">boot-game-host.js</text>
<text x="350" y="84" text-anchor="middle" font-size="9.8" fill="var(--muted)">boot bundle · 注入 11 插件</text>
<text x="350" y="99" text-anchor="middle" font-size="9.8" fill="var(--muted)">input bridge(canvas→game)</text>
<text x="350" y="113" text-anchor="middle" font-size="9.8" fill="var(--muted)">recHook → __engineCalls</text>
<rect x="490" y="40" width="180" height="80" rx="11" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="580" y="68" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--harness-ink)">play.cdp.cjs</text>
<text x="580" y="88" text-anchor="middle" font-size="10" fill="var(--muted)">逐门取证 + 驱动</text>
<text x="580" y="104" text-anchor="middle" font-size="10" fill="var(--muted)">→ verdict.json</text>
<line x1="210" y1="80" x2="248" y2="80" stroke="#475569" stroke-width="2" marker-end="url(#ar5)"/>
<line x1="450" y1="80" x2="488" y2="80" stroke="#475569" stroke-width="2" marker-end="url(#ar5)"/>
<!-- 九门 -->
<g font-size="10.5">
<rect x="40" y="170" width="150" height="48" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="115" y="190" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">A_boot</text><text x="115" y="206" text-anchor="middle" font-size="9.5" fill="var(--muted)">装载成功</text>
<rect x="200" y="170" width="150" height="48" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="275" y="190" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">B_uncaught</text><text x="275" y="206" text-anchor="middle" font-size="9.5" fill="var(--muted)">无未捕获异常</text>
<rect x="360" y="170" width="150" height="48" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="435" y="190" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">C_frame</text><text x="435" y="206" text-anchor="middle" font-size="9.5" fill="var(--muted)">掌帧推进</text>
<rect x="520" y="170" width="150" height="48" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="595" y="190" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">D_render</text><text x="595" y="206" text-anchor="middle" font-size="9.5" fill="var(--muted)">真渲染</text>
<rect x="680" y="170" width="160" height="48" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="760" y="190" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">F_wiring</text><text x="760" y="206" text-anchor="middle" font-size="9.5" fill="var(--muted)">真接引擎/插件</text>
<rect x="40" y="232" width="160" height="50" rx="8" fill="#fff7ed" stroke="#f59e0b" stroke-width="1.6"/><text x="120" y="252" text-anchor="middle" font-weight="700" fill="#b45309">E_live ⚠</text><text x="120" y="268" text-anchor="middle" font-size="9.5" fill="var(--muted)">活性·distinctStates</text>
<rect x="210" y="232" width="160" height="50" rx="8" fill="#fff7ed" stroke="#f59e0b" stroke-width="1.6"/><text x="290" y="252" text-anchor="middle" font-weight="700" fill="#b45309">H_progress ⚠</text><text x="290" y="268" text-anchor="middle" font-size="9.5" fill="var(--muted)">机制+latch·assertAfterPlay</text>
<rect x="380" y="232" width="150" height="50" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="455" y="252" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">G_input</text><text x="455" y="268" text-anchor="middle" font-size="9.5" fill="var(--muted)">输入有效</text>
<rect x="540" y="232" width="150" height="50" rx="8" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="615" y="252" text-anchor="middle" font-weight="700" fill="var(--harness-ink)">I_control</text><text x="615" y="268" text-anchor="middle" font-size="9.5" fill="var(--muted)">控制跟手(可选)</text>
</g>
<rect x="720" y="232" width="300" height="50" rx="9" fill="#fffbeb" stroke="#f59e0b" stroke-width="1.6"/>
<text x="870" y="252" text-anchor="middle" font-size="11" font-weight="700" fill="#b45309">⚠ E_live / H:未驱动→advisory</text>
<text x="870" y="268" text-anchor="middle" font-size="9.8" fill="var(--muted)">driven(spec 有 driver/inputs)→ 恢复致命</text>
<line x1="580" y1="120" x2="430" y2="166" stroke="#94a3b8" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#ar5)"/>
<text x="600" y="150" font-size="9.5" fill="var(--muted)">九门取证 A–I</text>
<rect x="858" y="44" width="172" height="72" rx="11" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="944" y="72" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">verdict.json</text>
<text x="944" y="92" text-anchor="middle" font-size="10" fill="var(--muted)">pass + advisoryGates</text>
<text x="944" y="107" text-anchor="middle" font-size="9.5" fill="var(--muted)">→ validate/play 节点消费</text>
<line x1="670" y1="80" x2="856" y2="80" stroke="#475569" stroke-width="2" marker-end="url(#ar5)"/>
</svg>
<div class="callout q">
<b>H 门 = per-game 断言的落点:</b><code>assertAfterPlay</code> 本就支持「<code>score increased</code>」这类断言。design 产的 gatespec 经现成 merge 进 <code>play_spec.assertAfterPlay</code>(SaaStudioNodes:665/674),被 H 门校验 —— <b>per-game 断言管线已存在</b>,新设计的「玩法语义门」就挂在这条现成路上,<b>通用九门一行不改</b>。
</div>
</section>
<!-- ⑥ 边界总表 -->
<section id="s6">
<h2><span class="n">⑥</span> Agent vs Harness 边界总表 <span class="badge now">现状</span></h2>
<p class="lead">一眼看清:整条链上只有 4 个节点真正吃 LLM(classify / design / generate / player〔+nreview〕),其余全是确定性 Harness。generate 是唯一的「真 agentic」节点。</p>
<table>
<thead><tr><th>节点</th><th>类型</th><th>模型</th><th>职责(现状)</th></tr></thead>
<tbody>
<tr><td><b>SaaGraphDispatcher</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>进程内提交 SAA 图;job 留 RUNNING;终态 → handleCallback</td></tr>
<tr><td><b>render</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>入参规整、brief 默认值齐全;图入口;分流 create / modify</td></tr>
<tr><td><b>classify</b></td><td><span class="pill p-a">Agent</span></td><td>stage1 · classify</td><td>brief 玩法品类分类</td></tr>
<tr><td><b>design</b></td><td><span class="pill p-a">Agent</span></td><td>stage1 · design</td><td>富化 brief → 设计稿(K_ENRICHED)+ gatespec;amodel 路经 K_ENRICHED 富化-brief 文本喂 generate</td></tr>
<tr><td><b>generate</b></td><td><span class="pill p-a">Agent · 真 agentic</span></td><td><b>MiniMax-M3</b>(thinking 关)</td><td>shell-out gen.mjs;ReAct 循环 6 工具;只写 L3;done 门绿 → staged bundle.iife.js</td></tr>
<tr><td><b>validate</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>查 K_AMODEL_GEN_OK + staged bundle + __GameBundle;写 play-spec.json;<b>amodel-ok → 短路 play</b></td></tr>
<tr><td><b>scaffold / asset / build</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td><b>factory/gamedef 旧路专用;amodel 主线被短路跳过(不可达)</b></td></tr>
<tr><td><b>play</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>serve + Chrome CDP + play.cdp.cjs 九门(玩法无关)→ verdict.json + per-game 断言</td></tr>
<tr><td><b>player</b></td><td><span class="pill p-a">Agent ×2</span></td><td>文本=stage1 · 视觉=<b>M3</b></td><td>VLM 玩家评判(软门);有体验问题 → repair</td></tr>
<tr><td><b>nreview</b></td><td><span class="pill p-a">Agent</span></td><td>stage1 · narrative</td><td>叙事类游戏叙事审查</td></tr>
<tr><td><b>emit</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>读 bundle.iife.js → 断 __GameBundle → status=succeeded</td></tr>
<tr><td><b>giveup</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td>超 maxRepairs(5) → status=failed + 落 giveup-dump.json</td></tr>
<tr><td><b>repair / escalate</b></td><td><span class="pill p-h">Harness 路由</span></td><td>—</td><td>repair=带反馈回 generate;escalate=升 stage2 强档回 generate</td></tr>
<tr><td><b>modify</b></td><td><span class="pill p-h">Harness</span></td><td>—</td><td><b>gamedef/factory 专用:regenerate-module→generate / deterministic→guard fail-loud(END);无 amodel 分支</b></td></tr>
<tr><td><b>handleCallback → 入库 → studio</b></td><td><span class="pill p-f">Fixed</span></td><td>—</td><td>建版本→组包→落包→回填;入 game_source_project + engineBundle;studio 前端消费</td></tr>
</tbody>
</table>
<p class="lead" style="margin-top:18px;border-color:var(--fixed)">
<b>核心数据契约:</b>
<code>K_ENRICHED</code>(design 产 → generate 消费,amodel 富化-brief 喂法)·
<code>K_PLAY_SPEC</code>(design 产 → play 消费 / 写 play-spec.json)·
<code>bundle.iife.js</code>(generate 产 → emit 消费)·
<code>verdict.json</code>(play 产)·
<code>K_AMODEL_GEN_OK</code> · <code>K_SOURCE_PROJECT</code>。
</p>
</section>
<!-- ============ 新设计部分 ============ -->
<div class="divider">
<div class="bar"></div>
<div class="t">新设计方向 · 让生成「更好玩」 · 求审</div>
<div class="bar"></div>
</div>
<!-- ⑦ 核心论点 -->
<section class="evo" id="s7">
<h2><span class="n">⑦</span> 核心论点:「能玩」已稳,短板在「好玩」 <span class="badge">新设计 · 求审</span></h2>
<p class="lead">这是整个新方向的判断锚点。依据 = <code>2026-06-23-design-agent-audio-asset-line-review.md</code>(§6.8 双评审已收口 + 代码核实)。</p>
<svg viewBox="0 0 1060 320" role="img" aria-label="核心论点">
<defs>
<marker id="ar7" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
</defs>
<!-- 左:能玩 已稳 -->
<rect x="30" y="40" width="300" height="240" rx="14" fill="var(--harness-bg)" stroke="var(--harness)" stroke-width="2"/>
<text x="180" y="72" text-anchor="middle" font-size="16" font-weight="800" fill="var(--harness-ink)">「能玩」已稳 ✓</text>
<text x="180" y="96" text-anchor="middle" font-size="11.5" fill="var(--harness-ink)">九门 = 运行时 · 玩法无关</text>
<g font-size="11" fill="var(--muted)">
<text x="52" y="128">· boot / 活性 / 输入 / 有进展</text>
<text x="52" y="150">· 故意不懂任何品类语义</text>
<text x="52" y="172">· v3 干净 re-gen 8/9 门通过</text>
<text x="52" y="194">· H score 0 → 1202(真有进展)</text>
<text x="52" y="220" font-weight="700" fill="var(--harness-ink)">这是它的价值 →</text>
<text x="52" y="238" font-weight="700" fill="var(--harness-ink)">不能破坏(新设计零改九门)</text>
</g>
<!-- 右:好玩 短板 -->
<rect x="730" y="40" width="300" height="240" rx="14" fill="var(--warn-bg)" stroke="#f59e0b" stroke-width="2"/>
<text x="880" y="72" text-anchor="middle" font-size="16" font-weight="800" fill="#b45309">「好玩」短板 ✗</text>
<text x="880" y="96" text-anchor="middle" font-size="11.5" fill="#b45309">两个缺口(创始人亲玩 v2 实测)</text>
<g font-size="11" fill="#78350f">
<text x="752" y="128" font-weight="700">缺口 1:design 层无策划脑</text>
<text x="762" y="148">· 没进货环(成长=营业额被动解锁)</text>
<text x="762" y="166">· 顾客来太快(数值无目标值)</text>
<text x="762" y="184" font-weight="700">缺口 2:平台音频/资产能力缺</text>
<text x="762" y="204">· audio-music 仅 ZzFXM 合成、不支持 mp3</text>
<text x="762" y="222">· studio 没水彩(资产随包不可达)</text>
<text x="762" y="246" font-weight="700" fill="#b45309">→ 补 design + 补平台能力</text>
</g>
<!-- 中:判断 -->
<rect x="380" y="100" width="300" height="120" rx="14" fill="#fff" stroke="var(--new)" stroke-width="2.4"/>
<text x="530" y="134" text-anchor="middle" font-size="14" font-weight="800" fill="var(--new-ink)">核心判断</text>
<text x="530" y="160" text-anchor="middle" font-size="11.5" fill="var(--ink)">九门验「能玩」</text>
<text x="530" y="180" text-anchor="middle" font-size="11.5" fill="var(--ink)"><tspan font-weight="800" fill="var(--new-ink)">不</tspan>验「好玩」</text>
<text x="530" y="202" text-anchor="middle" font-size="10.5" fill="var(--muted)">好玩 → 补 design 层 + 平台能力,不动门</text>
<line x1="330" y1="160" x2="378" y2="160" stroke="#475569" stroke-width="2" marker-end="url(#ar7)"/>
<line x1="730" y1="160" x2="682" y2="160" stroke="#475569" stroke-width="2" marker-end="url(#ar7)"/>
</svg>
<div class="callout q">
<b>三项决策(求批):</b>① 策划 agent(升级 designNode)= <b>低风险、大半已通</b>(design→amodel 富化-brief 喂法已通,主要是把 design 调强);② 音频/资产线 + ③ modify 双路 = <b>净新建</b>(双评审 BLOCKER + 代码核实:amodel 跳 scaffold/asset/build,现有 asset 机器与 modify 生命周期都在 gamedef 分支、amodel 根本不走)。
</div>
</section>
<!-- ⑧ 三阶段演进总图 -->
<section class="evo" id="s8">
<h2><span class="n">⑧</span> 现状 → Phase 1 → 2 → 3 演进总图(哪些复用 / 净新建 / 零触碰) <span class="badge">新设计 · 求审</span></h2>
<p class="lead">一张图看清演进。<b>蓝绿灰底</b>=现状不变骨架;<b>青色</b>=复用/升级(大半已通);<b>紫色</b>=净新建;<b>浅灰描边</b>=零触碰(gamedef/factory 旧路、通用九门)。三阶段彼此独立可交付。</p>
<svg viewBox="0 0 1060 640" role="img" aria-label="三阶段演进总图">
<defs>
<marker id="are" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
<marker id="aren" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#7c3aed"/>
</marker>
</defs>
<!-- 现状主链(一行) -->
<text x="30" y="44" font-size="13" font-weight="800" fill="var(--ink)">现状 amodel create 主链(不变骨架)</text>
<g font-size="11" font-weight="700">
<rect x="30" y="58" width="92" height="44" rx="9" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="76" y="84" text-anchor="middle" fill="var(--harness-ink)">render</text>
<rect x="138" y="58" width="92" height="44" rx="9" fill="var(--agent-bg)" stroke="var(--agent)"/><text x="184" y="84" text-anchor="middle" fill="var(--agent-ink)">classify</text>
<rect x="246" y="58" width="118" height="44" rx="9" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2"/><text x="305" y="80" text-anchor="middle" fill="var(--reuse-ink)">design</text><text x="305" y="94" text-anchor="middle" font-size="8.5" fill="var(--reuse-ink)">P1 升级</text>
<rect x="380" y="58" width="110" height="44" rx="9" fill="var(--agent-bg)" stroke="var(--agent)"/><text x="435" y="80" text-anchor="middle" fill="var(--agent-ink)">generate</text><text x="435" y="94" text-anchor="middle" font-size="8.5" fill="var(--muted)">M3 ReAct</text>
<rect x="506" y="58" width="118" height="44" rx="9" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="565" y="80" text-anchor="middle" fill="var(--harness-ink)">check+build</text><text x="565" y="94" text-anchor="middle" font-size="8" fill="var(--muted)">+P1 API 门</text>
<rect x="640" y="58" width="92" height="44" rx="9" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="686" y="84" text-anchor="middle" fill="var(--harness-ink)">validate</text>
<rect x="748" y="58" width="130" height="44" rx="9" fill="var(--untouch-bg)" stroke="var(--untouch)" stroke-width="2" stroke-dasharray="4 3"/><text x="813" y="80" text-anchor="middle" fill="var(--untouch-ink)">play 九门</text><text x="813" y="94" text-anchor="middle" font-size="8" fill="var(--untouch-ink)">零触碰</text>
<rect x="894" y="58" width="92" height="44" rx="9" fill="var(--harness-bg)" stroke="var(--harness)"/><text x="940" y="84" text-anchor="middle" fill="var(--harness-ink)">emit</text>
</g>
<g stroke="#475569" stroke-width="1.6">
<line x1="122" y1="80" x2="136" y2="80" marker-end="url(#are)"/>
<line x1="230" y1="80" x2="244" y2="80" marker-end="url(#are)"/>
<line x1="364" y1="80" x2="378" y2="80" marker-end="url(#are)"/>
<line x1="490" y1="80" x2="504" y2="80" marker-end="url(#are)"/>
<line x1="624" y1="80" x2="638" y2="80" marker-end="url(#are)"/>
<line x1="732" y1="80" x2="746" y2="80" marker-end="url(#are)"/>
<line x1="878" y1="80" x2="892" y2="80" marker-end="url(#are)"/>
</g>
<text x="686" y="124" text-anchor="middle" font-size="9" fill="var(--warn)">↑ validate-ok 短路到 play,跳 scaffold/asset/build(amodel 不可达)</text>
<!-- Phase 1 -->
<rect x="20" y="148" width="1020" height="132" rx="14" fill="#faf7ff" stroke="var(--new)" stroke-width="1.6"/>
<text x="40" y="174" font-size="14" font-weight="800" fill="var(--new-ink)">Phase 1 · 策划 agent(仅 create)+ 质量门</text>
<text x="40" y="192" font-size="10.5" fill="var(--muted)">大半已通 · 设计层补料能否更好玩 · 不依赖 modify/资产线</text>
<g font-size="10.5" font-weight="700">
<rect x="40" y="206" width="220" height="56" rx="10" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2"/>
<text x="150" y="228" text-anchor="middle" fill="var(--reuse-ink)">design → tool-using JS agent</text>
<text x="150" y="245" text-anchor="middle" font-size="9" fill="var(--reuse-ink)">read 真 sim skill(绝不手抄)</text>
<text x="150" y="257" text-anchor="middle" font-size="9" fill="var(--muted)">amodel 路 shell-out · gamedef 不动</text>
<rect x="280" y="206" width="200" height="56" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="380" y="228" text-anchor="middle" fill="var(--new-ink)">① API-存在静态门</text>
<text x="380" y="245" text-anchor="middle" font-size="9" fill="var(--new-ink)">check 阶段 · 玩法无关</text>
<text x="380" y="257" text-anchor="middle" font-size="9" fill="var(--muted)">治 stopBgm/defineScenes 幻觉</text>
<rect x="500" y="206" width="220" height="56" rx="10" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2"/>
<text x="610" y="228" text-anchor="middle" fill="var(--reuse-ink)">② per-game gatespec 断言</text>
<text x="610" y="245" text-anchor="middle" font-size="9" fill="var(--reuse-ink)">design 产 · 复用 H assertAfterPlay</text>
<text x="610" y="257" text-anchor="middle" font-size="9" fill="var(--muted)">经济=买扣钱/库存减</text>
<rect x="740" y="206" width="200" height="56" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="840" y="228" text-anchor="middle" fill="var(--new-ink)">③ 好玩 N-of-8 机检</text>
<text x="840" y="245" text-anchor="middle" font-size="9" fill="var(--new-ink)">设计时 · 过反无趣 8 条 ≥N</text>
<text x="840" y="257" text-anchor="middle" font-size="9" fill="var(--muted)">给主观调参兜客观地板</text>
</g>
<!-- Phase 2 -->
<rect x="20" y="296" width="1020" height="120" rx="14" fill="#faf7ff" stroke="var(--new)" stroke-width="1.6"/>
<text x="40" y="322" font-size="14" font-weight="800" fill="var(--new-ink)">Phase 2 · 音频/资产线(净新建 · harness 内部)</text>
<text x="40" y="340" font-size="10.5" fill="var(--muted)">注:是 amodel harness 内部新建,<b>不是</b>扩 SAA asset 节点(amodel 跳过它)</text>
<g font-size="10.5" font-weight="700">
<rect x="40" y="354" width="230" height="50" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="155" y="375" text-anchor="middle" fill="var(--new-ink)">mp3/opus 压缩 + 懒加载</text>
<text x="155" y="392" text-anchor="middle" font-size="9" fill="var(--muted)">首帧后 · ~150–300KB · 首屏≤2MB</text>
<rect x="290" y="354" width="230" height="50" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="405" y="375" text-anchor="middle" fill="var(--new-ink)">audio-music 扩 loadTrack</text>
<text x="405" y="392" text-anchor="middle" font-size="9" fill="var(--muted)">现仅 ZzFXM 合成 · 插件契约变更</text>
<rect x="540" y="354" width="230" height="50" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="655" y="375" text-anchor="middle" fill="var(--new-ink)">studio 下发资产 + serve</text>
<text x="655" y="392" text-anchor="middle" font-size="9" fill="var(--muted)">顺带修 studio 没水彩</text>
<rect x="790" y="354" width="150" height="50" rx="10" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2"/>
<text x="865" y="375" text-anchor="middle" fill="var(--reuse-ink)">SFX 留 ZzFX</text>
<text x="865" y="392" text-anchor="middle" font-size="9" fill="var(--muted)">短音效 · 不变</text>
</g>
<!-- Phase 3 -->
<rect x="20" y="432" width="1020" height="118" rx="14" fill="#faf7ff" stroke="var(--new)" stroke-width="1.6"/>
<text x="40" y="458" font-size="14" font-weight="800" fill="var(--new-ink)">Phase 3 · modify 双路(净新建)</text>
<text x="40" y="476" font-size="10.5" fill="var(--muted)">现 modify 是 gamedef 专用、不调 amodel → 需在 harness 新建 amodel modify 分支</text>
<g font-size="10.5" font-weight="700">
<rect x="40" y="490" width="280" height="50" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="180" y="511" text-anchor="middle" fill="var(--new-ink)">harness 加「读旧项目 src/」工具</text>
<text x="180" y="528" text-anchor="middle" font-size="9" fill="var(--muted)">现 harness 每次从 _template 全新 scaffold</text>
<rect x="340" y="490" width="280" height="50" rx="10" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="480" y="511" text-anchor="middle" fill="var(--new-ink)">modifyRouter 加 amodel 分支</text>
<text x="480" y="528" text-anchor="middle" font-size="9" fill="var(--muted)">全量重生成带意图 / 真增量改 src/</text>
<rect x="640" y="490" width="300" height="50" rx="10" fill="var(--untouch-bg)" stroke="var(--untouch)" stroke-width="2" stroke-dasharray="4 3"/>
<text x="790" y="511" text-anchor="middle" fill="var(--untouch-ink)">gamedef modify 生命周期 · 零触碰</text>
<text x="790" y="528" text-anchor="middle" font-size="9" fill="var(--untouch-ink)">deterministic / regenerate-module 不动</text>
</g>
<!-- 阶段间纵向指示 -->
<line x1="305" y1="102" x2="305" y2="204" stroke="var(--new)" stroke-width="1.6" stroke-dasharray="5 3" marker-end="url(#aren)"/>
<line x1="565" y1="102" x2="380" y2="204" stroke="var(--new)" stroke-width="1.4" stroke-dasharray="5 3" marker-end="url(#aren)"/>
</svg>
<div class="callout">
<b>读图三色:</b><span class="pill p-reuse">青=复用/升级</span> design 升级 + per-game 断言 + SFX;<span class="pill p-new">紫=净新建</span> API 静态门 + 好玩机检 + 整条音频/资产线 + modify 双路;<span class="pill p-untouch">灰=零触碰</span> 通用九门 + gamedef/factory 旧路(含其 modify 生命周期)。
</div>
</section>
<!-- ⑨ Phase 1 策划 agent 放大 -->
<section class="evo" id="s9">
<h2><span class="n">⑨</span> Phase 1 放大 = 策划 agent(design 升级为 tool-using JS agent) <span class="badge">新设计 · 求审</span></h2>
<p class="lead">Phase 1 只动 create 路的 design。把它从「Java 单次便宜模型调用」升级为<b>会读 skill 的 tool-using JS agent</b>:read 真 <code>sim-business-game-design.md</code>(守「绝不手抄 skill」铁律)→ 产更丰富设计(进货/库存环 + 节奏目标值 + 好玩自检)+ gatespec。<b>amodel 路 shell-out,gamedef design 一行不动(零回归)。</b></p>
<svg viewBox="0 0 1060 400" role="img" aria-label="Phase 1 策划 agent">
<defs>
<marker id="ar9" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
</defs>
<!-- 入口 -->
<rect x="30" y="170" width="150" height="56" rx="11" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="105" y="194" text-anchor="middle" font-size="12.5" font-weight="700" fill="var(--fixed-ink)">brief + 品类</text>
<text x="105" y="212" text-anchor="middle" font-size="9.5" fill="var(--muted)">classify 之后</text>
<!-- designNode 分流 -->
<rect x="210" y="160" width="150" height="76" rx="11" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2.2"/>
<text x="285" y="184" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--reuse-ink)">designNode</text>
<text x="285" y="202" text-anchor="middle" font-size="9.5" fill="var(--reuse-ink)">按 sourceMode 分流</text>
<text x="285" y="218" text-anchor="middle" font-size="9" fill="var(--muted)">amodel 开关可回滚</text>
<!-- amodel 路:JS 设计 agent -->
<circle cx="600" cy="120" r="74" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2.6"/>
<text x="600" y="106" text-anchor="middle" font-size="14" font-weight="800" fill="var(--new-ink)">JS 设计 agent</text>
<text x="600" y="124" text-anchor="middle" font-size="10" fill="var(--new-ink)">M3(D1:升 M3)</text>
<text x="600" y="140" text-anchor="middle" font-size="9.5" fill="var(--muted)">tool-using · ReAct</text>
<!-- 工具:read_file / list_dir / done -->
<g font-size="10" font-weight="600">
<rect x="800" y="50" width="180" height="38" rx="8" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="1.6"/>
<text x="890" y="69" text-anchor="middle" fill="var(--fixed-ink)">read_file</text>
<text x="890" y="82" text-anchor="middle" font-size="8.5" fill="var(--muted)">读 sim skill + api.d.ts</text>
<rect x="800" y="96" width="180" height="34" rx="8" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="1.6"/>
<text x="890" y="118" text-anchor="middle" fill="var(--fixed-ink)">list_dir(只读)</text>
<rect x="800" y="138" width="180" height="44" rx="8" fill="var(--new-bg)" stroke="var(--new)" stroke-width="1.8"/>
<text x="890" y="158" text-anchor="middle" fill="var(--new-ink)">done(designText)</text>
<text x="890" y="173" text-anchor="middle" font-size="8.5" fill="var(--muted)">无 write / 无 shell</text>
</g>
<!-- 关键铁律 -->
<rect x="440" y="224" width="320" height="48" rx="9" fill="#fff" stroke="var(--new)" stroke-width="1.6" stroke-dasharray="3 3"/>
<text x="600" y="244" text-anchor="middle" font-size="11" font-weight="800" fill="var(--new-ink)">铁律:read 真 skill,绝不手抄</text>
<text x="600" y="261" text-anchor="middle" font-size="9.5" fill="var(--muted)">sim skill §1 范式/§3 经济/§9 可达性/§10 反无趣8条(旧 Java 单次调用没读它)</text>
<!-- gamedef 路:保持不变 -->
<rect x="210" y="296" width="280" height="56" rx="11" fill="var(--untouch-bg)" stroke="var(--untouch)" stroke-width="2" stroke-dasharray="4 3"/>
<text x="350" y="320" text-anchor="middle" font-size="12" font-weight="700" fill="var(--untouch-ink)">gamedef 路 · Java DESIGN_SYSTEM 单次调用</text>
<text x="350" y="338" text-anchor="middle" font-size="9.5" fill="var(--untouch-ink)">字节不变 · 零回归</text>
<!-- 产出 -->
<rect x="800" y="240" width="230" height="120" rx="11" fill="#fff" stroke="var(--reuse)" stroke-width="2"/>
<text x="915" y="264" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--reuse-ink)">设计产出 → generate</text>
<g font-size="10" fill="var(--ink)">
<text x="816" y="288">· 核心循环:进货扣币→库存→售卖→补货</text>
<text x="816" y="308">· 节奏/数值目标值(spawn 区间/rush 起点)</text>
<text x="816" y="328">· 好玩自检:命中反无趣 ≥N 条</text>
<text x="816" y="348" font-weight="700" fill="var(--new-ink)">· gatespec(per-game 断言)</text>
</g>
<!-- 箭头 -->
<line x1="180" y1="198" x2="208" y2="198" stroke="#475569" stroke-width="2" marker-end="url(#ar9)"/>
<line x1="350" y1="160" x2="540" y2="135" stroke="var(--new)" stroke-width="2" marker-end="url(#ar9)"/>
<text x="430" y="138" font-size="9.5" font-weight="700" fill="var(--new-ink)">amodel · shell-out</text>
<line x1="350" y1="236" x2="320" y2="294" stroke="var(--untouch)" stroke-width="1.6" stroke-dasharray="4 3" marker-end="url(#ar9)"/>
<text x="250" y="278" font-size="9" fill="var(--untouch-ink)">gamedef</text>
<!-- agent ↔ tools -->
<line x1="674" y1="100" x2="798" y2="72" stroke="var(--new)" stroke-width="1.4" marker-end="url(#ar9)"/>
<line x1="674" y1="118" x2="798" y2="113" stroke="var(--new)" stroke-width="1.4" marker-end="url(#ar9)"/>
<line x1="668" y1="150" x2="798" y2="160" stroke="var(--new)" stroke-width="1.4" marker-end="url(#ar9)"/>
<!-- done → 产出(经富化-brief K_ENRICHED 喂法,merge 不变) -->
<path d="M915,182 C915,205 915,218 915,238" fill="none" stroke="#475569" stroke-width="2" marker-end="url(#ar9)"/>
<text x="975" y="212" text-anchor="middle" font-size="9" fill="var(--muted)">K_ENRICHED</text>
</svg>
<div class="callout q">
<b>喂法零接线:</b>design 产出经现成的 <b>K_ENRICHED 富化-brief 文本</b>喂 amodel harness(create 路已通);gatespec 经现成 merge 进 <code>play_spec.assertAfterPlay</code>。Phase 1 主要是<b>把 design 调强(prompt + 升 M3)</b>,几乎不新接线。design 输出格式(D8)<b>先维持富化散文 brief</b>(今天就能用、零接线),结构化 schema 留后(会扰动刚转绿的 amodel e2e)。
</div>
</section>
<!-- ⑩ 质量门三类 -->
<section class="evo" id="s10">
<h2><span class="n">⑩</span> 质量门三类 — 门各司其职,通用九门保持玩法无关 <span class="badge">新设计 · 求审</span></h2>
<p class="lead">这是新设计最关键的边界纪律(采纳创始人纠正):玩法正确性走 per-game 断言,<b>绝不进通用九门</b>。三类门各管一段,互不污染。</p>
<svg viewBox="0 0 1060 320" role="img" aria-label="质量门三类">
<!-- 三列 -->
<!-- ① API 存在静态门 -->
<rect x="30" y="40" width="320" height="240" rx="14" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="190" y="70" text-anchor="middle" font-size="14" font-weight="800" fill="var(--new-ink)">① API-存在静态门</text>
<rect x="120" y="84" width="140" height="26" rx="13" fill="#fff" stroke="var(--new)"/><text x="190" y="102" text-anchor="middle" font-size="10.5" font-weight="700" fill="var(--new-ink)">玩法无关 · 净新建</text>
<g font-size="10.5" fill="var(--ink)">
<text x="52" y="138" font-weight="700">在哪:check 阶段(静态)</text>
<text x="52" y="162">校验插件/ctx 方法<b>真存在</b></text>
<text x="52" y="184">解析 plugins.X.m() / ctx.m()</text>
<text x="52" y="206">比对各 api.d.ts 方法集</text>
<text x="52" y="234" font-weight="700" fill="var(--new-ink)">治:方法名幻觉</text>
<text x="52" y="254" font-size="10"><tspan fill="var(--warn)">stopBgm</tspan>(真名 stop)/ defineScenes</text>
</g>
<!-- ② per-game gatespec -->
<rect x="370" y="40" width="320" height="240" rx="14" fill="var(--reuse-bg)" stroke="var(--reuse)" stroke-width="2"/>
<text x="530" y="70" text-anchor="middle" font-size="14" font-weight="800" fill="var(--reuse-ink)">② per-game gatespec 断言</text>
<rect x="450" y="84" width="160" height="26" rx="13" fill="#fff" stroke="var(--reuse)"/><text x="530" y="102" text-anchor="middle" font-size="10.5" font-weight="700" fill="var(--reuse-ink)">玩法语义 · 复用现成</text>
<g font-size="10.5" fill="var(--ink)">
<text x="392" y="138" font-weight="700">谁产:design agent(这一局)</text>
<text x="392" y="162">经济=买扣钱、库存减</text>
<text x="392" y="184">{path:"currency",op:"decreased"}</text>
<text x="392" y="206">复用现成 H 门 assertAfterPlay</text>
<text x="392" y="234" font-weight="700" fill="var(--reuse-ink)">通用九门:一行不改</text>
<text x="392" y="254" font-size="10">+ 保证该动作 driver 可达</text>
</g>
<!-- ③ 好玩 N-of-8 -->
<rect x="710" y="40" width="320" height="240" rx="14" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="870" y="70" text-anchor="middle" font-size="14" font-weight="800" fill="var(--new-ink)">③ 好玩 N-of-8 机检</text>
<rect x="795" y="84" width="150" height="26" rx="13" fill="#fff" stroke="var(--new)"/><text x="870" y="102" text-anchor="middle" font-size="10.5" font-weight="700" fill="var(--new-ink)">设计时 · 净新建</text>
<g font-size="10.5" fill="var(--ink)">
<text x="732" y="138" font-weight="700">在哪:design 产出自检(设计时)</text>
<text x="732" y="162">过「反无趣 8 条」≥N 条</text>
<text x="732" y="184">动作有飘字+音效 / score-delta</text>
<text x="732" y="206">解锁阈值 / 经济扣减 …</text>
<text x="732" y="234" font-weight="700" fill="var(--new-ink)">作用:客观地板</text>
<text x="732" y="254" font-size="10">给主观调参兜底(N 待定,建议 4)</text>
</g>
</svg>
<div class="callout">
<b>边界纪律(硬约束):</b>通用九门 = 运行时 + 玩法无关,<b>绝不加品类/玩法语义</b>。① 是玩法无关的静态门(治幻觉);② 是玩法语义但走 per-game 断言(挂 H 门现成 assertAfterPlay,九门定义零改);③ 是设计时机检(不在运行时门里)。三者分立 = 既补「好玩」又不破坏「能玩」门的纯洁性。
</div>
</section>
<!-- ⑪ Phase 2 音频/资产线 -->
<section class="evo" id="s11">
<h2><span class="n">⑪</span> Phase 2 放大 = 音频/资产线(净新建 · harness 内部,非扩 SAA asset 节点) <span class="badge">新设计 · 求审</span></h2>
<p class="lead"><b>定位纠正(双评审 BLOCKER):</b>asset/audio 必须做进 amodel harness 自己的 scaffold/build/stage,<b>不是</b>扩 SAA assetNode —— 因为 amodel 路 validate-ok 短路、根本不走 SAA asset 节点。这是一条贯穿「插件契约 → host → harness → studio」的新运行时通道。</p>
<svg viewBox="0 0 1060 360" role="img" aria-label="Phase 2 音频资产线">
<defs>
<marker id="ar11" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#7c3aed"/>
</marker>
</defs>
<!-- 五处通道 横链 -->
<text x="30" y="44" font-size="13" font-weight="800" fill="var(--new-ink)">新音频通道(五处串起来)</text>
<rect x="30" y="62" width="180" height="74" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="120" y="86" text-anchor="middle" font-size="11.5" font-weight="800" fill="var(--new-ink)">1 · 插件契约变更</text>
<text x="120" y="106" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">audio-music 加 loadTrack(url)</text>
<text x="120" y="122" text-anchor="middle" font-size="9" fill="var(--muted)">decode/loop · 走 contract-first</text>
<rect x="240" y="62" width="170" height="74" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="325" y="86" text-anchor="middle" font-size="11.5" font-weight="800" fill="var(--new-ink)">2 · host 解锁接文件</text>
<text x="325" y="106" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">首手势解锁 → 播文件</text>
<text x="325" y="122" text-anchor="middle" font-size="9" fill="var(--muted)">现喂的是合成器</text>
<rect x="440" y="62" width="180" height="74" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="530" y="86" text-anchor="middle" font-size="11.5" font-weight="800" fill="var(--new-ink)">3 · harness build</text>
<text x="530" y="106" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">拷/引 mp3 + manifest</text>
<text x="530" y="122" text-anchor="middle" font-size="9" fill="var(--muted)">amodel harness 内部</text>
<rect x="650" y="62" width="170" height="74" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="735" y="86" text-anchor="middle" font-size="11.5" font-weight="800" fill="var(--new-ink)">4 · studio serve</text>
<text x="735" y="106" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">serve mp3 + 资产随包可达</text>
<text x="735" y="122" text-anchor="middle" font-size="9" fill="var(--muted)">现仅内联 engineBundle</text>
<rect x="850" y="62" width="180" height="74" rx="11" fill="var(--warn-bg)" stroke="#f59e0b" stroke-width="2"/>
<text x="940" y="84" text-anchor="middle" font-size="11.5" font-weight="800" fill="#b45309">5 · fetch 张力</text>
<text x="940" y="104" text-anchor="middle" font-size="9.5" fill="#78350f">build-from-source 现禁 fetch</text>
<text x="940" y="120" text-anchor="middle" font-size="9" fill="#78350f">开例外 vs base64 内联(撞包体门)</text>
<g stroke="var(--new)" stroke-width="1.6">
<line x1="210" y1="99" x2="238" y2="99" marker-end="url(#ar11)"/>
<line x1="410" y1="99" x2="438" y2="99" marker-end="url(#ar11)"/>
<line x1="620" y1="99" x2="648" y2="99" marker-end="url(#ar11)"/>
<line x1="820" y1="99" x2="848" y2="99" marker-end="url(#ar11)"/>
</g>
<!-- 决策 D2 -->
<rect x="30" y="170" width="480" height="150" rx="12" fill="#fff" stroke="var(--new)" stroke-width="2"/>
<text x="270" y="196" text-anchor="middle" font-size="13" font-weight="800" fill="var(--new-ink)">交付方式(D2 倾向)</text>
<g font-size="11" fill="var(--ink)">
<text x="52" y="224" font-weight="700">BGM:mp3/opus 压缩 + 懒加载 fetch + 首帧后</text>
<text x="52" y="248">· ffmpeg 压到 ~150–300KB → 首屏 ≤2MB / 总 ≤10MB</text>
<text x="52" y="270">· 懒加载需同源 served URL + fetch 例外</text>
<text x="52" y="292" font-weight="700" fill="var(--reuse-ink)">SFX 留 ZzFX(短音效,几十字节,不变)</text>
<text x="52" y="312" font-size="10" fill="var(--muted)">容错:无音乐资产 → 退 ZzFX/静音(不强制每款都有 mp3)</text>
</g>
<!-- 图片侧 -->
<rect x="540" y="170" width="490" height="150" rx="12" fill="#fff" stroke="var(--reuse)" stroke-width="2"/>
<text x="785" y="196" text-anchor="middle" font-size="13" font-weight="800" fill="var(--reuse-ink)">图片(部分已有,缺 studio 可达)</text>
<g font-size="11" fill="var(--ink)">
<text x="562" y="224"><tspan font-weight="700" fill="var(--reuse-ink)">已有:</tspan>harness stage 已拷 assets/</text>
<text x="562" y="246">· loadHostAssets 浏览器侧已载图(staged 真玩已验水彩)</text>
<text x="562" y="270" font-weight="700" fill="var(--new-ink)">缺口:studio 侧把资产随包可达</text>
<text x="562" y="290">· studio 现只内联 engineBundle → 顺带修「没水彩」</text>
<text x="562" y="312" font-size="10" fill="var(--muted)">loadHostAssets 已有但明确不载音频 → 音频全新</text>
</g>
</svg>
<div class="callout">
<b>体积门(M2 待解):</b>首屏≤2MB / 总≤10MB 门目前在 amodel 路<b>未真正布防</b>(publish-amgen 只记 bundleSize)。执行版须明确:该门在 amodel 哪里布 + 解 inline-vs-lazy-fetch 张力。<b>这是兔子洞(五处都要动),故 Phase 2 独立交付。</b>
</div>
</section>
<!-- ⑫ Phase 3 modify 双路 -->
<section class="evo" id="s12">
<h2><span class="n">⑫</span> Phase 3 放大 = modify 双路(净新建) <span class="badge">新设计 · 求审</span></h2>
<p class="lead"><b>现状(B1,代码核实):</b>无 amodel modify;harness 每次从 <code>_template</code> 全新 scaffold、<b>无「读旧项目 src/」工具</b>;现 modify 节点是 gamedef/factory 专用(regenerate-module→generate / deterministic→guard fail-loud)。要让 amodel 也能「按意图改旧游戏」,必须新建两件能力 + 定语义。</p>
<svg viewBox="0 0 1060 320" role="img" aria-label="Phase 3 modify 双路">
<defs>
<marker id="ar12" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#475569"/>
</marker>
<marker id="ar12n" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#7c3aed"/>
</marker>
</defs>
<!-- 入口 -->
<rect x="30" y="130" width="160" height="60" rx="11" fill="var(--fixed-bg)" stroke="var(--fixed)" stroke-width="2"/>
<text x="110" y="154" text-anchor="middle" font-size="12" font-weight="700" fill="var(--fixed-ink)">改意图 + 旧游戏</text>
<text x="110" y="172" text-anchor="middle" font-size="9.5" fill="var(--muted)">「加进货+放慢+BGM」</text>
<!-- modifyRouter -->
<rect x="220" y="120" width="180" height="80" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2.2"/>
<text x="310" y="146" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--new-ink)">modifyRouter</text>
<text x="310" y="166" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">+ amodel 分支(新建)</text>
<text x="310" y="182" text-anchor="middle" font-size="9" fill="var(--muted)">现仅 gamedef/factory 分支</text>
<!-- 新建工具:读旧项目 -->
<rect x="440" y="40" width="260" height="70" rx="11" fill="var(--new-bg)" stroke="var(--new)" stroke-width="2"/>
<text x="570" y="64" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--new-ink)">harness 加「读旧项目 src/」工具</text>
<text x="570" y="84" text-anchor="middle" font-size="9.5" fill="var(--new-ink)">载入 base 项目 src/ → 喂 M3</text>
<text x="570" y="100" text-anchor="middle" font-size="9" fill="var(--muted)">现 harness 无此入口</text>
<!-- 语义二选一 -->
<rect x="440" y="130" width="260" height="80" rx="11" fill="#fff" stroke="var(--new)" stroke-width="2"/>
<text x="570" y="154" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--new-ink)">modify 语义(二选一)</text>
<text x="570" y="176" text-anchor="middle" font-size="10" fill="var(--ink)">A 全量重生成-带意图(便宜)</text>
<text x="570" y="194" text-anchor="middle" font-size="10" fill="var(--ink)">B 真增量改 src/(贵 · 新能力)</text>
<!-- 首靶纠正 -->
<rect x="440" y="230" width="260" height="60" rx="11" fill="var(--warn-bg)" stroke="#f59e0b" stroke-width="2"/>
<text x="570" y="254" text-anchor="middle" font-size="11.5" font-weight="800" fill="#b45309">首靶纠正(m4)</text>
<text x="570" y="274" text-anchor="middle" font-size="9.5" fill="#78350f">用干净 v3-类项目,<b>别用 hand-fix 的 v2</b></text>
<!-- gamedef 零触碰 -->
<rect x="740" y="120" width="290" height="80" rx="11" fill="var(--untouch-bg)" stroke="var(--untouch)" stroke-width="2" stroke-dasharray="4 3"/>
<text x="885" y="146" text-anchor="middle" font-size="12.5" font-weight="800" fill="var(--untouch-ink)">gamedef modify 生命周期</text>
<text x="885" y="168" text-anchor="middle" font-size="10" fill="var(--untouch-ink)">deterministic / regenerate-module</text>
<text x="885" y="188" text-anchor="middle" font-size="10" fill="var(--untouch-ink)">零触碰 · 不动</text>
<!-- 箭头 -->
<line x1="190" y1="160" x2="218" y2="160" stroke="#475569" stroke-width="2" marker-end="url(#ar12)"/>
<line x1="400" y1="140" x2="438" y2="80" stroke="var(--new)" stroke-width="1.8" marker-end="url(#ar12n)"/>
<text x="400" y="110" font-size="9" font-weight="700" fill="var(--new-ink)">amodel</text>
<line x1="400" y1="170" x2="438" y2="170" stroke="var(--new)" stroke-width="1.8" marker-end="url(#ar12n)"/>
<line x1="400" y1="190" x2="438" y2="255" stroke="var(--new)" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#ar12n)"/>
<line x1="400" y1="150" x2="738" y2="150" stroke="var(--untouch)" stroke-width="1.6" stroke-dasharray="4 3" marker-end="url(#ar12)"/>
<text x="600" y="142" font-size="9" fill="var(--untouch-ink)">gamedef/factory(不变)</text>
</svg>
<div class="callout q">
<b>三阶段独立性:</b>Phase 1(仅 create)不依赖 modify/资产线,可独立验「设计层补料能否更好玩」;Phase 2、3 各自独立执行版。每阶段回归底线 = factory/gamedef 冻结路、studio 现播放路、<b>通用九门、SAA 83 确定性回归、刚转绿的 amodel e2e</b> 全绿。
</div>
</section>
<div class="foot">绘境AI · SAA 游戏生成引擎 · 现状(代码核实)+ 演进(策划 agent / 3 阶段 · §6.8 双评审收口)· new-api 网关 100.64.0.8:3000 · 上半=当前真实形态,下半=求审的新设计方向</div>
</div>
</body>
</html>