lili 17727d08c3
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
feat(storage): 接入游戏内容对象存储与运行时提交门
冻结 GameArtifactManifest/GameSourceArchive 契约和 V34 pending/committed 状态记录,增加专用 writer 的全量回读物化校验,并让 Runtime API 在消费和发布前对账 committed、GamePackage checksum 与实际 engineBundle hash。隔离 staging 已验证真实消费拒绝未提交版本;源码桶权限仍保持 fail-closed,正式金标不切换。
2026-07-29 12:02:22 -07:00

14 KiB
Raw Blame History

topic, canonical, date
topic canonical date
契约总览 true 2026-06-18

契约总览

这是什么:绘境AI 全部契约族的导航与口径收口表 —— 有哪几类契约、各落在哪、各算"第几"、怎么镜像同步到代码、防漂移门到底有没有。 给谁看:跨仓对接的工程师、做契约变更与评审的人、第一次 ls contracts/ 想搞清楚目录里这堆 schema 各是什么的新人。

字段级定义不在这里 —— 它们在各 .schema.json / .d.ts / -api 包,以及生成主线 execution §5。本档只给指针,负责回答"到底有几类、各算第几"。导航起点是 docs/agent-specs/_index.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。

flowchart TB
    subgraph C["contracts/ 顶层 · 跨三仓五工位 SSOT"]
        C1["跨仓 8 类<br/>API yaml · DB Flyway · SDK · GamePackage<br/>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<br/>游戏宿主装载契约"]
        R2["第 10 类 runtime-api-2d.d.ts<br/>运行时访问约定 rt"]
    end
    subgraph A["game-cloud/game-module-*/*-api · Java 镜像"]
        A1["VO / DTO / 错误码常量<br/>(后端先写 → 前端据此定 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)。退场不等于可删。

#4 内部有三份用途互斥的 JSON。game-package.schema.json 是宿主真正解析的运行清单,版本前缀下文件名仍为 manifest.jsongame-artifact-manifest.schema.json 是平台上传、下载和逐文件校验使用的运行包/素材/证据对象索引,文件名固定为 artifact-manifest.jsongame-source-archive.schema.json 是引擎无关源码归档索引,文件名固定为 source-manifest.json,存放在独立 game-sources 桶。对象索引不能作为 manifestUrl 返回给宿主,不能携带 candidate/published/retired 状态,也不保存签名 URL。生产版本身份来自 project 数据库Tier2 源工程继续绑定现有 SourceProjectStoreLittleJS/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)。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)。

DB 契约是个例外,它有两份物理副本,而声明的方向和实际的方向相反。

flowchart LR
    A["contracts/db-schemas/<br/>README 声称=授权源<br/>18 个 V*.sql"]
    B["game-cloud/.../db/migration/<br/>Flyway 执行副本<br/>25 个 V*.sql"]
    A -.->|README §41 声称:diff 须一致<br/>实际:已破| B
    B ==>|事实全集 · 真授权源| A
    note["缺口 V14-V20 只在执行副本:<br/>trade withdraw / aigc level / trace<br/>source / source_project / modify / uk"]
    B -.- note

contracts/README.md:41contracts/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 已漂移:18 vs 25,加 Huijing/Yudao 审计列注释差
CI 跑 flyway validate 阻断不合规 README.md:62 全仓唯一 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 只是 YAML 注释,未见对应 workflow
改契约先改 contracts 再写码 README.md:4 / 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,不在此重抄,只列锚点:API 走 URL Path 版本,新增字段不算 breaking、删除或重命名要升版本、旧版本保留 ≥ 3 个月;事件每条带 schema_version,新增字段给默认值、老消费者忽略未知字段、不兼容变更用新事件名并行消费;DB 迁移已合入的禁改,回滚写新补偿迁移。跨模块对齐的三条硬约束在 §2.1:两个 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)。