---
topic: 契约总览
canonical: true
date: 2026-06-18
---
# 契约总览
> **这是什么**:绘境AI 全部契约族的导航与口径收口表 —— 有哪几类契约、各落在哪、各算"第几"、怎么镜像同步到代码、防漂移门到底有没有。
> **给谁看**:跨仓对接的工程师、做契约变更与评审的人、第一次 `ls contracts/` 想搞清楚目录里这堆 schema 各是什么的新人。
字段级定义不在这里 —— 它们在各 `.schema.json` / `.d.ts` / `-api` 包,以及生成主线 execution §5。本档只给指针,负责回答"到底有几类、各算第几"。导航起点是 [`docs/agent-specs/_index.md`](../../agent-specs/_index.md) 活地图与 [`contracts/README.md`](../../../contracts/README.md)。
## 契约全景
绘境AI 的契约分布在三个物理落点,落点本身就决定了一类契约的归属和拆仓时的命运。
跨三仓五工位共享的业务契约落 `contracts/` 顶层,是 Day-0 全员锁定的单一事实源:任何接口、数据结构、事件、SDK 签名改动,先改这里、再写代码。game-runtime 内部的两类接线契约不落 `contracts/`,落 `game-runtime/src/` —— 它们只在 game-runtime 内被消费,落 `contracts/` 会让跨仓 SSOT 反向相对 import `../game-runtime/src`,拆仓即断(理由整段写在两个 `.d.ts` 头注释里,见下文)。Java 侧则各模块一份 `-api` 包,是 `contracts/` 的代码镜像,后端先写它的 VO/DTO、前端据此定 TS。
```mermaid
flowchart TB
subgraph C["contracts/ 顶层 · 跨三仓五工位 SSOT"]
C1["跨仓 8 类
API yaml · DB Flyway · SDK · GamePackage
events · Dify(废) · ad-slot · prompts"]
C2["agent-loop/ · 生成 QA 闭环 + 源项目 keystone"]
C3["templates/ · 13 个品类/形态 schema"]
end
subgraph R["game-runtime/src/ · 内部接线面 · 不落 contracts(拆仓即断)"]
R1["第 9 类 game-host.d.ts
游戏宿主装载契约"]
R2["第 10 类 runtime-api-2d.d.ts
运行时访问约定 rt"]
end
subgraph A["game-cloud/game-module-*/*-api · Java 镜像"]
A1["VO / DTO / 错误码常量
(后端先写 → 前端据此定 TS)"]
end
C1 -->|后端镜像| A1
C2 -->|生成线产出落库 · 引擎线消费| R2
R1 --> R2
```
下面这张表是"到底有几类"的唯一答案。状态列如实标"已建 / ACTIVE / 废 / future 占位"。
| 契约族 | 文件 / 落点 | owner | 产方 → 消方 | 状态 |
|---|---|---|---|---|
| **跨仓 #1 API** | `contracts/api-schemas/*.yaml`(12 个:aigc/ad/biz/community/compliance/feed/passport/project/runtime/studio/telemetry/trade) | WS1 lead | 后端定义 → 前端 mock / 各模块互调 | 已建 |
| **跨仓 #2 DB** | `contracts/db-schemas/V*.sql` | WS1 | 后端各模块 | 已建(镜像已漂移,见下节) |
| **跨仓 #3 SDK** | `contracts/sdk-interface.d.ts` | WS3 | game-studio 宿主 ↔ 游戏侧 postMessage | 已建 |
| **跨仓 #4 GamePackage** | `contracts/game-package.schema.json` + `contracts/game-artifact-manifest.schema.json` + `contracts/game-source-archive.schema.json` | WS3 + WS2 | 生成 → 编译 → 源归档/对象存储 → 预览 → 发布 → 运行全链路 | GamePackage 已建;制品索引和引擎无关源归档契约已建、生产接线分波迁移 |
| **跨仓 #5 events** | `contracts/events.schema.json` | WS5 | 前端埋点 / SDK 上报 → 看板 | 已建 |
| **跨仓 #6 Dify I/O** | `contracts/dify-workflow-io.json` | WS2 | ~~aigc 壳 ↔ Dify~~ | **废**(降远期 / 从未部署;现行=new-api,见 `DEPRECATED-dify-workflow-io.md`) |
| **跨仓 #7 ad-slot** | `contracts/ad-slot.schema.json` | WS5 | ad 模块 / SDK Plugin.Ad | 已建 |
| **跨仓 #8 prompts** | `contracts/prompts/`(`registry.yaml` + 8 阶段 01-safety…08-meta) | 各 prompt owner 工位 | aigc / 生成链路 | 已建 |
| **第 9 类 装载契约** | `game-runtime/src/core/game-host.d.ts` | Runner v2 P1 | host boot(装游戏)+ 生成 agent(产出靶) | 已建 · additive |
| **第 10 类 rt 约定** | `game-runtime/src/host/runtime-api-2d.d.ts` | 引擎线(plan 2026-06-18-001 U1) | U2 build-from-source 装配器 + U3 GAMEDEF_SYSTEM prompt | 已建 · additive(是否升格 contracts/ 待 6c6g 定) |
| **生成主线 keystone** | `contracts/agent-loop/source-project.schema.json` | 后端线(Opus 设计) | 后端线产出 / 落库 → 引擎线 2D 适配器消费 | ACTIVE(两开发线交汇 keystone) |
| **生成 QA 闭环** | `contracts/agent-loop/game-design.schema.json` · `verdict.schema.json` · `skeletons/{tictactoe,breakout,simon}.json` | WS2(HJ-AGENT-LOOP) | 策划 LLM 产 GameDesign / judge.py 产 Verdict → 对抗评审、JSONL 账本、eval 回流 | ACTIVE |
| **品类/形态 schema** | `contracts/templates/*.schema.json`(13 个:clicker/dodge/match/narrative/tycoon/merge/business-sim/puzzle/idle/heritage/generic/runner/trpg) | 生成线 | 后端 `parseAndValidate` / `PromptResourceLoader` | 已建(部分退场仍须就位,见下) |
`templates/generic.schema.json` 有个反直觉的约束:P3 走引擎 bundle 路后,后端对 GameConfig 玩法字段的校验已退场,但这 13 个 schema 文件仍须就位 —— 缺一个,`PromptResourceLoader.isReady()` 就因 `getAllTemplateSchemaTexts` 链加载不到资源而卡在 false([generic.schema.json:5](../../../contracts/templates/generic.schema.json))。退场不等于可删。
#4 内部有三份用途互斥的 JSON。`game-package.schema.json` 是宿主真正解析的运行清单,版本前缀下文件名仍为 `manifest.json`;`game-artifact-manifest.schema.json` 是平台上传、下载和逐文件校验使用的运行包/素材/证据对象索引,文件名固定为 `artifact-manifest.json`;`game-source-archive.schema.json` 是引擎无关源码归档索引,文件名固定为 `source-manifest.json`,存放在独立 `game-sources` 桶。对象索引不能作为 `manifestUrl` 返回给宿主,不能携带 candidate/published/retired 状态,也不保存签名 URL。生产版本身份来自 project 数据库,Tier2 源工程继续绑定现有 SourceProjectStore;LittleJS/Canvas 等游戏通过 `game_source_archive` provider 绑定引擎无关源归档。对象存储提交记录由 V34 `game_artifact_storage` 保存,不能把 MinIO marker 当作 committed。
`agent-loop/` 和 `templates/` 这两个目录在 `contracts/README.md` 的目录结构图里没有 —— 那张图只画了跨仓 8 类。新人 `ls` 到它们时无从对应,这是 README 自身的覆盖缺口,在此补登:`agent-loop/` = 生成线的源项目 keystone 加 QA 闭环工件契约族,`templates/` = 13 个品类/形态 schema。
## "第几类"口径收口
仓里出现过至少五套互相冲突的"第 N 契约"编号。四套都带"第 9",两套都叫"8 契约",数字撞得很凶,语义却各不相干。一个新读者顺着任意一处文档读进来,几乎必然误判"到底有几类、各算第几"。
跨仓主编号轴只有一条:`contracts/README.md` 的 1-8 类,加上后来 additive 进来的第 9(装载)、第 10(rt 约定)。这是唯一一条"类"的编号轴。所有别的"第 N 契约"字样,都属于某个局部上下文,不在这条轴上 —— 它们撞数字、不撞语义。
| 出现过的"第 N 契约"字样 | 它属于哪个上下文 | 与主轴的关系 |
|---|---|---|
| 跨仓 **第 9 类**(装载契约 game-host.d.ts) | game-runtime 内部 additive | **就是主轴上的第 9 类** |
| **第 9 契约组 9a-9g**(9a 模板协议 … 9g 收益归因) | 2026-06-12 生成系统总体架构 review 的生成工厂契约包 | 生成工厂的内部编号,与装载第 9 类**同数字、零语义重叠** |
| **9c**(渠道版 GameConfig 的 c 子项) | 渠道发行档(运营/渠道发行.md) | 渠道侧 GameConfig 的局部子项,future;与上面两个"9"都不是一回事 |
| **生成主线 8 契约 ①-⑧**(①源项目 schema … ⑧trace+cost) | 2026-06-17 固定游戏架构与 SAA execution §5(ACTIVE) | 生成两开发线交汇的契约集,与跨仓 README 的 8 类**几乎零重叠**;其中 ① = `contracts/agent-loop/source-project.schema.json` |
引用契约时认上下文、不认数字。看到"第 9 契约"先问是哪条轴上的:跨仓 additive 的装载面,还是生成工厂 review 的 9a-9g,还是渠道的 9c。`agent-loop/` 下的 `game-design` / `verdict` 这两件 QA 闭环工件,任何"第 N 类"编号体系都没把它们收编,它们走自己的 `工件契约①/②` 编号(HJ-AGENT-LOOP)。
## 契约怎么同步进代码
声明的同步序是单向的:后端先写 `-api` 包的 VO/DTO,前端据此定义 TS 类型;改任何 API 必先改 `contracts/` 再写代码([contracts/README.md:63](../../../contracts/README.md))。Java 侧的镜像分布在每模块一份 `-api` 包 —— `game-module-{aigc,ad,biz,community,compliance,feed,project,runtime,studio,telemetry,trade}-api`,加上沿用 huijing 框架的 `huijing-module-{infra,pay,system,bpm}-api`。GamePackage 这类跨链路契约通过字段镜像进 `-api`:`game_version.package_url`/`checksum`/`bundle_size` 的权威写者是 runtime 编译成功后回写,aigc 只回填生成元数据,不得写编译产物字段(这条跨模块对齐在 [README §2.1](../../../contracts/README.md))。
DB 契约是个例外,它有两份物理副本,而声明的方向和实际的方向相反。
```mermaid
flowchart LR
A["contracts/db-schemas/
README 声称=授权源
18 个 V*.sql"]
B["game-cloud/.../db/migration/
Flyway 执行副本
25 个 V*.sql"]
A -.->|README §41 声称:diff 须一致
实际:已破| B
B ==>|事实全集 · 真授权源| A
note["缺口 V14-V20 只在执行副本:
trade withdraw / aigc level / trace
source / source_project / modify / uk"]
B -.- note
```
`contracts/README.md:41` 把 `contracts/db-schemas/` 称作授权源,要求执行副本与它保持 Flyway 校验和 diff 一致。这条承诺实际已破:执行副本 25 个 `V*.sql`,`contracts` 只有 18 个,缺 V14-V20 七个(trade withdraw 引用、aigc 加 level、trace readiness、aigc source、source_project 建表、aigc modify、source_project 唯一键 —— 全部只在执行副本)。连两边共有的文件也 diff:V1.0.0、V2.0.0 都是"Huijing 审计列"(contracts)对"Yudao 审计列"(执行副本)的注释级差异 —— 2026-06-15 命名空间改名只改了执行副本、没回灌 contracts 镜像。真正的授权源其实是执行副本那 25 个全集,`contracts/db-schemas` 是一份滞后的部分镜像。
## 防漂移门:声明的 vs 代码里的
`contracts/` 的纪律靠几道"防漂移门"撑着,文档把它们写成 CI 会自动拦截。核到代码里,这些门目前都不存在 —— 它们是文档承诺,不是运行中的机器门。
| 防漂移承诺 | 文档出处 | 代码实情 |
|---|---|---|
| `contracts/db-schemas` ↔ 执行副本 Flyway diff 一致 | [README.md:41](../../../contracts/README.md) | **已漂移**:18 vs 25,加 Huijing/Yudao 审计列注释差 |
| CI 跑 `flyway validate` 阻断不合规 | [README.md:62](../../../contracts/README.md) | 全仓唯一 CI = `game-cloud/.github/workflows/maven.yml`,是 yudao fork 继承件:`on push to master`(本仓分支 dev/2.0.0、默认 dev/1.0.0,**永不触发**)、`-Dmaven.test.skip=true`(跳测)、**无 flyway validate、无 contracts↔migration diff 步** |
| prompt 变更过"四道闸(CI)" | [registry.yaml:3](../../../contracts/prompts/registry.yaml) | 只是 YAML 注释,未见对应 workflow |
| 改契约先改 contracts 再写码 | README.md:4 / [contract-first-development.md](../../../.agents/skills/contract-first-development.md) | 纯口头纪律,无脚本校验 |
`deploy/smoke-test.sh` 里 grep `flyway`/`contracts`/`diff`/`cmp` 零命中,确认这套门在部署 smoke 阶段也不存在。同步当成纯人工纪律在跑,DB 镜像已经因此漂掉。
这是一个待决策位,不在本档自裁:要么补一道真门 —— 在 wave-close 或 pre-commit 加一步 `contracts/db-schemas` ↔ migration 的 `cmp`,漂移即红线拦截;要么干脆裁定执行副本为唯一源、把 `contracts/db-schemas` 降为只读快照或删掉,消除"声称授权源却滞后"的矛盾。无论哪条,先得让 README §41 的口径和代码实情对上。
更重的两套机器化契约手段是**远期待引、MVP 不引**(创始人 2026-06-22 历史回收判定:不捡,记远期待引)。一是服务间的 Pact 消费者驱动契约测试,二是事件的 Schema Registry。MVP 阶段不上它们,靠现有的人工纪律(改契约先改 `contracts/`)加 CI diff、加 events 的枚举守门人就够;真正把它们引进来的时机是微服务拆分、事件消费方变多、人工纪律开始压不住漂移的时候。登记在此是为了不让"现在没有机器门"被误读成"以后也不该有"——它们是有意推迟的升级路径,不是缺口。
## 版本与兼容铁律
规则正文在 [`contracts/README.md` §2](../../../contracts/README.md),不在此重抄,只列锚点:API 走 URL Path 版本,新增字段不算 breaking、删除或重命名要升版本、旧版本保留 ≥ 3 个月;事件每条带 `schema_version`,新增字段给默认值、老消费者忽略未知字段、不兼容变更用新事件名并行消费;DB 迁移已合入的禁改,回滚写新补偿迁移。跨模块对齐的三条硬约束在 [§2.1](../../../contracts/README.md):两个 `qualityScore` 不同量纲不可互相回灌(aigc 0-1 生成质量分 vs telemetry/feed 0-100 运营质量分)、`package_url` 权威写者=runtime、`play_count` 权威源=telemetry `game_play_start` 事件聚合。
## 落点速查
跨三仓共享契约 → `contracts/` 顶层。game-runtime 内部接线契约(第 9 / 第 10 类)→ `game-runtime/src/{core,host}/*.d.ts`,这两类"为什么落 game-runtime 不落 contracts"的完整 rationale 写在各自 `.d.ts` 头注释(game-host.d.ts:9-12 / runtime-api-2d.d.ts:4-7),不在此重抄。Java 侧镜像 → `game-cloud/game-module-*/*-api`。错误码段每模块独占,project=100 / aigc=101 / runtime=102 / feed=103 / telemetry=104 / pay=105 / trade=106 / community=107 / ip=108 / compliance=109 / biz=110 / ad=111 / studio=112,端前缀 `/app-api`·`/admin-api` 由框架按包名自动加([README.md:71-81](../../../contracts/README.md))。