# 固定游戏架构 + SAA agentic studio — execution 版
> 类型:**execution 版**(供两开发线实现)。日期:2026-06-17 · 起草:Opus(effort=高)。
> **上游(权威,勿翻旧账)**:
> - [`2026-06-17-固定游戏架构与SAA-agentic-studio-review.md`](2026-06-17-固定游戏架构与SAA-agentic-studio-review.md) —— 以 **§1.5(已定决策)+§10(深析并入)** 为权威;§4 旧 agent 表已被 §10.1 取代。
> - [`2026-06-17-生成主线-游戏生命周期项目管理-review.md`](2026-06-17-生成主线-游戏生命周期项目管理-review.md) —— modify=**改源不改打包产物** v2 口径权威;manifest 可寻址 diff 已废、不采用。
> - 命名/状态/分层规范见 [`.agents/rules/engineering-conventions.md`](../../.agents/rules/engineering-conventions.md) §10;收口走 [`.agents/skills/wave-close-checklist.md`](../../.agents/skills/wave-close-checklist.md)。
> **状态**:ACTIVE。**契约先行**——本档锁定的 8 契约 + 拓扑表是两开发线开工前必须齐的单一事实源;契约未冻结前两线不得独立开工。
---
## 0. 结论先行(TL;DR)
1. **范式**:Opus 设计**固定游戏架构**(轻量声明式领域模型 + profile 三维),cheap 模型在 **SAA agentic studio** 里**填槽**;游戏 = **源项目**,生命周期 create/modify/extend/build/release/maintain;**改源不改打包产物**(GamePackage 产物 schema、宿主 `window.__GameBundle.bootGameHost`、feed 全不变)。
2. **8 契约**(本档 §5 逐个给字段级 schema/签名):①源项目 schema ②源项目 DB(独立 `game_source_project` 表)③GamePackage 源/产物分离 ④SAA state key 全表 ⑤asset 产消 ⑥modifyPatch ⑦唯一确定性构建 API ⑧trace+cost 字段。
3. **拓扑**(§6):现 11 节点 SAA 图 → 演进为 **render·classify(新)·design·scaffold·logic/repair(一个核心代码 agent 两面)·config/balance·asset(新)·build·QA(九门)·modify(新)** + 条件激活 **narrative-designer-reviewer** + 离线 **skill-curator**(MVP 只做 Debug Skill);救场阶梯 = cheap→连续 5 次九门失败→升档(stage1→stage2)→再 3 次失败→放弃,`recursionLimit` 按 5+3 重算。
4. **两线切分**(§8):**后端线**(6c6g 编辑 / mini-desktop 构建)锁源项目 schema+DB、SAA state/拓扑/救场路由、studio 编排、trace+cost、modifyPatch;**引擎+前端线**(Mac)锁 2D 适配器(游戏定义→LittleJS)、asset 产消、唯一构建实现、九门 QA、前端创作/修改/预览 UI。交汇=本档 8 契约。
5. **开工前待解 → ✅ 全部收口**(创始人 2026-06-17「按推荐」):①源项目表归属=**studio 模块** ②后端 RuntimeBuild **只落包**(构建留 worker/SAA build.mjs)③缓存透传 ✅ spike 绿(76-93%)+ classify ✅ 绿(tickModel 100%)④classify→archetype 映射表=**引导非校验** ⑤narrative `needsRepair=true` **等价一次九门失败计入 failCount** ⑥studio `create`=**draft+generate 一步式封装**。**契约冻结进行中**:keystone `source-project.schema.json` 已落;V18/ALL_KEYS/trace = 后端线 module 实现(V18 入 game-cloud 模块迁移,非 stale 的 contracts/db-schemas)。
---
## 1. 目标与范围边界
### 1.1 目标(本期 execution 要交付什么)
把"固定架构 + SAA agentic studio"由 review 决策**落成可被验证的契约与实现切分**,使两条开发线(后端 / 引擎+前端)能**契约先行、各自独立开工**,最终合流出:**一句话 → studio(cheap 填固定架构槽) → 源项目 → 确定性构建 → 九门真玩过门 → 现 GamePackage 产物 → feed 真玩** 的全链,且支持 **modify(改源不改产物,出新预览版,走发布审核)**。
### 1.2 范围内(In Scope)
- 固定游戏定义模型 schema(实体/组件/行为/场景/规则 + profile 三维)+ **2D 适配器(LittleJS)**。
- 源项目工件契约 + 独立 DB 存储(`game_source_project`)+ 源/产物分离。
- SAA 图扩展:新增 classify/asset/modify 节点 + narrative 分支 + 救场升档阶梯 + 完整观测/dump。
- 唯一确定性构建 API(化解构建权威分裂)。
- 三段式 prompt 缓存策略 + 版本化 Prompt Registry 资产(冻结 system+few-shot)。
- trace+cost 契约扩展(modelTier/escalationEvents/缓存命中两套字段/giveupDumpPath)。
- modify 生命周期(确定性编辑 / LLM 重生成单模块)。
### 1.3 范围外(Out of Scope,本期不做)
- **3D 适配器**(Cocos):定义格式留前向兼容,适配器本身 Phase 2。
- **Template Skill**(骨架萃取晋升):MVP 只做 Debug Skill(冷路径)。
- **per-creator 计量计费**:沿用现 new-api logs.quota 权威对账,不拆到人(对齐 memory `newapi-billing-plane-integration`)。
- **渠道线(小游戏)引擎**:另行竞标(HJ-CH-001),本架构不动 feed 终裁。
- **打包产物 schema / 宿主 / feed 改动**:零改(blast radius 收窄铁律)。
### 1.4 不可破坏的硬约束(回归红线)
| 约束 | 出处(已核码) |
|---|---|
| GamePackage 产物 schema 不变(`schemaVersion:"1.0"`,additive 新字段不升版本) | `contracts/game-package.schema.json:10-13` |
| 宿主装载契约不变:`window.__GameBundle.bootGameHost({canvas,seed})` + iife 全局名 `__GameBundle` | `game-studio/src/host/inject.ts:202,241-248` |
| 回调唯一写入路径不变:`DifyCallbackService.handleCallback`(succeeded 建版本→组包→落包→回填三表同事务) | `SaaGraphDispatcher.java:251`、`DifyCallbackReqVO.java:13-17` |
| 九门 harness(serve-and-play.sh/play.cdp.cjs/build.mjs)**子进程复用·禁重写** | `SaaGenNodes.java:37`、`run.py:45-72` |
| Flyway 已合入禁改,只新增:新表走 **V18.0.0**(接 V17 之后) | `V17.0.0__aigc_task_add_source.sql:19`(头注铁律) |
| 错误码段:studio=1-112-***、aigc=1-101-***、runtime=1-103-*** | `V8.0.0__create_game_studio.sql:14` |
---
## 2. 前置条件
1. **上游决策已定**:review §1.5 + §10 全部已定(领域模型轻量声明式+三维 profile、Phase1 仅 2D、agent 列表、救场带限额、narrative 留 MVP、MVP 只 Debug Skill、写码+修 bug=一个核心代码 agent)。
2. **现行底座可用且已核码**:
- SAA 全图 11 节点已建并生产收口(`SaaStudioGraph.assemble`),checkpoint(MysqlSaver)/observation/trace split-brain 已闭合(`703e462c`)。
- W-G1 worker 闭环可跑(generate→validate→build→play 九门),救场 stage1/stage2 在 `models.yaml`。
- 宿主引擎包路(P2)已真接:`bootGameHost` 引擎掌帧 + game_loaded/game_end 桥接已 e2e。
- `game-module-studio` 编排层已建(session/taskchain/asset,`/studio/{draft,generate,task,regenerate}`)。
3. **机器分工**(memory `internal-build-infra-servers`):后端编辑 6c6g、权威构建/e2e 门 mini-desktop、基建 mini-infra;**6c6g 禁起浏览器**(九门真玩在 mini-desktop)。
4. **子代理模型分流**(memory `opus-subagents-critical-tasks`):实现派 opus、机械活派 haiku、最高复杂度终裁用 opus 兜(本环境 fable 子代理不可用)。
---
## 3. 涉及模块与文件路径
### 3.1 后端线(game-cloud,com.wanxiang.huijing)
| 关注点 | 路径 |
|---|---|
| 源项目 schema(新契约) | `contracts/agent-loop/source-project.schema.json`(**新建**) |
| 源项目 DB(新 Flyway) | `game-cloud/huijing-server/.../db/migration/V18.0.0__create_game_source_project.sql`(**新建**)+ 同名副本入对应模块 `db/migration/`(与 V8/V17 同模式,两处一致) |
| SAA 图布线唯一源 | `game-cloud/.../aigc/saa/SaaStudioGraph.java`(扩节点/边/recursionLimit) |
| SAA 编排节点 + state key | `game-cloud/.../aigc/saa/SaaStudioNodes.java`(ALL_KEYS 扩、新 classify/asset/modify NodeAction) |
| SAA 生成流水节点 | `game-cloud/.../aigc/saa/SaaGenNodes.java`(validate/scaffold/build/play) |
| 派发器(job↔state↔回调桥) | `game-cloud/.../aigc/saa/SaaGraphDispatcher.java`(buildInputs/buildCallbackReqVO/extractTraceQuietly 扩) |
| 回调入参 VO | `game-cloud/.../aigc/.../vo/DifyCallbackReqVO.java`(trace 字段扩 cost/modelTier/cacheHit) |
| studio 编排层 | `game-cloud/game-module-studio/.../service/studio/StudioServiceImpl.java`(+ create/modify/extend 编排) |
| studio 路由 | `game-cloud/.../studio/controller/app/studio/AppStudioController.java`(+ `/studio/{create,modify,extend}`) |
| 确定性构建权威 | `game-cloud/game-module-runtime/.../service/build/RuntimeBuildServiceImpl.java`(桩转真 / 或定义构建实现归属) |
| 执行器配置(救场旋钮) | `game-cloud/.../aigc/service/executor/AigcExecutorProperties.java`(`saaMaxRepairs=5`/`saaMaxPlayerRounds=1` 已在;补 stage/升档阈值) |
### 3.2 引擎+前端线(Mac)
| 关注点 | 路径 |
|---|---|
| 2D 适配器(游戏定义→LittleJS) | `game-runtime/src/`(新增 adapter 层;`scripts/build.mjs` 不动=唯一构建脚本) |
| 唯一构建脚本(共用) | `game-runtime/scripts/build.mjs`(worker 与 SAA build 节点共调,`--global-name=__GameBundle`) |
| 九门 harness(禁重写) | `game-runtime/games/_wg1-gen/_shared/{serve-and-play.sh,play.cdp.cjs,entry-bundle.template.js,index.template.html}` |
| 宿主装载契约 | `game-studio/src/host/{inject.ts,contract.ts,runtime/index.ts}`(消费源不变,零改) |
| 前端创作/修改/预览 UI | `game-studio/src/`(创作工作坊接 `/studio/create`、修改接 `/studio/modify`、预览复用 GamePlayer) |
| Prompt Registry(缓存前缀资产) | `contracts/prompts/`(冻结 system+few-shot 为版本化资产,CI 卡 byte 不变) |
### 3.3 worker(wg1,Python,引擎线复用)
| 关注点 | 路径 |
|---|---|
| 生成编排闭环 | `wg1/gen-worker/worker/run.py`、`wg1/gen-worker/worker/agent_loop/studio.py` |
| prompt 上下文包(缓存前缀来源) | `wg1/gen-worker/worker/prompt.py`(`SYSTEM` 常量 + `_few_shot()` 现从文件读=字节不稳,要固化) |
| 校验门 | `wg1/gen-worker/worker/validate.py` |
| 模型路由(救场阶梯) | `wg1/gen-worker/worker/models.yaml`(stage1/stage1_mmx/stage2) |
| 成本折算(缓存命中字段) | `wg1/gen-worker/worker/cost.py`(现**不计缓存折扣**,`cache_ratio` 已在 `/api/pricing`,要落命中 token) |
---
## 4. 数据流与依赖
### 4.1 全链数据流(create / build / modify)
```mermaid
flowchart TB
subgraph FE["前端线(Mac)"]
U["创作者一句话+六类素材"] -->|POST /studio/create| ST
U2["改一处(换美术/调参/改关卡/改玩法)"] -->|POST /studio/modify| ST
PV["预览(GamePlayer)
window.__GameBundle.bootGameHost"]
end
subgraph BE["后端线(6c6g/mini-desktop)"]
ST["studio 编排层
create/modify/extend"] -->|job(brief/素材/mode/baseVersion/target)| DISP
DISP["SaaGraphDispatcher
job→state"] -->|invoke| G
G["SAA 固定架构图
(§6 拓扑)"] -->|源项目 sourceProject| SRC
SRC[("game_source_project
独立表 · 源 JSON/url")]
SRC -->|确定性构建 API
sourceProject+buildProfile| BUILD
BUILD["唯一确定性构建
esbuild → __GameBundle"] -->|engineBundle/checksum/bundleSize| CB
CB["DifyCallbackService.handleCallback
(唯一写入路径·不变)"] -->|建版本→组包→落包| PKG
PKG[("game_version + game_runtime_package
GamePackage 产物(schema 不变)")]
end
PKG -->|/app-api/runtime/package/{versionId}/manifest| PV
PKG -->|发布审核(B1 链)| FEED["feed 真玩(不变)"]
```
### 4.2 关键依赖与边界
- **源项目 = 新增 additive 存储面**(`game_source_project`);**GamePackage 产物 = 既冻产物面**。二者解耦:源落库成功 → 触发构建 → 构建成功才建 preview package;构建失败 → 源标孤儿、不建包(§5.2 事务)。
- **生成侧(new-api)/收益侧(trade)硬边界**不变(memory `newapi-billing-plane-integration`):缓存命中 token 只落**成本台账**(token 计量),不接收益结算。
- **modify 不动 currentVersion、不自动入 feed**:产物=新预览版,仍走发布审核;失败=不建新版、base 不动(对齐 v2 review §3.2 / 待确认⑤)。
- **classify→archetype 映射表**依赖现 `contracts/templates/*.schema.json`(8 个:clicker/dodge/match/runner/idle/merge/tycoon/generic)作为品类 profile 锚(§5.5)。
---
## 5. 接口/数据契约(8 契约逐个给 schema/签名)
> 原则:**契约 schema 具体到字段**,但不预写无法验证的实现代码。所有 schema 与现行代码兼容性已逐条核对。
### 5.1 契约①:源项目 schema(新 `contracts/agent-loop/source-project.schema.json`)
cheap agents 在固定架构上填的**唯一结构化产物**;Opus 设计、长期版本化升级。
```jsonc
{
"$id": "https://wanxiang.ai/contracts/agent-loop/source-project.schema.json",
"title": "SourceProject",
"type": "object",
"required": ["schemaVersion", "sourceHash", "profile", "gameDefinition"],
"additionalProperties": false,
"properties": {
"schemaVersion": { "const": "1.0" }, // 源项目契约版本(独立于 GamePackage 版本)
"sourceHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, // 源 JSON 规范化后 sha256(可寻址/幂等键)
"buildInputHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, // sha256(sourceHash + buildProfile);构建缓存命中键
"profile": { // 维度无关三维(review §10.2,否则剧情/TRPG 绷断)
"type": "object",
"required": ["tickModel", "inputModel", "progressModel"],
"additionalProperties": false,
"properties": {
"tickModel": { "enum": ["realtime", "turn-based", "event"] },
"inputModel": { "enum": ["continuous", "discrete-choice", "text-command"] },
"progressModel": { "enum": ["metric", "narrative"] } // narrative → 走 narrative-reviewer 质量门(§5.9)
}
},
"gameDefinition": { // 轻量声明式领域模型(非 AAA ECS:无 system 调度器/无 archetype 存储/无 query DSL)
"type": "object",
"required": ["entities", "scenes", "rules"],
"additionalProperties": false,
"properties": {
"entities": { "type": "array", "items": { "$ref": "#/$defs/entity" } },
"components": { "type": "array", "items": { "$ref": "#/$defs/component" } }, // 组件定义(声明式属性:渲染/碰撞/物理)
"behaviors": { "type": "array", "items": { "$ref": "#/$defs/behavior" } }, // 行为模块(逻辑,维度无关,读参数操作实体)
"scenes": { "type": "array", "items": { "$ref": "#/$defs/scene" } }, // 关卡构成(数据驱动)
"rules": { "type": "array", "items": { "$ref": "#/$defs/rule" } } // 胜负条件(数据驱动)
}
},
"assets": { "type": "array", "items": { "$ref": "#/$defs/assetSpec" } }, // 六类资产规格/引用(§5.5 asset 契约写入)
"config": { "type": "object" } // 平衡参数(数值/难度/速度)——数据驱动,确定性 modify 改这里免 LLM
},
"$defs": {
"entity": { // 实体:transform(2D 用 xy/z=0,3D 用 xyz,同字段两维成立)+ 组件引用
"type": "object", "required": ["id", "transform"], "additionalProperties": false,
"properties": {
"id": { "type": "string", "minLength": 1 },
"transform": { "type": "object", "required": ["position"], "properties": {
"position": { "type": "object", "required": ["x", "y"], "properties": {
"x": {"type":"number"}, "y": {"type":"number"}, "z": {"type":"number","default":0} } },
"rotation": { "type": "number" }, "scale": { "type": "number", "default": 1 } } },
"components": { "type": "array", "items": { "type": "string" } } // 引用 components[].id
}
},
"component": { "type": "object", "required": ["id", "kind"], "additionalProperties": true,
"properties": { "id": {"type":"string"}, "kind": { "enum": ["render","collision","physics","custom"] } } },
"behavior": { "type": "object", "required": ["id", "trigger"], "additionalProperties": true,
"properties": { "id": {"type":"string"}, "trigger": { "enum": ["init","update","input","collision","timer"] } } },
"scene": { "type": "object", "required": ["id", "entityRefs"], "additionalProperties": true,
"properties": { "id": {"type":"string"}, "entityRefs": { "type": "array", "items": {"type":"string"} } } },
"rule": { "type": "object", "required": ["id", "condition", "outcome"], "additionalProperties": true,
"properties": { "id": {"type":"string"}, "condition": {"type":"string"}, "outcome": { "enum": ["win","lose","score","advance"] } } },
"assetSpec": { "$ref": "#/$defs/assetSpecBody" },
"assetSpecBody": { "type": "object", "required": ["id", "category", "ref"], "additionalProperties": false,
"properties": {
"id": { "type": "string" },
"category": { "enum": ["sprite","character","effect","scene","ui","music"] }, // 六类(对齐 v2 review C5)
"ref": { "type": "string" }, // 平台 assetId/ref 为主(url 只读镜像)
"url": { "type": "string", "format": "uri" },
"provider": { "type": "string", "default": "mmx-cli" } // provider 可插拔(§5.5)
} }
}
}
```
> **诚实边界**:`gameDefinition.behaviors` 是声明式**模块描述**,真逻辑代码仍由核心代码 agent(logic)产出并经构建编译进 `__GameBundle`;源项目存"可维护的结构化定义",非裸 iife。**MVP 最小集**(对齐 v2 review 待确认①)= profile 三维 + 单 behavior + config + assets 引用,复杂结构随品类迭代。
### 5.2 契约②:源项目 DB 存储(新 Flyway · 独立 `game_source_project` 表)
**勿塞 `game_version`**(架构 review §10 明令);源项目是 additive 新存储面。
```sql
-- V18.0.0__create_game_source_project.sql(Flyway 只新增,接 V17 之后;头注铁律:已合入禁改,回滚写 V18.0.1 补偿)
CREATE TABLE `game_source_project` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '源项目工件 ID',
`game_id` BIGINT NOT NULL COMMENT '所属游戏 ID(project.game_project.id)',
`version_id` BIGINT NULL COMMENT '关联产物版本 ID(project.game_version.id);构建成功建包后回填,失败留 NULL=孤儿',
`source_json` LONGTEXT NULL COMMENT '源项目工件 JSON(SourceProject schema;M0 走 DB,大资产经 ref 外置)',
`source_url` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '源项目对象存储 URL(切对象存储后用,与 source_json 二选一)',
`source_hash` CHAR(64) NOT NULL DEFAULT '' COMMENT '源规范化 sha256(可寻址/幂等;= SourceProject.sourceHash)',
`schema_version` VARCHAR(16) NOT NULL DEFAULT '1.0' COMMENT '源项目契约版本',
`build_profile` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '构建画像(目标引擎/优化档;= buildProfile,影响 buildInputHash)',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '源态:0 草稿 1 已构建 2 孤儿(构建失败) 3 已发布',
`base_version_id` BIGINT NULL COMMENT 'modify 血缘:本源派生自的 base 版本(create 时 NULL)',
`creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID',
PRIMARY KEY (`id`),
KEY `idx_game` (`game_id`, `id`) COMMENT '按游戏查源项目历史',
KEY `idx_version` (`version_id`) COMMENT '按产物版本反查源',
KEY `idx_source_hash` (`source_hash`) COMMENT '可寻址/幂等去重'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '游戏源项目工件表(改源不改产物;与 game_version 解耦)';
```
**事务边界(关键,化解孤儿)**:
```
① studio 编排 → 图产源项目 → 源落库(status=0)成功
② 触发确定性构建(§5.7)
③ 构建成功 → 建 preview package(走 handleCallback 唯一写入路径)→ 回填 version_id + status=1
④ 构建失败 → 源 status=2(孤儿),不建 package,不动 currentVersion
```
源落库与建包**不同一大事务**(构建数十秒级,长事务=连接池杀手,对齐 `SaaGraphDispatcher.java:55-57` 既有纪律);用 status 机器态串联,而非分布式事务。
> **归属待解见 §12-①**:`game_source_project` 物理归 studio 还是 project 模块(都引用 game_id/version_id),DB review 曾提"game_version 双存",与本独立表口径冲突,**本档采架构 review 的独立表口径,归属在 §12 收口**。
### 5.3 契约③:GamePackage 源/产物分离(`game-package.schema.json` 维持产物-only)
**不改 `game-package.schema.json`**。明确边界:
| 面 | 存储 | schema | 可变性 |
|---|---|---|---|
| **源项目(可维护)** | `game_source_project`(新) | `source-project.schema.json`(新,§5.1) | cheap 填、确定性 modify 改 |
| **打包产物(可玩)** | `game_version` + `game_runtime_package` | `game-package.schema.json`(既冻,`additionalProperties:false`) | 构建产出、宿主消费,**不变** |
- `engineBundle` 仍是 GamePackage 的 **additive 可选**产物字段(`game-package.schema.json:94-97`),由构建写入、`handleCallback` 落包;**源项目不进 GamePackage**。
- `additionalProperties` 边界:GamePackage 顶层 `additionalProperties:false` → 源项目字段**禁止**塞进 GamePackage;源项目经独立表存,经 `provenance`(可选)只挂 `sourceHash` 做溯源(若需)。
### 5.4 契约④:SAA state key 全表(`SaaStudioNodes.ALL_KEYS` 扩)
现 ALL_KEYS(已核码 `SaaStudioNodes.java:96-106`)+ 新增键。全 `ReplaceStrategy`(节点 return 覆盖写)。
**现有键(保留,不动语义)**:`brief, enriched, designText, gatespec, gatespecParseError, playSpec, factorySrc, gameId, role, repairCount, playerRound, feedback, playerFeedback, validateOk, buildOk, playPass, verdict, playerPanel, tokensIn, tokensOut, tokensByModel, attempts, status, failureReason, engineBundle`,+ SaaGenNodes 内部键 `gameDir, validateErrors, buildLog`,+ 参数化 `port, cdpPort, runId`。
**新增键(本档锁定)**:
| key | 类型(state 内存形态) | 写节点 | 读/消费 | 语义 |
|---|---|---|---|---|
| `archetype` | String | classify | design/scaffold,映射表 | 品类原型(§5.5 枚举);填错品类比写错码更致命 |
| `tickModel` | String | classify | logic/QA(选质量门机制) | realtime/turn-based/event(profile 三维之一) |
| `inputModel` | String | classify | logic | continuous/discrete-choice/text-command |
| `progressModel` | String | classify | playerRouter / narrative 路由 | metric/narrative;narrative→走 reviewer 门 |
| `sourceProject` | String(JSON 串) | scaffold/logic/config/asset | build/emit/落库 | SourceProject 工件 JSON(§5.1);**新核心产物** |
| `assetSpec` | String(JSON 串) | asset | logic/scaffold | 六类资产规格/引用(§5.5) |
| `modifyMode` | String | render(modify 入口) | modify 路由 | "deterministic"\|"regenerate-module"(§5.6) |
| `modifyPatch` | String(JSON 串) | modify | logic(regenerate)/scaffold(deterministic) | 改一处寻址+载荷(§5.6) |
| `baseVersionId` | String | render | modify/emit | modify 的 base 版本(create 时空) |
| `failCount` | int | playRouter/各失败回边 | okElseRepairOrGiveup/升档判据 | 连续九门失败计数(锚 `verdict.pass=false`) |
| `modelTier` | String | render(初 stage1)/升档节点 | generate/repair 选模型 | "stage1"\|"stage2";救场升档作用面 |
| `escalationEvents` | String(JSON 数组串) | 升档节点 | trace/dump | [{tierBefore,tierAfter,atFailCount,ts}] |
| `giveupDumpPath` | String | giveup | trace/Opus 离线读 | 放弃前完整 dump 落盘路径 |
| `narrativeReviewVerdict` | String(JSON 串) | narrative-reviewer | playerRouter/repair 路由 | narrative 质量门裁决(§5.9) |
> **兼容性**:严格 additive——存量节点不读新键则零影响;`extractTraceQuietly`(`SaaGraphDispatcher.java:448`)best-effort 抽取,新键缺则省略,trace_json 字节兼容(键集仍为 `_extract_trace` 输出超集时需同步扩 worker 侧 `_extract_trace`,见 §8 后端线步骤)。
### 5.5 契约⑤:asset 产消(asset 节点写 `assets[]`,下游消费)
- **生产**:asset 节点据 `archetype`/`gameDefinition` 产六类资产规格,写 `state.assetSpec`(JSON 串)→ merge 进 `sourceProject.assets[]`(§5.1 `assetSpec` 形态)。
- **消费**:logic/scaffold 读 `sourceProject.assets[].ref`,构建时解析为 GamePackage `assets[]`(`game-package.schema.json:34-50`,类型 image/audio/...);宿主经 `assets[].url` 走 `
/drawImage`(inject.ts CSP img-src 已放行)。
- **provider 可插拔**:`assetSpec.provider` 默认 `mmx-cli`(memory `mmx-asset-pipeline`:王蓝莓投资人版默认走 mmx-cli,音乐"记谱→合成"两步);切 provider 只改 asset 节点,不动消费侧。
- **六类枚举一次定死**(对齐 v2 review C5,四处同引):`sprite/character/effect/scene/ui/music`。
- **MVP 边界**:asset 节点 v0 可只产**规格(spec)不真生图**(用 canvas 几何兜底,对齐现 prompt.py 硬规则 7"资产容错"),真资产生成 provider 接入为 additive 升级;但 `assets[]` 产消通道**必须打通**(否则 v2 review §2 "assets[] 空"缺口未补)。
### 5.6 契约⑥:modifyPatch(modify 节点输入/输出)
modify = 在固定结构上改一处 + 重构建;对齐 v2 review §3.2(改源不改产物)。
```jsonc
// modifyPatch(state.modifyPatch JSON 串;modify 节点产)
{
"baseVersionId": "string", // 改谁(base 版本;对应 game_source_project.base_version_id)
"mode": "deterministic | regenerate-module", // 确定性编辑 | LLM 重生成模块
"target": { // 寻址源项目部件
"kind": "asset | config | level | behavior", // 换美术=asset / 调参=config / 改关卡=level(scene) / 改玩法=behavior
"path": "string", // 源项目内 JSON 指针(如 /config/speed、/assets/0、/gameDefinition/scenes/2;config/assets 为顶层,scenes 在 gameDefinition 下,对齐 keystone)
"id": "string" // 或按部件 id 寻址(behaviors[].id / assets[].id)
},
"payload": { // mode=deterministic:新值(直接覆写,免 LLM);mode=regenerate-module:重生成意图(喂 LLM)
"value": {}, // deterministic 用:确定性新值
"intent": "string" // regenerate-module 用:改玩法逻辑的自然语言意图(只重生成那一个 behavior 模块)
}
}
```
**modify 口径(锁定,对齐 v2 review)**:
- **换美术 / 调参 / 改关卡** = `mode=deterministic`:确定性编辑源文件→重构建→新预览版,**免 LLM、秒级**。
- **改玩法逻辑** = `mode=regenerate-module`:LLM **只重生成那个 behavior 模块**(非全量重出),其余部件不动。
- **产物**:新预览版(`game_version` 新行 status=2 预览就绪),**不动 currentVersion、不自动入 feed**,仍走发布审核(B1 链)。
- **失败**:不建新版、base 不动(`game_source_project` 不新增行 / 新增行标孤儿)。
- **manifest 可寻址 diff 已废**:不采用(v2 review supersede)。
### 5.7 契约⑦:唯一确定性构建 API(化解构建权威分裂)
**输入** `sourceProject + buildProfile` → **输出** `engineBundle + checksum + bundleSize + buildLog`。
```jsonc
// 构建 API 契约(语言无关签名;Java 侧 RuntimeBuild、worker 侧 build.mjs 同一实现)
buildSource(input) -> output
// input:
{ "sourceProject": { /* SourceProject §5.1 */ }, "buildProfile": "default" }
// output:
{
"ok": true,
"engineBundle": "string", // iife bundle 全文,顶层全局名 __GameBundle(契约冻结)
"checksum": "string", // 整包 sha256(hex);= GamePackage.manifest.checksum
"bundleSize": 123456, // raw 字节(B1 入场券:raw≤1.5MB,见 generic.schema.json)
"bundleGzBytes": 12345, // gz 字节(B1 入场券:gz≤350KB)
"buildLog": "string" // 失败回喂用;ok=false 时载错因
}
```
**构建权威化解(本档裁定,见 §12-②)**:
- **推荐构建实现留 worker/编排器侧 esbuild**(`game-runtime/scripts/build.mjs` `--global-name=__GameBundle`,现已真跑、九门验过),**SAA build 节点与 worker 共调同一 `scripts/build.mjs`**(`SaaGenNodes.buildNode`/`run.py:build` 已是同脚本同参,**已天然统一**,无需第二实现)。
- **后端 `RuntimeBuildServiceImpl` = 落包+manifest 权威**(非第二构建器):接收构建产物(engineBundle/checksum/bundleSize)→ 经 `handleCallback` 落 `game_runtime_package` + 回填 `game_version` 三字段。当前它是桩(`RuntimeBuildServiceImpl.java:51` TODO),本期**不要求它自己跑 esbuild**,只要求它消费构建产物落包——**构建执行权在 worker/SAA build 节点,落包权在后端**,职责单一、不分裂。
- **确定性**:同 `buildInputHash`(sourceHash+buildProfile)→ 字节等价产物(v2 review M0 验收"非一次性"=从源重新构建出字节等价 bundle);锁引擎版本/依赖(`game-runtime/package.json` + esbuild 锁参)。
### 5.8 契约⑧:trace+cost 字段(扩 `DifyCallbackReqVO.trace` + SAA trace 抽取)
现 `trace`(9d 账本,`DifyCallbackReqVO.java:68-75`,落 `game_aigc_task.trace_json`)扩字段:
```jsonc
// trace(扩;snake/camel 与现 _extract_trace 口径一致;additive,缺则省略键)
{
// —— 现有(保留)——
"pass": true, "repairs": 0, "wallS": 12.3, "models": {}, "attempts": [],
"gameId": "", "stage": "saa", "gatespec": {}, "sevenGateVerdict": {}, "tokens": { "prompt": 0, "completion": 0 }, "player": {},
// —— 新增(本档锁定)——
"modelTier": "stage1", // 终态模型档(救场升档后为 stage2)
"escalationEvents": [ // 升档事件(state.escalationEvents)
{ "tierBefore": "stage1", "tierAfter": "stage2", "atFailCount": 5, "ts": "ISO8601" }
],
"cacheHit": { // 缓存命中(成本台账;命中 token 落账)
"promptCacheHitTokens": 0, // 取 max(DeepSeek prompt_cache_hit_tokens, MiniMax prompt_tokens_details.cached_tokens)
"source": "deepseek | minimax | none" // 命中来源(字段口径见下)
},
"giveupDumpPath": "string", // 放弃前完整 dump 落盘路径(state.giveupDumpPath;Opus 离线读)
"cost": { // F3 折¥(SAA 路现只 token,本期补折¥见 §8;非阻断)
"totalRmb": 0.031, "gateRmb": 0.15, "byModel": {}
}
}
```
**缓存命中检测(兼容两套字段,锚 review §1.5)**:
- **DeepSeek**:`usage.prompt_cache_hit_tokens`。
- **MiniMax**:`usage.prompt_tokens_details.cached_tokens`。
- **取 max** 落 `cacheHit.promptCacheHitTokens`;命中 token 进**成本台账**(`cost.py` 现不计缓存折扣 `cost.py:10-12`,本期补:读 `/api/pricing` 的 `cache_ratio` + 命中 token 折真实 quota,落 trace.cost,**不阻断、仅观测**)。
### 5.8.1 缓存策略(三段式 prompt + Prompt Registry 资产)
- **三段式**(review §10.3,spike 已证绿):**固定前缀(契约/few-shot/能力面,永不变)→ 共享中段(GDD)→ 可变后缀(brief/反馈)**。现 `prompt.py:SYSTEM` 已是固定前缀骨架,但 `_few_shot()` **从文件读 → 字节不稳**(缓存命中要求前缀 byte 完全一致)。
- **动作(本档锁定)**:把 `SYSTEM` + few-shot 冻结为**版本化 Prompt Registry 资产**(`contracts/prompts/`,现有 `registry.yaml` + 04-config/07-fix 等),**CI 卡 byte 不变**(few-shot 从文件读改为读已固化的版本化常量,字节固定)。
- **L1 架构契约前缀先做**(review §1.5:最大块,第二款起省~90% input)。
- **✅ 缓存透传已 spike 证绿(2026-06-17)**:deepseek-v4-flash / MiniMax-M3 / M2.7 经 new-api 网关前缀缓存**全部透传**,第二款起命中 **76-93%**;检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens` 取 max)。**缓存策略坐实、风险解除**;实施只剩"冻结 few-shot 字节稳 + 落命中台账"工程动作(B9/E5)。
### 5.9 narrative 子系统(创始人裁定保留 MVP · 条件激活)
`progressModel=narrative` 分支由 **narrative-designer-reviewer** 当质量门(替代九门——九门确定性测不了叙事连贯)。
```jsonc
// narrativeReviewVerdict(state.narrativeReviewVerdict JSON 串;narrative-reviewer 产)
{
"problems": [ "string" ], // 叙事连贯/设计问题列表
"needsRepair": true, // 是否需回 repair(true→走 repair 路,消费同 playerFeedback 范式)
"founderFinal": false // 终判留创始人(false=未终审;质量门按品类换机制,叙事类终判人工)
}
```
**接入(锁定)**:
- **路由**:`progressModel=narrative` → play 节点后**不走九门 playRouter,改走 narrative-reviewer**;`needsRepair=true` → repair(同 `repairNode` 自增 repairCount + role=fix);`needsRepair=false` → emit。
- **失败计入救场 failCount(§12-⑤ 收口点)**:九门对 narrative 失效,故 narrative 的 `needsRepair=true` **等价一次"九门失败"计入 `failCount`**(同 5+3 阶梯:连续 5 次 needsRepair→升档、再 3 次→giveup);`recursionLimit` 同口径重算。
- **follow-up**:产品需求清单需补一条 **narrative P-id 与验收**(标注待补,见 §11)。
---
## 6. 最终拓扑表(无孤儿节点 · recursionLimit 按救场 5+3 重算)
### 6.1 拓扑图(SAA 固定架构图)
```mermaid
flowchart TB
START([START]) --> render
render --> classify
classify --> design
design --> generate["generate(logic 写 / role=fix 时 repair 修=同一核心代码 agent 两面)"]
generate --> validate
validate -->|ok| scaffold
validate -->|fail & failCount<5| repair
validate -->|fail & 升档窗| escalate
scaffold --> asset
asset --> build
build -->|ok| play
build -->|fail| repair
play -->|metric & 九门 pass| player
play -->|narrative 路| nreview["narrative-reviewer"]
play -->|fail & failCount<阈| repair
player -->|无体验问题/轮尽| emit
player -->|有体验问题| repair
nreview -->|needsRepair=false| emit
nreview -->|needsRepair=true & 轮未尽| repair
repair --> generate
escalate["escalate(升档 stage1→stage2)"] --> generate
emit --> END([END])
giveup["giveup(完整 dump)"] --> END
play -.->|failCount≥5 & 已 stage2 再失败 3| giveup
modify["modify(确定性编辑/regenerate-module)"] -.->|确定性路| build
modify -.->|regenerate-module 路| generate
```
### 6.2 逐节点拓扑表(入边/出边/读写 state/消费者/路由条件)
| 节点 | 入边 | 出边(路由条件) | 读 state | 写 state | 消费者 |
|---|---|---|---|---|---|
| **render** | START / modify 入口 | →classify(create)/→generate(modify regenerate)/→build(modify deterministic) | brief, modifyMode, baseVersionId | role=code, repairCount=0, playerRound=0, failCount=0, modelTier=stage1, tokensIn/Out=0 | classify/modify |
| **classify(新)** | render | →design | brief | archetype, tickModel, inputModel, progressModel | design/scaffold/logic/QA;映射表 |
| **design** | classify | →generate | brief, archetype | designText, enriched, playSpec, gatespec, gatespecParseError | generate |
| **generate(logic+repair 两面)** | design / repair / escalate | →validate | role, modelTier, enriched, feedback | factorySrc, sourceProject(填 behaviors/config), feedback, tokensByModel, attempts | validate |
| **validate** | generate | ok→scaffold;fail→repair(failCount<5)/escalate(升档窗) | factorySrc | validateOk, validateErrors, feedback | scaffold/repair |
| **scaffold** | validate(ok) | →asset | gameId, factorySrc, playSpec, sourceProject | gameDir, sourceProject(落盘结构) | asset/build |
| **asset(新)** | scaffold | →build | archetype, sourceProject | assetSpec, sourceProject.assets[] | build/logic;provider=mmx-cli |
| **build** | scaffold/asset / modify(确定性) | ok→play;fail→repair | gameId, gameDir, sourceProject, buildProfile | buildOk, buildLog, engineBundle, checksum, bundleSize | play/emit |
| **play(QA 九门)** | build(ok) | metric&pass→player;narrative→nreview;fail→repair/giveup | gameId, port, cdpPort, playSpec, progressModel | playPass, verdict, feedback, failCount(++) | player/nreview/repair |
| **player(软门)** | play(metric pass) | 无问题/轮尽→emit;有问题→repair | gameId, brief, verdict | playerPanel, playerFeedback | playerRouter/emit |
| **nreview(narrative,条件)** | play(narrative 路) | needsRepair=false→emit;true&轮未尽→repair | brief, sourceProject, verdict | narrativeReviewVerdict, failCount(++) | emit/repair |
| **repair** | validate/build/play/player/nreview 失败回边 | →generate | repairCount, playerFeedback | role=fix, repairCount++, playerRound++(软门), feedback | generate |
| **escalate(新)** | 失败回边(failCount 命中升档阈) | →generate | failCount, modelTier | modelTier=stage2, escalationEvents+= | generate |
| **emit** | player/nreview(收口) | →END | gameId(bundle), sourceProject | status=succeeded, engineBundle | handleCallback |
| **giveup** | 救场耗尽(stage2 再失败 3) | →END | feedback, attempts(全), sourceProject | status=failed, failureReason, giveupDumpPath(完整 dump) | handleCallback/Opus 离线 |
| **modify(新)** | render(modify 入口) | 确定性→build;regenerate-module→generate | modifyPatch, baseVersionId, sourceProject(base) | sourceProject(改一处), modifyMode | build/generate |
### 6.3 救场阶梯 + recursionLimit 重算
- **锚**:`verdict.pass=false`(确定性九门,非主观;`SaaGenNodes.playNode:409` `rc==0 ∧ verdict.pass`)。
- **阶梯**:cheap(stage1)→**连续 5 次九门失败 → escalate 升档(stage1→stage2)**→ stage2 **再 3 次失败 → giveup**。
- **state 驱动**:`failCount` 在每次九门(及 narrative `needsRepair`)失败 +1;`okElseRepairOrGiveup`/`playRouter` 判据改为:
- `failCount < 5` → repair(stage1);
- `failCount == 5 ∧ modelTier==stage1` → escalate(升 stage2,failCount 续计或重置由实现定,**建议续计**:stage2 阈 = 5+3=8);
- `failCount ≥ 8`(stage2 再 3 次)→ giveup。
- **recursionLimit 重算**:现 `recursionLimitFor(maxRepairs) = (maxRepairs+1)*7+78`(`SaaStudioGraph.java:221`)。救场总轮 = 5(stage1)+3(stage2)=8 主轮,每轮主链 ≈ 9 节点(render→classify→design→generate→validate→scaffold→asset→build→play),+ 升档/repair 回环。**新公式建议**:`recursionLimitFor = (5+3+1)*10 + 余量` ≈ `90+余量`,确保正常收口不误触超步(余量保守取 ≥30)。`AigcExecutorProperties.saaMaxRepairs` 由 5 升为可配,新增 `saaStage2ExtraRepairs=3`。
- **全程留存(硬观测,非可选)**:每编辑步 + 日志留存;`giveup` 前**完整 dump**(所有 attempts 源码/verdict 整包落盘 → `giveupDumpPath`),喂 Opus 离线分析。
### 6.4 classify→archetype 映射表(避免孤儿分类,锚 review §10.1)
`archetype` 枚举把"消除族"拆细(review 明令);映射到现 `contracts/templates/*.schema.json` 品类 profile:
| archetype | tickModel | inputModel | 品类 profile 锚(现模板 schema) |
|---|---|---|---|
| `clicker` | event | discrete-choice | `templates/clicker.schema.json` |
| `dodge` | realtime | continuous | `templates/dodge.schema.json` |
| `runner` | realtime | continuous | `templates/runner.schema.json` |
| `bubble`(瞄准发射) | realtime | continuous | `templates/match.schema.json`(瞄准变体) |
| `match3`/`line-clear`(点选) | turn-based | discrete-choice | `templates/match.schema.json` |
| `merge` | event | discrete-choice | `templates/merge.schema.json` |
| `idle` | event | discrete-choice | `templates/idle.schema.json` |
| `tycoon` | turn-based | discrete-choice | `templates/tycoon.schema.json` |
| `generic`(兜底) | * | * | `templates/generic.schema.json` |
- **物理优先分类**(OpenGame Physics-First,review §10.5):classify 先定 tickModel(spike 已证 100%)→ 再定 archetype(93%);可选 DeepSeek 副模型双跑兜底。
- **映射表是契约**:classify 出 archetype 必落到此表的一行(否则=孤儿分类);新增 archetype 必须同时补本表 + 对应 profile 锚。
---
## 7. 关键实现步骤(按两线)
> 顺序:**先冻结 8 契约 → 两线并行**。契约冻结前任一线不得开工(契约先行铁律)。
### 7.0 契约冻结(联合,开工门)
1. 写 `contracts/agent-loop/source-project.schema.json`(§5.1)。
2. 写 `V18.0.0__create_game_source_project.sql`(§5.2,两处副本)。
3. 扩 `SaaStudioNodes.ALL_KEYS` 新键(§5.4)。
4. 扩 `DifyCallbackReqVO.trace` 字段口径(§5.8)。
5. 收口 §12 五个待解项(尤其①归属、②构建权威、⑤narrative failCount)。
6. 两轮评审已过(review 版),实现版按 memory `impl-review-one-opus-round` **单轮 opus 评审**。
### 7.1 后端线(6c6g 编辑 / mini-desktop 构建)
| # | 步骤 | 文件 | 验证 |
|---|---|---|---|
| B1 | 源项目 schema + DB | `source-project.schema.json`、`V18.0.0__*.sql` | Flyway 启动迁移绿(mini-desktop) |
| B2 | SAA 图扩 classify/asset/modify/escalate/nreview 节点 + 边 + recursionLimit 重算 | `SaaStudioGraph.java`、`SaaStudioNodes.java` | `SaaStudioGraphTest` 回归绿 + 拓扑无孤儿 |
| B3 | state key 全表落地(新键 + KeyStrategy 注册) | `SaaStudioNodes.java:ALL_KEYS` | 单测:全键 ReplaceStrategy 注册 |
| B4 | 救场阶梯 failCount/modelTier 路由 + giveup 完整 dump | `SaaStudioNodes.java`(playRouter/okElseRepairOrGiveup/giveupNode) | 单测:5 失败升档、8 失败 giveup + dump 落盘 |
| B5 | trace+cost 扩(modelTier/escalationEvents/cacheHit/giveupDumpPath)+ worker 侧 `_extract_trace` 同步扩 | `SaaGraphDispatcher.java:extractTraceQuietly`、`service.py:_extract_trace` | 字节兼容:`ReadinessScorer` 两路同读 |
| B6 | studio 编排 + /studio/{create,modify,extend} | `StudioServiceImpl.java`、`AppStudioController.java` | 单测 + 编译绿;modify 出新预览版不动 currentVersion |
| B7 | modifyPatch 契约接线(确定性编辑路 / regenerate-module 路) | `StudioServiceImpl.java`、`SaaStudioNodes.modifyNode` | 单测:确定性免 LLM、regenerate 只重一模块 |
| B8 | 源落库事务边界(源→构建→建包,失败标孤儿) | `StudioServiceImpl.java` / 落库服务 | 单测:构建失败源 status=2、不建包 |
| B9 | 缓存命中字段落账(`cache_ratio` + 命中 token 折¥) | `cost.py`、`SaaGraphDispatcher` | 单测:两套字段取 max、落 trace.cost |
### 7.2 引擎+前端线(Mac)
| # | 步骤 | 文件 | 验证 |
|---|---|---|---|
| E1 | 2D 适配器(游戏定义→LittleJS 渲染):SourceProject.gameDefinition → 可玩 | `game-runtime/src/`(adapter 层) | 单测:实体→sprite、behavior→逻辑 |
| E2 | 唯一构建实现核对(SAA build 节点与 worker 共调 `scripts/build.mjs`) | `game-runtime/scripts/build.mjs`(不动) | 同源同参:`__GameBundle` 产出一致 |
| E3 | asset 产消(asset 节点 spec → assets[] → 宿主消费)+ provider=mmx-cli 插桩 | `game-runtime/` + worker asset 角色 | 九门:assets[] 非空、宿主加载 |
| E4 | 九门 QA harness 复用(禁重写)+ narrative 路质量门接 reviewer | `_shared/*`(harness 不动)+ reviewer 角色 | 九门真玩绿(mini-desktop) |
| E5 | Prompt Registry 冻结 few-shot(byte 稳)+ CI 卡 byte | `contracts/prompts/`、`prompt.py` | CI:few-shot byte 不变 |
| E6 | 前端创作(接 /studio/create)/ 修改(接 /studio/modify)/ 预览 UI | `game-studio/src/` | 真机 UI 走查(mini-desktop CDP) |
### 7.3 缓存透传 spike ✅ 已完成(绿)
- **结果(2026-06-17)**:deepseek-v4-flash / MiniMax-M3 / M2.7 经 new-api 网关前缀缓存**全部透传成立**,第二款起命中 **76-93%**。
- **落地口径**:检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens`,取 max);命中 token 落成本台账。
- **结论**:缓存策略(三段式 + Prompt Registry 冻结)坐实,**不再是前置阻断项**;实施只剩"冻结 few-shot 字节稳(B9/E5)+ 落命中台账(B9)"。
---
## 8. 两线切分与交汇(谁锁谁)
```mermaid
graph LR
subgraph 契约交汇["8 契约(开工前冻结=两线交汇面)"]
C1["①源项目 schema"]; C2["②源项目 DB"]; C3["③源/产物分离"]; C4["④SAA state 全表"]
C5["⑤asset 产消"]; C6["⑥modifyPatch"]; C7["⑦唯一构建 API"]; C8["⑧trace+cost"]
end
BE["后端线(6c6g/mini-desktop)
锁:C1·C2·C4·C6·C8 + SAA 拓扑/救场/studio 编排"] --> 契约交汇
EN["引擎+前端线(Mac)
锁:C1(消费)·C5·C7(构建实现)·C3(产物消费) + 2D 适配器/九门 QA/前端 UI"] --> 契约交汇
```
- **后端线主锁**:①②④⑥⑧(源项目 schema/DB、SAA state/拓扑/救场路由、studio 编排、modifyPatch、trace+cost)。
- **引擎+前端线主锁**:①(消费)⑤⑦(构建实现)③(产物消费)+ 2D 适配器、asset 产消、九门 QA、前端创作/修改/预览 UI。
- **共锁**:① 源项目 schema(后端写、引擎消费)、⑦ 构建 API(引擎实现 esbuild、后端落包)。
- **并行边界 = 模块边界**(memory `prefer-parallel-over-serial`):零共享可变态,worktree 隔离,只对真独占资源(九门端口 4320/9222)串行。
---
## 9. 边界失败路径
| 失败 | 处置 | 出处 |
|---|---|---|
| 源落库成功但构建失败 | 源 status=2(孤儿),不建 package,不动 currentVersion | §5.2 事务 |
| 构建产 bundle 缺 `__GameBundle` 全局名 | emit 判 failed(`bundle_missing_global_name`),不落坏包 | `SaaStudioNodes.emitNode:666` |
| LLM 调用超时/429/5xx | 单次 180s 超时 + ≤3 重试,超限不裸退图(写 feedback) | `SaaStudioNodes.callWithTimeoutAndRetry:154` |
| 九门连续失败 | failCount++ → 5 升档 → 8 giveup + 完整 dump | §6.3 |
| narrative needsRepair 连续 true | 同九门 failCount 阶梯(§5.9/§12-⑤) | §5.9 |
| 缓存前缀不透传(spike 已证透传,仅理论兜底) | 命中字段缺则按 miss 计价、生成不阻断(仅成本回退) | §7.3 ✅ |
| modify 失败 | 不建新版、base 不动 | §5.6 |
| classify 出未知 archetype | 落 generic 兜底 + 告警(不污染下游) | §6.4 |
| trace 抽取异常 | best-effort 吞+warn,trace_json 留 NULL,不阻断生成 | `SaaGraphDispatcher.java:497` |
| 图终态空(超步等) | 兜底 failed(llm_error) 回调,真因留日志 | `SaaGraphDispatcher.java:376` |
---
## 10. 验证方法(含两线各自验收门)
### 10.1 后端线验收门
1. **编译 + 单测绿**:`SaaStudioGraphTest`(图布线回归)、`SaaStudioNodesRegressionTest`、新 classify/asset/modify/救场单测。
2. **Flyway 迁移绿**(mini-desktop):V18 迁移成功,`game_source_project` 建表。
3. **救场阶梯单测**:5 失败升档(escalationEvents 落)、8 失败 giveup(giveupDumpPath 落盘+完整 dump)。
4. **trace 字节兼容**:`ReadinessScorer` 在 SAA 路与 worker 路同读 `pass/gatespec.driver/sevenGateVerdict.guards/repairs`(`SaaGraphDispatcher.java:424` 口径)。
5. **modify 局部性单测**:确定性编辑免 LLM、regenerate-module 只重一 behavior、不动 currentVersion。
### 10.2 引擎+前端线验收门
1. **2D 适配器单测**:SourceProject.gameDefinition → 可玩(实体→sprite、behavior→逻辑)。
2. **九门真玩绿**(mini-desktop,端口 4320/9222):cheap 在固定架构填出过九门游戏,`verdict.pass=true`。
3. **asset 产消门**:`sourceProject.assets[]` 非空 → GamePackage `assets[]` → 宿主加载(非空 assetContext)。
4. **构建确定性门**(v2 review M0):同 `buildInputHash` → 字节等价 bundle(从源重新构建可复现)。
5. **前端真机 UI 走查**(memory `frontend-link-ui-walk-publish-fix`:编排器旁路掩盖 UI 缺陷 → 真 UI 走查唯一手段):create/modify/预览闭环。
### 10.3 联合 spike 门(决定架构成不成立)
1. **缓存透传 spike ✅ 已绿**(§7.3):deepseek-v4-flash/M3/M2.7 经 new-api 透传成立(76-93% 命中)。
2. **classify 准确率 spike ✅ 已绿**(§6.4):M3 tickModel 100%/archetype 93%、$0.003/次,便宜分类不污染下游;唯一改进=archetype 枚举拆"消除族"(bubble vs match3/line-clear)。
3. **端到端门(待实施后验)**:一句话 → studio 填固定架构 → 源项目 → 构建 → 九门过门 → GamePackage → feed 真玩;成功率(对齐 MVP ≥80%)+ 单款 < $1(¥0.15 门)+ modify 局部性。
---
## 11. 完成条件(Definition of Done)
1. **8 契约全冻结**(schema/DB/state/构建 API/trace 全落地且评审过)。
2. **后端线**:SAA 固定架构图扩节点全绿(单测+Flyway),救场阶梯/giveup dump/trace+cost 可验,studio create/modify/extend 编排通。
3. **引擎+前端线**:2D 适配器可把游戏定义渲成可玩,九门真玩绿,asset 产消通,构建确定性(字节等价),前端创作/修改/预览真机走查过。
4. **联合 spike 三门过**:缓存透传成立、classify 准确、端到端成功率+成本+modify 局部性达标。
5. **无孤儿设计**(AGENTS §7):每节点有入边/出边/消费者(§6.2);每新数据结构有产消通道。
6. **follow-up 登记**:
- 产品需求清单补 **narrative P-id 与验收**(§5.9)。
- 3D 适配器(Phase 2)、Template Skill(冷路径晋升)登记为远期。
- `game_source_project` 物理归属模块最终落定(§12-①)。
7. **知识回写**(AGENTS §7):新增 skill `fixed-architecture-saa-studio`(固定架构填槽+救场阶梯+缓存前缀+classify 映射),更新 `.agents/README.md` 索引;收口走 wave-close-checklist 8 步。
---
## 12. 开工前待解(诚实列出 · 现行代码与设计未解冲突)
> 以下是我判断**现行代码/文档与本设计存在未解冲突或需创始人/两线收口的点**,不糊过去。开工门(§7.0)须逐条收口。
>
> **✅ 全部待解经创始人 2026-06-17「按推荐」确认收口**(本节转为已收口记录):① 源项目表归属 = **studio 模块**(create/modify 编排入口在此;project 仍管 game_version 产物权威)· ② 后端 `RuntimeBuildServiceImpl` 本期**只接"落包"**,不自驱 esbuild(构建留 worker/SAA `build.mjs`)· ③ 缓存透传 **✅ spike 已证绿**(76-93%)· ④ classify→archetype 映射表语义 = **引导(品类预设)非校验**(对齐"玩法模板未废、游戏模板已废")· ⑤ narrative 失败 = **`needsRepair=true` 等价一次九门失败计入 `failCount`**(reviewer 自身抖动也计,防卡死)· ⑥ studio `create` = **`draft+generate` 一步式封装(additive,不破现两步路)**。**契约冻结进行中**:keystone `contracts/agent-loop/source-project.schema.json` 已落;V18 SQL + ALL_KEYS 扩 + trace 扩 = 后端线 module 实现(V18 入 game-cloud 模块迁移,**非 stale 的 `contracts/db-schemas/`〔已停于 V9〕**)。
### ① 源项目存储权威归属(DB review 与架构 review 口径冲突)
- **冲突**:v2 生命周期 review §5 说"`game_version` 内容从打包产物**升级为源项目工件+构建产物双存**";架构 review §10 + 本档定"**独立 `game_source_project` 表,勿塞 game_version**"。两者矛盾。
- **本档裁定**:采**独立表**(改源不改产物的关键=源/产物物理解耦,塞 game_version 会破 GamePackage 产物纯净 + 既冻 schema)。
- **待收口**:`game_source_project` 物理归 **studio 模块**(它是编排层、已持 game_id/version_id 引用,V8 错误码段 112)还是 **project 模块**(版本台账权威)?**倾向 studio 模块**(create/modify 编排入口在此,源项目是其编排产物;project 仍管 game_version 产物权威)。需创始人/两线拍。
### ② 构建权威分裂(worker 私有 esbuild vs 后端 RuntimeBuild 桩)
- **现状(已核码)**:worker `run.py:build`/SAA `SaaGenNodes.buildNode` **已共调** `game-runtime/scripts/build.mjs`(同脚本同参,**已天然统一**);后端 `RuntimeBuildServiceImpl.triggerCompile` 是**桩**(`:51` TODO,只落排队不跑编译)。
- **本档裁定(§5.7)**:**构建执行权 = worker/SAA build 节点的 `scripts/build.mjs`**(已真跑、九门验过,不另造第二实现);**后端 RuntimeBuild = 落包+manifest 权威**(消费构建产物落 `game_runtime_package` + 回填 `game_version`,不要求它自跑 esbuild)。
- **待收口**:`RuntimeBuildServiceImpl` 桩的 TODO 是否本期转真?**建议本期只接"消费构建产物落包"路**(经 handleCallback 已通),不强求它自驱 esbuild 执行引擎(那是渠道线/未来 CI 化才需)。需确认范围。
### ③ 缓存前缀经 new-api 网关透传 ✅ 已 spike 证绿(已解除,非待解)
- **结果(2026-06-17 spike)**:deepseek-v4-flash/M3/M2.7 经 new-api 网关前缀缓存**全部透传**,命中 76-93%;检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens`)。
- **classify 准确率 spike 同绿**:M3 tickModel 100%/archetype 93%、$0.003/次。
- **剩工程动作**(非待解):冻结 few-shot 字节稳(B9/E5)+ 命中 token 落账(B9)。
### ④ classify→archetype 映射表必须与现 8 模板 schema 对齐(否则孤儿分类)
- **现状**:`contracts/templates/` 现有 8 schema(clicker/dodge/match/runner/idle/merge/tycoon/generic),但**语义已变**(`generic.schema.json` 注:P3 走引擎 bundle 路,校验 bundle 形态而非 GameConfig 玩法字段)。
- **待收口**:§6.4 映射表把 archetype 锚到这些 schema 作"品类 profile 引导",但旧模板 schema 校验对象(GameConfig)在 bundle 路已退场——映射表用它们做**引导 prompt/脚手架预设**(玩法模板=品类框架,非 pre-built 代码,对齐 memory `tiered-engine-cocos-decision` 2026-06-17 术语纠偏:废的是"游戏模板"、"玩法模板未废"),**非填参执行器**。需确认映射表语义=引导而非校验。
### ⑤ narrative 失败如何计入救场 failCount(九门对它失效)
- **冲突点**:救场阶梯锚 `verdict.pass=false`(九门),但 narrative 路**不走九门**(走 reviewer)。若不定义,narrative 路无升档/giveup 终止条件 → 可能死循环。
- **本档裁定(§5.9)**:narrative `needsRepair=true` **等价一次九门失败计入 failCount**(同 5+3 阶梯);`recursionLimit` 同口径。
- **待收口**:确认此口径(narrative reviewer 的 needsRepair 作为 failCount 源);+ narrative reviewer 自身失败(LLM 调用失败)是否也计 failCount(建议计,避免 reviewer 抖动卡死)。
### ⑥ studio 路由 create/modify/extend 部分净新(现只有 draft/generate/task/regenerate)
- **现状(已核码)**:`AppStudioController` 现有 `/studio/{template/list,draft,generate,task/{id},task/{id}/regenerate}`;**无** `/create`(统一入口)/`/modify`/`/extend`。
- **待收口**:v2 review C3 的 `/studio/{create,modify,extend}` 是**净新增**;`create` 与现 `draft+generate` 关系=统一入口归一(generic 缺省,对齐 `7534bdf9` 一句话入口),还是并存?建议 `create` = `draft+generate` 的一步式封装(additive,不破现有两步路)。
---
## 13. 回滚策略
| 变更 | 回滚 |
|---|---|
| Flyway V18(`game_source_project`) | 写 **V18.0.1 补偿迁移 DROP TABLE**(不改 V18,头注铁律);additive 表删除不影响 GamePackage 产物链 |
| SAA 图新节点(classify/asset/modify/escalate/nreview) | 新节点 additive;`SaaStudioGraph.assemble` 布线唯一源,删新节点+边即回现 11 节点图;state 新键不读则零影响 |
| 救场阶梯(failCount/升档) | `AigcExecutorProperties` 开关:`saaStage2ExtraRepairs=0` → 退回单档 5 轮(现行);recursionLimit 回旧公式 |
| trace+cost 扩字段 | additive(`DifyCallbackReqVO.trace` Map):删字段口径,缺则省略键,trace_json 字节回旧 |
| modify 编排 + /studio/{create,modify,extend} | 新路由 additive;下线路由即回现 draft/generate/regenerate;modify 不动 currentVersion=天然安全 |
| 缓存 Prompt Registry 冻结 | few-shot 回从文件读(`_few_shot()`);缓存命中字段不落账=现行(cost 不计缓存折扣) |
| 2D 适配器 | 引擎线产物;现 worker 直产 iife 路保留=回退路 |
> **核心安全垫**:GamePackage 产物 schema / 宿主装载契约 / 回调唯一写入路径 / 九门 harness **全不变** → 任一新增失败,生成主线可回退到现 W-G1 worker 直产 bundle + 现 11 节点 SAA 图,B1 发布链/feed 真玩零回归。
---
## 14. 知识回写(收口时执行,AGENTS §7)
- **skills/**:新增 `fixed-architecture-saa-studio.md`(固定架构填槽范式 + 救场 5+3 阶梯 + 三段式缓存前缀冻结 + classify→archetype 物理优先映射 + modify 改源不改产物);更新 `saa-graph-orchestration.md`(新节点/state 键/recursionLimit 重算)。
- **knowledge/**:`tech-decisions.md` 补"固定游戏架构=轻量声明式领域模型(非 AAA ECS)+ 2D 适配器(LittleJS)/ 3D Phase2";`glossary.md` 补 SourceProject/archetype/profile 三维/救场阶梯/escalate。
- **rules/**:补红线"源项目独立 `game_source_project` 表,勿塞 game_version;GamePackage 产物 schema 不变"。
- **更新 `.agents/README.md` 索引 + `docs/agent-specs/_index.md` 活地图**(本档登记 ACTIVE)。