games-development-ai/docs/agent-specs/2026-07-05-游戏资产管理范围决策-设计.md
lili 960198bd20
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
docs(资产/观测): workflow 双产出 fable 终审通过(资产面范围决策设计 + 阶段四后续波 plan)
workflow wdfr57899 异步产出、fable 终审:
- 资产面范围决策设计(①源工程存储+②素材市场都要):opus 调研→主笔→对抗/完整性/材料三视角评审→修。fable 终审:对抗评审 3 硬伤真修(孤儿覆盖断言据代码纠正+修法二选一+清扫job抬必交/assetRefs据实化+跨线前置/验收改字节等价硬门sha256)、关键事实亲验坐实(BackendStore save:482/fetch:574 真实现非占位、assetRefs/lineage 契约零命中、TIER2_STORE 部署零命中)。四段拆:SRC最优先/MAT-VERIFY并行/MARKET-READONLY排期/MARKET-TRADE后置(对齐数据飞轮§4)。待创始人评审§7六项。
- 阶段四观测后续波 plan(②game-cloud挂agent+actuator/③Python两线接OTLP/④三跳traceparent/⑤埋点+看板+告警):照已签设计施工、不触发双评审门;工单六要素齐、碰生产窗口(game-cloud单体②⑤/生成线③④)全文分开、验收引设计§8六条+脱敏第7条。待创始人定窗口§7六项。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 08:38:20 -07:00

50 KiB
Raw Blame History

date, topic, status, sot-impact, 上级, 关联, 图清单
date topic status sot-impact 上级 关联 图清单
2026-07-05 资产管理-范围决策 草案(fable 主笔 2026-07-05 · 三视角评审已修订 · fable 终审通过)—— 范围拆线与先后已由创始人 2026-07-05 拍板(见 §5)。本档在既定范围内补 ① 源工程存储 与 ② 素材市场的工程设计,已按对抗 / 完整性 / 材料三视角评审逐条修订:半落库孤儿失败模式据代码纠正、`fetch` 消费方边界划清、重建验收改字节等价硬门、公开货架补审核门、`assetRefs` 跨线依赖据实化、分期号对齐数据飞轮 §4。**fable 终审:对抗评审 3 硬伤(孤儿覆盖断言/assetRefs 已预留/验收弱化)确认真修,关键事实亲验坐实(BackendStore save:482/fetch:574 真实现非占位、assetRefs/lineage 契约零命中、TIER2_STORE 部署零命中)。** 仍待执行计划或创始人拍板的点集中在 §7,待创始人评审。 不新建 canonical topic。本档在既定范围内细化 `架构/生成引擎/数据飞轮` §3.2 资产层与 §4 分期的落地设计,不改其 canonical 命题;分期编号以数据飞轮 §4(阶段一溯源链 / 二市场只读半 / 三市场流通半 / 四收益回流)为权威,本档 ①②③④ 是其下的执行拆段、不另立编号真相(对照见 §5)。真正落地时将牵动 `后端/数据模型`(game_source_project / game_material / tier2_source_project_version 的治理口径)与 `架构/契约总览`(studio.yaml 新增货架端点、trade.yaml source 加 ASSET),届时各走 contract-first 与迁移治理。素材归因用的 `lineage` / `assetRefs` 字段在数据飞轮 §3.1 目前只是提案、未落 source-project.schema.json,只读半对它有跨线前置(见 §4.1 / §5)。不改 55 P0 验收定义,不擅自把 P-MAT 升 P0。 docs/architecture/架构/生成引擎/数据飞轮.md docs/mvp/MVP作战清单.md:37(W-ASSET 四段) · docs/mvp/MVP进度总账.md:59(studio 素材库 minimal-real / 前端 mock) · docs/mvp/MVP进度总账.md:150(tier2 BackendStore 孤儿风险) · docs/architecture/架构/生成引擎/数据飞轮.md §3.2/§4/§6 · contracts/api-schemas/studio.yaml(R-MAT 三端点) · contracts/api-schemas/trade.yaml(source enum 无 ASSET) · game-cloud/huijing-server/src/main/resources/db/migration/V18.0.0__create_game_source_project.sql · contracts/db-schemas/V24.0.0__create_game_material.sql · tier2/gen-worker/worker/store.py · tier2/gen-worker/worker/run.py:904
图1 四类资产与四段拆线 · 图2 源工程存储双面架构 · 图3 素材市场数据流(只读半/流通半) · 图4 四段优先级与闸门依赖

游戏资产管理范围决策 · 设计

W-ASSET 这条作战线原本用「游戏资产管理」一个宽词兜住了太多东西。创始人在一次范围复核里把它揪了出来:「资产」底下其实躺着四类完全不同的东西——玩家点开就玩的运行包、生成出来能改能重建的源工程、创作者上传自用的素材、以及跨创作者能买卖分成的资产市场。它们的负责人、卡点、验收门互不相同。焊在一个完成线里,结果就是本可以马上补稳的源工程可靠性,被支付牌照、授权、分成、法务这些外部闸门一起拖住。

2026-07-05 创始人已就范围拍板:拆线、先做「源工程长期存储 + 私有素材库真验」、并允许资产市场只读半在支付接通前先上(拍板全文见 §5)。本档承接这个已定的范围,把要动手的两件补成工程设计——数据结构、入口、使用路径、失败模式、验收线。两件的成熟度不一样,别混谈:① 源工程存储是一套可以直接切执行计划的完整设计;② 素材市场分三层,私有库真验(第一层)也可直接切,只读半(第二层)只给出契约增量清单与跨线前置——它要等素材中心 P-MAT 把素材结构化、要等溯源链阶段一把 assetRefs 归因字段落进契约,详细 schema 是素材中心模块的交付物、由承接它的执行计划做,流通半(第三层)只标边界与外部闸门依赖。范围决策本身不再重开;要评审的是 §3、§4 这两套做法。

1. 这份设计要解决的问题

要同时推进 ① 和 ②,不是因为它们像,恰恰因为它们各撑护城河的不同一面。

源工程存储撑的是生成这条主线的可靠性。 一款游戏发布之后,它的源工程还能不能被找回来改、能不能按版本重建出同一个包——这是便宜档 A11 修改回路、tier2 富游戏第二次装载、以及「游戏 = 长生命周期项目」这个范式共同的地基。它也是数据飞轮收益回流语料里那一份源工件(source_json)的来源。地基塌了,上面所有「改一改、做个变体、按版本回滚」的能力都是空中楼阁。

素材市场撑的才是护城河四层里的「资产」层。 把创作者各自攒的私有素材,长成一个可检索、可复用、可流通、权利链清晰的资产库——这正是数据飞轮 SoT §3.2 写的那条「资产沉淀」飞轮。它是竞品搬不走的东西之一:模型可以追平,越积越厚的素材库和权利链不行。

一个是地基,一个是护城河,谁也替不了谁,所以都要。但它们受的约束天差地别:源工程存储的验收不碰钱、不碰授权、不碰法务,是纯工程可靠性问题,靠自己就能验完;素材市场一旦要「流通」,就撞上真实支付收单和 IP 分成口径这两道外部闸门,快不了。这个差异决定了后面 §5 的排序——不是优先级偏好,是物理约束。要说清的是,「① 能先交付」讲的是它的验收自足,不是它和素材资产无关:① 存的那份源工程 manifest,正是收益回流语料的源工件来源,也是素材归因 assetRefs 将来要寄生的地方(见 §4.1)。① 其实是回流飞轮和资产飞轮共同的底座,先做它恰恰因为两条飞轮都踩在它上面,而它自己不欠钱、不欠授权、不欠法务。

四类资产的边界先摆清楚,后面只碰中间两类:

flowchart TB
  R["运行包资产<br/>GamePackage / engineBundle / runtime package"]:::built
  S["源工程资产<br/>src 多文件工程 / sourceProject / versionId"]:::now
  M["私有素材 → 资产市场<br/>game_material / 六类 / 货架 / 授权 / 分成"]:::now
  X["(同上,市场化的流通半)"]:::later

  R -->|已支撑 feed 真玩与发布链| Done["维持现状,不在本次范围"]
  S -->|"① 本次要动"| SRC["W-ASSET-SRC<br/>长期存储 + 版本寻址 + 取回重建"]
  M -->|"② 本次要动"| MAT["W-ASSET-MAT-VERIFY + MARKET-READONLY<br/>私有库真验 + 支付前只读货架"]
  M -->|"② 后置(外部闸门)"| TRADE["W-ASSET-MARKET-TRADE<br/>购买 / 授权 / 分成"]

  classDef built fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20;
  classDef now fill:#fff7e6,stroke:#d97706,color:#7c2d12;
  classDef later fill:#f5f3ff,stroke:#7c3aed,color:#4c1d95,stroke-dasharray:5 4;

运行包资产已经在 runtime / project / feed 主线里运转,玩家能打开能玩,不是本次缺口,维持现状。本档只设计中间两类:源工程资产(§3)和素材资产 → 资产市场(§4)。

2. 现状与地基

设计要落在现状上,先把两条线现在库里有什么、缺什么核清楚。

2.1 源工程存储:两套并存,各服务一档

源工程存储今天是两套并存的面,各服务一档,彼此独立、没有统一寻址。

便宜档 / Tier0-1 这一档,走 Java 的 game_source_project(V18.0.0,已在生产 Flyway 迁移里落地)。源工程 JSON 直接落 DB 的 source_json LONGTEXT 列(M0 存储);source_hash 是 sha256 幂等键;version_id 在构建成功后回填,反查产物版本;status 是状态机(0 草稿 / 1 已构建 / 2 孤儿 / 3 已发布),status=2 就是构建失败留下的孤儿态,这套机制本身已经把「落库了但没建成包」这种半成品显式建模了;base_version_id 记同一款游戏内 modify 的血缘。三个索引各管一件事:idx_source_hash 去重、idx_game 查历史、idx_version 反查源。它现在真实支撑着便宜档 A11 的 base 源注入。表里留了一个 source_url 列(第 33 行),打算切对象存储后用,但 DEFAULT '' 至今空着——现在 DB 里的 source_json 才是权威。

tier2 富游戏这一档,走 tier2/gen-worker/worker/store.py 这里有一个抽象接口 SourceProjectStore(save/fetch),和两个实现。LocalFsStore(157319 行)是真能跑的实现:内容哈希寻址、幂等、留版本历史,落在 GEN_DIR/_store/<id>/<versionId>/,spike 期一直真用,default_store() 默认返回的就是它。落库入口是 run.persist_source_project(run.py:904941):它是收口处的额外一步,GEN_DIR 下的 workdir 源文件仍照常留盘(现有 build / run_gates / smoke 全链路不受影响),这一步只是把交付的源工程经 store 持久化一份;而且是 best-effort——落库失败只告警、不中断生成主链。

2.2 纠正一个过时前提:BackendStore 不是「占位」,是「真实现但没接线」

早前的口径(包括本决策的初稿)把 tier2 的 BackendStore 说成「占位、raise NotImplementedError」。这在今天的代码里已经不成立,得据实改。

store.py 第 351 行起的 BackendStore 是一份写完的真实现:save(482 行)把 manifest 写进 MySQL、把源文件全文逐个 put 进 MinIO;fetch(574 行)按 (game_id, versionId) 从两边拼回一个可重建的源工程;幂等键是 (game_id, source_hash),版本寻址键是 (game_id, version_id);MinIO 的 object key = <game_id>/<versionId>/<工程内相对路径>,bucket 默认 tier2-src。它连惰性 import、连接失败响亮抛、路径安全校验都写了。真正陈旧的只有模块头部第 12 行那句注释,它还停在「占位 raise NotImplementedError」的旧话上,和下面 351 行起的真实现自相矛盾。

所以缺口不是「没写」,而是另外三件事,这三件才是 W-ASSET-SRC 要收的:

  • 没接线。 default_store() 只有在环境变量 TIER2_STORE=backend 时才返回 BackendStore。全仓 grep 这个开关,命中的全是 store.py 自己的定义与文档、以及凭据档里一句「怎么切」的说明,没有任何一个 yaml / sh / env 部署配置真把它打开。生产默认仍然走本地 LocalFsStore
  • 没真跑证据。 进度总账第 150 行明记:BackendStore 落库只有 schema 形状、缺真跑证据、有孤儿风险。「按 versionId 取回 base 源 / 取回重建」这条端到端从来没在真 MySQL + 真 MinIO 上跑通过。
  • 建表游离在 Flyway 之外。 tier2_source_project_version 这张 manifest 表,是 store.py 里的 CREATE TABLE IF NOT EXISTS(334348 行)在首次落库时自建的,不是一条 Flyway 迁移。它不受迁移治理,schema 变更没有版本、没有回滚补偿迁移的位置。

一句话:2026-07-05 决策稿说 BackendStore「占位」,在代码层不精确,但在「它不是已接生产的后端」这一点上,实质是对的。

2.3 素材侧:只有私有库,市场化能力为零

素材这条线,今天只有创作者私有素材库,资产市场(货架 / 授权 / 分成)一样没有。

game_material 表(V24.0.0,contracts/db-schemas 与 huijing-server 迁移同源,已生产落地)的列是:creator_user_id(归属隔离,创作者只见、只用自己的)、category(六类冻结:sprite / character / effect / scene / ui / music,由 MaterialCategoryEnum 四处同引)、name / ref / url / provider(默认 mmx-cli)/ size_bytes / mime_type,加 Yudao 审计列。只有一个索引 idx_owner_cat(creator_user_id, category, id) 供归属隔离浏览。字节存 infra 的 FileApi,本表只登记 ref/url。

Service 是 StudioMaterialServiceImpl:register(71 行,按引用登记,六类校验 + 声明 MIME 与类目族一致性自检,无字节所以单条 insert 不加事务)、browse(104 行,归属隔离,mapper 内 creatorUserId eq 硬过滤)、selectIntoDraft(116 行,悲观锁 FOR UPDATE 锁会话行 + DRAFT-only 守卫,把选中素材按 ref 去重合并进草稿 assetContext)。端点是 studio.yaml 的三个:POST /app-api/studio/assetGET /app-api/studio/asset/browsePOST /app-api/studio/asset/select。单测 15/0/0。

缺的是整个市场化的那一半。game_material 没有货架、没有公开可见范围、没有授权类型、没有锁风标签、没有分成规则(SQL 列缺项,grep studio 模块 Java 的 shelf / 货架 / 授权 / 分成 / marketplace 全空)。trade 的收入来源枚举只有 [1, 2](1 广告 / 2 打赏,trade.yaml:55/200/416 三处一致),没有 ASSET;没有采购单 / 授权单表;支付收单没接进 game 业务(只有 PayWalletApi 的余额加减,不是收单)。前端 game-studio 的素材中心页当前是 build/mock,没在 staging 真验过——这是「前端假绿」的风险:build 能过只证明代码能构建,不证明已接真后端。产品需求 P-MAT-01..06 全是 P1。所以素材市场目前只是设计立位,实现未建。

3. ① 源工程存储设计

源工程存储要解决的问题很具体:一款游戏发布之后,它的源工程还能不能被稳定找回来改、能不能按版本重建出同一个包。今天便宜档这一面(game_source_project)已经在生产真用,富游戏这一面(BackendStore)真实现已写好、却没接线也没真跑过。W-ASSET-SRC 就是把富游戏这一面从「写好但没通电」推到「接生产、有真跑证据、schema 受治理」,同时把两面的边界划清楚。

3.1 设计立场:两面各管一档,统一在寻址语义而非合表

一个容易犯的错是「既然有两套源工程存储,那就并成一张统一资产表」。不做。两面服务的档不同、存储模型也不同:便宜档源工程小、直接 DB LONGTEXT 就够,和 Java 事务、A11 血缘紧耦合;富游戏源工程是多文件工程,manifest 落库 + 全文落对象存储才合理,而且 tier2 是一个独立 Python service,forbidden-import 守着它不实连 game-cloud 后端。硬把两者并表,等于给一次性收益造一层长期抽象,违反「不为一次性代码建长期抽象」和「不留孤儿设计」两条红线。

真正需要统一的,是寻址语义,而不是物理表。两面其实已经在说同一套寻址三元组:便宜档有 game_id / version_id / source_hash,富游戏 manifest 表也有 game_id / version_id / source_hash。所以「统一」落在契约层——两面对外都用 { id(即 gameId), versionId, sourceHash } 这个三元组做寻址键,谁调用谁都按这个三元组存取。这是最小改动、不制造孤儿抽象的做法。

这里要顺带把一层耦合讲透,别等溯源链落地时才发现撞车:两面的 manifest 都要能容忍将来 additive 进来的 lineage / assetRefs 字段(§4.1)。好在这层耦合是无害的——便宜档的 source_json 和富游戏的 manifest_json 都是不透明 JSON 列,新增一个可选字段不破存量;而按版本重建只读 fileTree 里的文件内容、根本不碰 lineage,所以字节等价重建这条核心保证不受溯源链改动影响。① 存的是骨架,lineage 是骨架上后贴的一张归因便签,两者互不干涉。

flowchart TB
  subgraph CHEAP["便宜档 / Tier0-1(Java · game-cloud studio)"]
    J1["game_source_project 表(V18 · 已生产)<br/>source_json LONGTEXT · source_hash 幂等<br/>version_id 回填 · status 0草稿/1已构建/2孤儿/3已发布<br/>base_version_id 同款内 modify 血缘"]
  end
  subgraph RICH["tier2 富游戏(Python service · gen-worker)"]
    T1["run.persist_source_project 收口<br/>additive · best-effort"]
    T2["LocalFsStore(已生产默认)<br/>_store/id/versionId/ 落盘 · 内容哈希寻址"]
    T3["BackendStore(真实现 · 未接线)<br/>MySQL manifest = tier2_source_project_version<br/>+ MinIO bucket=tier2-src key=game_id/versionId/路径"]
    T1 --> T2
    T1 -. 只有 TIER2_STORE=backend 才走 .-> T3
  end
  ADDR["共同寻址语义:{ id=gameId, versionId, sourceHash }<br/>两面各自实现,不合表"]
  J1 --- ADDR
  T3 --- ADDR
  style T3 stroke-dasharray:5 4

3.2 把 BackendStore 推到真接生产的三步

第一步,接线激活。 按 AGENTS §6 条款 9(内网决策进配置、不散落硬编码),把 TIER2_STORE=backend 放进 tier2 service 的部署配置(tier2/config/infra.yaml 的 store 段或部署 env 单点),让 default_store() 在生产返回 BackendStore,同时 MinIO / MySQL 连接参数从 infra.yaml 现取现连。切换只在 default_store() 这一处,主链和测试都经这个工厂取实现,不在别处硬编码具体类——这已经是现有代码的形态,只是把开关真打开。LocalFsStore 不动,6c6g 本地往返自检仍用它。

第二步,给那张自建表定治理归属。 tier2_source_project_version 现在靠 CREATE TABLE IF NOT EXISTS 自建。这里有一个要在执行计划里拍死的选择:是把它抬成一条 Flyway 迁移,还是把这份 in-code DDL 正式登记成「tier2 自有、Flyway 之外」的受治理 schema。判断的关键是 tier2 用的是不是 huijing 那套 MySQL——infra.yaml 默认 database='tier2',是一个独立逻辑库,而 game-cloud 的 Flyway 治的是 huijing 库。若确实是独立库,把它塞进 game-cloud 的 Flyway 是跨库的类别错误;更合适的是把 store.py 里那份 DDL 与「另有一份同源 .sql」(docstring 已提及 config/schema/)对齐成单一真相,登记为 tier2 owner 的受治理 schema,并在 doc↔code 之间立一个断言点(表名 tier2_source_project_version、幂等键、object key 前缀是契约点)。要说清对齐面在哪一侧:这张表和它的 object key 是 tier2 这个 Python service 自己的落库 / 取回口径(store.py 的 save / fetch),forbidden-import 守着 tier2 不实连 game-cloud、Java 也不跨读这张表(全仓 grep 确无 Java 引用它)。所以契约对齐的三处是凭据档、store.py 落库、store.py 取回,都在 Python 侧;便宜档 Java 那一面走的是它自己的 game_source_project,与这张表是两套并存的存储面、不交叉取回。无论走哪条,底线是:这张表不能继续游离——schema 变更要有版本、有回滚位置。这一步的最终定夺留给执行计划,因为它取决于「tier2 与 huijing 是否共库」这个部署事实,本档先标出选择与判据。

第三步,补取回重建的真跑证据。 这是 W-ASSET-SRC 的验收核心,也是今天最大的空白。要在真 MySQL + 真 MinIO(mini-infra / mini-desktop 窗口)上跑通一条端到端 harness,它同时就是本线几条核心断言的落点(下面 §3.3 提到的「缺源显式失败」「半落库不留隐形孤儿」都在这个 harness 里验,不悬空):

  • 确定性重建:save 一份富游戏源工程 → fetch 按 versionId 取回 → 把取回的 fileTree[].content 重新 esbuild 构建 → 断言重建产物与原 bundle 一致。一致的判据要硬:首选重建 bundle 的 sha256 与原 bundle 相等;若 esbuild 产物在某些环境下非字节稳定,则退到 buildInputHash(= sha256(sourceHash + buildProfile),契约里已有此字段)相等、且重跑九门 verdict 完全一致、无任何 missingContent。不接受「能进构建就算过」这类降级——那放得过一个缺文件的残缺取回,而残缺取回根本不是同一个包。
  • 版本寻址:改一处源、重建,断言落一个新 versionId、旧版本仍可按旧 versionId 取回。
  • 幂等:拿同一份源重投,断言幂等命中 (game_id, source_hash)、不落第二个版本。
  • 缺源显式失败:人为让某文件在 MinIO 缺失(fetch 会把该文件项标 missingContent),断言重建步骤据此显式失败、报「无法重建此版本」,绝不静默退回全量重生成。
  • 半落库不留隐形孤儿:模拟 MinIO 写成功而 MySQL 写失败,断言按 §3.3 选定的方案不产生「有对象、无 manifest 指向」的隐形残片,且清扫 job 能把它认出来回收。

跑通并留下日志,才算这条存储线真的「通电」了。

3.3 入口 / 使用路径 / 失败模式 / 验收

入口。 落库入口是 tier2 收口的 run.persist_source_project(已在,additive、best-effort),它经 default_store() 取实现,save 有真实生产接线。取回入口是 BackendStore.fetch(game_id, versionId)——这里要诚实:fetch 今天全仓没有一个生产调用点(grep .fetch( 零命中),它设计上的消费方「tier2 富游戏第二次装载(modify / 重建)」这个入口还没建。所以 W-ASSET-SRC 交付的是 save 接生产 + fetch 由第三步 harness 的 save→fetch→rebuild 闭环验证;fetch生产消费方(tier2 second-load)是另立的后续线,本档只声明它、不在本线接线。别把 fetch 当成已有生产消费的接口。便宜档这一面的入口是 studio 的 create/modify/extend 落 game_source_project,取回走 base_version_id / version_id 查历史,这套已在生产——注意它走的是便宜档自己的 Java 表,不经 BackendStore,和富游戏取回是两条独立路。

使用路径。 便宜档:A11 修改回路按 base_version_id 拿 base 源注入,已真用(Java 侧、game_source_project)。富游戏:一款游戏首次生成收口时 save 落 manifest + 全文;将来要改、要重建、要做变体时,由 tier2 second-load 入口按 (game_id, versionId) fetch 回来,拿到含各文件 content 的源工程重新构建或重跑九门——这条消费路是后续线,W-ASSET-SRC 先把 save/fetch 这对底座连通、用 harness 坐实取回可重建。两面都遵「改源不改产物」——源工程落源侧存储面,构建产物仍存 game_version + game_runtime_package,GamePackage 产物 schema 不动。

失败模式。 落库失败已经是 best-effort:persist_source_project try/except 兜住,产物仍在 GEN_DIR workdir,只告警不中断主链——生成这条主线不会因为归档失败而断。

真正要正视、且当前代码有真实隐患的是半落库的孤儿BackendStore.save 的顺序是:按 (game_id, source_hash) 查幂等 → 未命中则派生 versionId → 先把文件 put 进 MinIO → 再写 MySQL 一行。这里有个不能忽略的事实:versionId = v{秒级时间戳}-{hash12},时间戳是收口那一刻 time.time() 现取的(studio.py 收口处 now_ts=time.time())。于是当 MinIO 写成功、MySQL insert 失败时,那一行从没落库,下次重投(重新生成)now_ts 变了 → versionId 变了 → MinIO object key 的前缀 game_id/{新versionId}/ 也变了 → 上一次写进旧前缀的那批对象不会被覆盖、永久留成没有 manifest 指向的隐形残片。所以孤儿不是「只有永久失败才产生」,而是每一次 MySQL-after-MinIO 的瞬时失败都产生;初稿里「重投同 key 覆盖写、重试自愈」的说法与代码相反,据此纠正。

修法要在 W-ASSET-SRC 执行计划里定死一条,不能留在正文当推理。推荐先写 manifest 再写对象:save 改成先在 MySQL 落一行 status=pending 的 manifest、再 put MinIO、成功后把 statuscommitted;幂等查询改成认 (game_id, source_hash) 不分状态——命中 pending 就复用该行已定的 versionId 续 put(同前缀真覆盖,才是真自愈)、命中 committed 直接返回。这样崩溃留下的是一条可见的 pending 行(fetch 本就靠 missingContent 容忍残缺),而不是无处可查的隐形对象;孤儿就等于 pending 超时行,清扫 job 按 status 认得出、按行里的 (game_id, versionId) 拼出前缀删得掉。代价是 manifest 表加一个 status 列。备选是让 versionId 只由 source_hash 派生(去掉时间戳,版本时序改靠 created_at + idx_game_created 兜),重投复用同前缀真覆盖——但它动的是 derive_version_id 这个和 LocalFsStore 共享的代码契约,改动面更大。无论走哪条,孤儿清扫 job 都要抬成本线的必交项:触发条件(pending 超时行,或有对象前缀却无 committed manifest 行)、调度、owner、一条可验收断言,一并进 §6,不再只在括号里带一句。方案 pick 留执行计划据代码影响面拍(见 §7)。

取回侧的失败要显式:fetch 命中 manifest 但某文件在 MinIO 缺失时,该文件项标 missingContent、不崩;但重建必须据此显式失败——缺源就报「无法重建此版本」,绝不静默退回全量重生成(静默重生成会悄悄丢掉这一版的真实源工程)。这条守卫的落点在上面第三步的重建 harness(它专门验一次缺文件重建显式失败),不是靠某个还没建的生产消费方兜。便宜档侧的孤儿由 status=2 显式建模,已在。

验收线(W-ASSET-SRC)。

  • 生成成功后有源工程寻址结果,能关联到 gameId / versionId / sourceHash
  • versionId 能取回 base 源工程;改源重建落新 versionId,旧版本仍可取回;缺源时(某文件 missingContent)重建显式失败、不静默全量重生成。
  • 取回的源工程能确定性重建:重建 bundle 的 sha256 与原 bundle 相等(esbuild 非字节稳时退到 buildInputHash 相等 + 重跑九门 verdict 完全一致 + 无任何 missingContent)。不接受「能进构建/进九门就算过」这种降级。
  • 幂等重投不重复制造源版本:同 game_id + 同 source_hash 复用既有记录。
  • TIER2_STORE=backend 在生产真接线,真 MySQL + 真 MinIO 往返有跑通日志(不是本地 LocalFsStore 假通过)。
  • tier2_source_project_version 的 schema 治理归属已定(Flyway 迁移或登记为受治理的 tier2 自有 schema),不再游离。
  • 源落库失败不伪造成功,日志能追到 gameIdversionId / sourceHash、失败阶段。
  • 半落库不留隐形孤儿:按选定方案(manifest-first 的 pending 行 + 按 status 回收,或 versionId 复用同前缀真覆盖),模拟 MinIO 成功而 MySQL 失败后,没有「有对象、无 manifest 指向」的残片;孤儿清扫 job 有 owner、有触发条件、有一条真跑过的回收断言。

4. ② 素材市场设计

素材市场的终点,是数据飞轮 SoT §3.2 写的那句话:把创作者的私有素材库,升级成「可检索、可复用、可流通、权利链清晰的资产层」。这一节的设计严格落在那条 SoT 划的边界里,不越位替素材中心和 trade 做它们的活。

4.1 边界:生成侧只有两件薄活,市场横跨三模块

数据飞轮 SoT 把职责划得很清楚,照搬不改:生成侧只做两件,都很薄。 第一,把创作者选用的素材(无论私有库的还是市场买的)透传进生成上下文——复用 P-CRT-04 附件驱动创作那条已有通道(就是 selectIntoDraft 写进草稿 assetContext、create/modify/extend 接受 assetContext 输入的现成链路)。第二,在源项目血缘的 assetRefs 里记下本款游戏复用了哪些 materialId。生成侧不做素材的授权类型管理、不做货架检索、不做采购支付——那些是素材中心和 trade 的活。

这里有一个必须说清、否则阶段二会误判的点:assetRefs 不是「同款创作」的自然副产物。选不选素材,走的是 assetContext 这条正交通道,跟是不是同款无关。所以素材复用归因这份数据,得等创作者真的走了选素材通道才产生;溯源链攒下的是血缘本身,不是分成依据。别让流通半以为只读半会自动把「谁用了谁的素材」攒好。

还有一处契约归宿没定,得在切只读半前拍掉,否则字段无处安放:数据飞轮 §3.1 把 assetRefs 嵌在拟新增的 lineage 顶层对象里,而 lineage 按设计只在同款(remix)生成时才挂载、原创生成不带它。可 assetRefs 讲的是「本款用了哪些市场素材」,这件事和是不是同款无关——一个原创但用了市场素材的作品照样要记 assetRefs,它却没有 lineage 对象可挂。所以 assetRefs 到底是继续嵌在 lineage 里、还是抬成源 schema 的独立顶层字段(或独立关联表),是个悬着的契约选择,要和溯源链阶段一一并定(列入 §7 待拍板)。

整条线横跨三个模块加一道支付硬墙:

flowchart TB
  subgraph STUDIO["studio 模块(登记 / 透传 / 归因)—— 生成侧只这两件薄活"]
    S1["game_material 私有六类库(V24 · 已生产)<br/>register / browse / selectIntoDraft"]
    S2["assetContext 透传生成上下文(复用 P-CRT-04)"]
    S3["源项目血缘 assetRefs 记 materialId<br/>(需选素材通道产生,非同款顺带)"]
  end
  subgraph MAT["素材中心 P-MAT(结构化 · 硬前置)"]
    M1["game_material additive 列<br/>+可见范围状态机(私有→待审→公开/驳回)<br/>+授权类型 +锁风标签 +分成规则"]
    M0["素材审核门(compliance/ip)<br/>锁风标签由审核写 · 只有过审进公开货架"]
    M2["跨创作者公开货架浏览 / 按 IP·类目检索<br/>服务端只列 可见范围=公开 且 锁风通过"]
    M1 --> M0 --> M2
  end
  subgraph TRADE["trade 模块(钱 · 流通半)"]
    R1["收入来源加 ASSET 枚举<br/>source_ref = 采购单 id · uk_source 幂等"]
    R2["采购单 / 授权单表 + 素材作者 share_rate 分成"]
  end
  PAY["真实支付收单(② 充值收单未接 · 日历闸门 / 二清)"]:::gate
  S1 --> S2 --> S3
  M2 -->|"只读半:免费 / 本人自有素材选用"| S2
  M2 -->|"流通半:付费采购 / 他人素材授权"| R1 --> R2
  PAY -. 硬前置 .-> R1
  classDef gate fill:#fee,stroke:#c33,color:#900;
  class PAY gate

4.2 分三层落:私有库真验 → 只读半 → 流通半

第一层,私有库 staging 真验(W-ASSET-MAT-VERIFY)。 已有的 game_material 最小链路今天是 build/mock/单测,要把它推到 staging 真实可走:infra 上传字节 → studio register 登记引用 → browse 浏览本人素材 → selectIntoDraft 选用进草稿 → create/modify/extend 携带 assetContext → 生成侧能在 trace 或 sourceProject 里留下可追踪引用。这一层范围小、和 W-ASSET-SRC 并行、互不阻塞、不新增市场化能力;但它未必全是「跑通已有骨架」——最后一环「生成侧真消费 assetContext 并回填归因」当前没有证据,要先核实(见 §4.3 验收与 §5),核实为缺就补这段接线。

第二层,资产市场只读半(W-ASSET-MARKET-READONLY,创始人已定支付前可先上)。 这一层要动素材契约,但只动「能浏览、能选用、暂不能买卖」的那半。它有两条跨线前置,不是 studio 单独能闭合的(见 §5):素材结构化归素材中心 P-MAT,assetRefs 归因字段归溯源链阶段一。具体做法:

  • P-MAT 契约增量,在 game_material 上加 additive 列:授权类型(原创自有 / UGC 孵化 / 授权 IP)、锁风标签、分成规则占位(素材作者分成比例,先留字段不结算),以及一个可见范围状态机——不是简单的私有 / 公开二值,而是「私有 → 待审 → 公开 / 驳回」,因为素材一旦要上跨创作者的公开货架,就得先过一道内容 / 版权审核(对比 game_source_projectstatusgame_lineage_edge 有 pending/active/voided,素材转公开同样要有审核态,不能一步从私有翻公开)。additive 不破存量私有库。这些列的详细 schema 是素材中心模块的活,但不能悬空引用一个不存在的 spec:在承接它的素材中心设计产物出现前,这份 additive 列 schema 由 W-ASSET-MARKET-READONLY 的执行计划作为 contract-first 交付物先落,owner 记素材中心。
  • 素材审核门:公开货架是 UGC,不能无审核直接上。可见范围从「待审」到「公开」要过 compliance/ip 的审核门;锁风标签由审核流程写(不是随便哪个写入方能填的孤儿字段),服务端货架端点按「可见范围=公开 且 锁风通过」硬过滤,只有过审素材才进公开货架。审核门本身的流程与判据归 compliance/ip + 素材中心,是只读半的硬前置。
  • 跨创作者货架浏览:新增一个不做归属隔离的公开货架浏览端点(区别于现有 browsecreatorUserId 硬过滤),按 category / IP 检索——但服务端只列「可见范围=公开 且 过审」的素材,归属隔离在这条路上换成「审核态过滤」,不是取消过滤。
  • 免费 / 本人自有素材选用:selectIntoDraft 扩展成允许把公开货架里免费的、或本人自己上传的素材选进草稿透传生成。这里要收窄一个初稿含糊的口径:「对他人素材已购授权后再选用」依赖一条授权成交记录来判定,而那条记录(采购单 / 授权单)排在流通半,只读半没有数据能在服务端判「这个人对他人这份素材有没有授权」。所以只读半的选用只认「免费」和「本人自有」这两种服务端判得清的情形,「已购授权他人素材」明确划到流通半。
  • assetRefs 归因(带跨线前置):创作者选用市场素材时,把 materialId 记进源项目血缘的 assetRefs。这里要据实说清:这个字段契约里还没有——source-project.schema.json 顶层目前没有 lineage、也没有 assetRefs(grep 零命中),game_lineage_edge 表也没建;它们是数据飞轮 §3.1「溯源链阶段一」的 contract-first 待办,不是「已预留」的既成事实。所以只读半要记 assetRefs,硬依赖溯源链阶段一先把 lineage / assetRefs 落进 schema 与落库链(或单独把 assetRefs 抬成顶层字段,见 §4.1 的归宿选择)。这条前置要在 §5 显式登记,别当成只读半自己范围内的活。
  • 边界拍死(创始人 2026-07-05):这个中间态只能做「能浏览、能选用、暂不能买卖」,不接购买、授权支付、分成。

第三层,流通半(W-ASSET-MARKET-TRADE,后置,被外部闸门物理卡住)。 这一层带钱,前置齐了才能启动:

  • trade 收入来源加 ASSET 枚举:trade.yaml 的 source enum 从 [1, 2] 扩到含 ASSET,game_trade_income.source 注释同步;source_ref 指采购单 id,素材作者按 share_rate 入账,uk_source(source, source_ref, tenant) 天然防重复分账。账户、提现整套现成,不重建。
  • 采购单 / 授权单表(新建):game_material 只是私有登记,没有「谁买了谁的素材、授权范围多大、单价多少」的成交记录。新建采购单表(买家 userId / 素材 materialId / 素材作者 / 成交价 / 授权范围 / 状态)作成交凭证,它的 id 就是上面入账的 source_ref;要有自己的幂等键(同一买家对同一素材的同一次采购不重复扣款)。授权关系并进采购单还是单列授权单表,由素材中心模块定。
  • 退款 / 撤权口径:素材下架、版权争议、买家退款时,已入账的分成怎么冲、授权怎么撤,是带钱的逆向流程,口径要对齐 trade 的冲正机制(不是简单删行)。这条挂律所与 trade,本档只标出缺口。
  • 硬前置:真实支付收单,且要分清三条线别混。 ① 钱包余额 API(PayWalletApi.addWalletBalance/getOrCreateWallet,已有,但只是给钱包加减余额,不产生真实现金流入);② 支付订单充值收单 API(真实微信 / 支付宝把钱打进来,当前没接进 game 业务);③ trade 入账(分账记账,上面那组)。今天只有 ①③ 有地基,② 是断的,而流通半依赖 ②——② 受日历闸门(牌照 / 二清)约束,不是写几行代码能解决的。所以流通半物理上排在支付真实化之后,这是外部闸门决定的,不是工程优先级选择。

4.3 入口 / 使用路径 / 失败模式 / 验收

入口。 私有库:studio.yaml 现有三端点 POST /app-api/studio/asset(register)、GET .../asset/browsePOST .../asset/select。只读半:新增货架浏览端点、公开选用端点(additive,老调用不受影响)。流通半:新增采购端点,接 trade 入账。

使用路径。 创作者上传 → register 登记私有 →(只读半)提交转公开、进「待审」→ 过 compliance/ip 审核 → 「公开」上货架(未过审驳回,不进货架)→ 他人 browse 货架 → 免费 / 本人自有素材 select 进草稿 → assetContext 透传生成 → 血缘 assetRefs 归因(依赖溯源链阶段一字段就绪)。(流通半)付费采购 / 他人素材授权 → 采购单落成交凭证 → trade 按 ASSET 入账 → 素材作者 share_rate 分成到余额。

失败模式。 register 无字节、单条 insert(不加事务),字节由共享 infra 上传;若 infra 上传成功而 register 失败,留孤儿文件,补偿口径已就近记录(进度总账:59)。归属隔离、货架可见范围、审核态一律服务端强制——前端不是可信边界。selectIntoDraft 用悲观锁 FOR UPDATE 串行化并发选用的「读既有→合并→回写」、DRAFT-only 守卫挡对已提交 / 已生成会话的污染,这两道已在。六类枚举、MIME 错族在服务端拒。

只读半引入一个私有库没有的时序失败要正视:市场素材是别人的,创作者把它 select 进草稿(草稿 assetContext 里存的是 ref 快照)之后、真正生成之前,原作者可能把素材下架、把可见范围翻回私有、或(流通半)撤掉授权。草稿于是攥着一个悬空或失权的 ref。口径:assetContext 存 ref 快照,但生成侧装配素材那一刻要对市场 ref 重解析——复核可见范围仍为公开、审核仍有效、(流通半)授权仍在,失效的显式剔除并提示创作者(或整单拒绝),绝不拿一个已下架 / 已撤权的素材硬装进生成。私有库素材是本人的,不涉此时序缺口。

流通半:uk_source 幂等防重复分账,退款走冲正不删行,账户恒等式 balance + frozen + total_withdraw = total_income 不破。

验收线。

  • W-ASSET-MAT-VERIFY:登录用户只能浏览、选用自己的素材(服务端归属校验是可信边界);infra 上传成功后 studio 只登记 ref/url/元数据、不重持字节;六类枚举只允许 sprite/character/effect/scene/ui/music,MIME 错族服务端拒;选用只能写本人 DRAFT 会话,非草稿态拒;assetContext 能随 create/modify/extend 进入生成侧,生成侧留下可追踪引用。整条 upload→register→browse→select→进生成上下文在 staging 跑通(不是 mock 页面假绿)。开工前先核实一件事、别按「零新增能力」排期:assetContext 目前只见于 Java 编排层 DTO,生成 worker(cheap-worker / tier2)侧 grep 无 assetContext / materialId 消费,Java 执行器自己也注明「assetContext 仍 transient(引擎线后用)」。也就是说素材大概率还没真流进生成、更没回填归因。若核实确实如此,「生成侧消费 assetContext + 把用到的 materialId 回填进 trace / sourceProject」要显式列入②的范围与验收,不能当作「跑通已有骨架」白捡(类比 A11 便宜档 cheap-worker 当初不认 modify 的同类缺口)。
  • W-ASSET-MARKET-READONLY 进入条件:P-MAT 契约补齐可见范围状态机(私有 / 待审 / 公开 / 驳回)、授权类型、锁风标签、分成规则占位;素材审核门就绪(compliance/ip 的内容 / 版权审核流程 + 锁风标签写入者 + 服务端「过审才进公开货架」的强制点);溯源链阶段一的 lineage / assetRefs 契约已落 schema 与落库链(这是 assetRefs 归因的硬前置,不落它这条验收无处记);产品接受「只读 / 免费选用」中间态、明确它不代表可交易市场。验收:跨创作者货架只列过审的公开素材、可按 category/IP 检索;免费 / 本人自有素材选用进草稿并透传;选用后素材失效时生成侧重解析剔除;血缘 assetRefs 正确记录(在其字段落地之后)。
  • W-ASSET-MARKET-TRADE 进入条件:真实支付收单接进 game 业务;trade 增加 ASSET 收入来源与采购 / 授权幂等凭据;法务确认素材授权、IP 派生、商用分成口径。验收:采购走 trade 入账、素材作者按分成比例收到余额、uk_source 幂等不重复分账、账户恒等式不破;退款 / 撤权走 trade 冲正、不删行、账户恒等式不破(详细逆向口径随 MARKET-TRADE 执行计划对齐律所 + trade,此处先立占位锚点,别在后续单里被「已标缺口」当成已交代而漏掉)。

5. 范围与优先级

创始人 2026-07-05 已就范围拍板三项:① 同意把 W-ASSET 拆成源工程存储与素材市场两条线;② 同意下一步做「源工程长期存储 + 私有素材库真验」;③ 同意支付接通前先做资产市场只读半。据此拆成四段执行:

内容 排序 卡点
W-ASSET-SRC 源工程长期存储 / 版本寻址 / 取回重建 最优先 无外部闸门,验收纯工程可自闭合
W-ASSET-MAT-VERIFY 私有素材库 staging 真验 与 SRC 并行 范围小、可验;先核实生成侧是否真消费 assetContext(见 §4.3)
W-ASSET-MARKET-READONLY 支付前资产市场只读半 已拍方向,排期另切 前置 = P-MAT 契约增量 + 素材审核门 + 溯源链阶段一落 lineage/assetRefs 契约(跨线)
W-ASSET-MARKET-TRADE 购买 / 授权 / 分成 后置 被支付收单 + trade ASSET + 法务口径物理卡住

排序原则是一句话:能自己闭合的先闭合,受外部闸门卡住的不伪装成已完成。 源工程存储的验收不碰钱、不碰授权、不碰法务,直接服务生成主线可靠性,所以最优先、能自足。私有素材库真验范围小、和 SRC 无依赖,并行做(唯一要先核实的是生成侧是否真消费 assetContext,见 §4.3)。资产市场只读半已拍方向,但压着三道前置:P-MAT 契约把素材结构化、素材审核门就绪、以及溯源链阶段一先把 assetRefs 归因字段落进契约——后者是跨线依赖,不是选素材通道顺带就有。所以它排在真验之后、单独切执行计划。流通半被真实支付收单和法务口径两道外部闸门物理卡住,不是工程量问题,后置。

flowchart LR
  SRC["① W-ASSET-SRC<br/>源工程长期存储 / 版本寻址 / 取回重建"]:::now
  MV["② W-ASSET-MAT-VERIFY<br/>私有素材库 staging 真验"]:::now
  RO["③ W-ASSET-MARKET-READONLY<br/>资产市场只读半"]:::next
  TR["④ W-ASSET-MARKET-TRADE<br/>购买 / 授权 / 分成"]:::later
  SRC -->|并行·互不阻塞| MV
  MV -->|真链就绪| RO
  RO -->|真实支付收单 + 法务口径| TR
  PMAT["P-MAT 契约增量 + 素材审核门<br/>(素材中心 / compliance)"]:::dep -. 前置 .-> RO
  LIN["溯源链阶段一<br/>lineage / assetRefs 契约(数据飞轮 §4 阶段一)"]:::dep -. 跨线前置 .-> RO
  PAYG["支付收单 + 二清 / 牌照"]:::gate -. 前置 .-> TR
  LAW["法务 IP 派生 / 商用分成口径"]:::gate -. 前置 .-> TR
  classDef now fill:#e6ffe6,stroke:#3a3;
  classDef next fill:#fff7e6,stroke:#d90;
  classDef later fill:#f5f3ff,stroke:#7c3aed,stroke-dasharray:5 4;
  classDef dep fill:#eef2ff,stroke:#4f46e5;
  classDef gate fill:#fee,stroke:#c33,color:#900;

与数据飞轮资产层的一致性,要把两类资产分清楚,否则容易把源工程和素材混成一件事。数据飞轮 §3.2 的「资产沉淀」飞轮讲的是素材——私有素材库升级成货架、授权、分成。W-ASSET 里对得上它的是 MAT-VERIFY / MARKET-READONLY / MARKET-TRADE 这三段,它们严格落在 §3.2 的边界、§6 开放问题 4(2026-07-05 只读半可先上)之内,不改那份 canonical 的任何命题,只补落地做法。而 W-ASSET-SRC(源工程存储)不在数据飞轮的资产市场飞轮里——它补的是「源工程资产」这一类,服务生成主线可靠性和「游戏 = 长生命周期项目」范式。这里按 §1 说清一层关系:① 不是与素材资产毫无瓜葛,它存的那份 manifest 是收益回流语料的源工件、也是 assetRefs 归因将来的落脚处,它其实是两条飞轮共同的底座;说它「能先做」讲的是它自身验收不依赖钱 / 授权 / 法务,不是说它和另一条飞轮无关。两者都叫「游戏资产管理」,但是两类资产、两套机制、两个 owner,这次一起做,是因为一个是地基、一个是护城河。

分期编号对齐(避免和上级 canonical 双轨漂移)。 数据飞轮 §4 的权威分期是「阶段一溯源链 / 阶段二市场只读半 / 阶段三市场流通半 / 阶段四收益回流」。本档为落地另起了 ①②③④ 的执行拆段,两套编号必须对得上、且以数据飞轮 §4 为准,不另立编号真相:本档 ③ MARKET-READONLY = 数据飞轮 §4 阶段二只读半,④ MARKET-TRADE = 阶段三流通半;本档 ② MAT-VERIFY 是数据飞轮分期里没单列的一段——它是 §3.2「资产沉淀第一块砖」私有库从 mock 到 staging 真链的兑现,是市场化之前的前验、不属于 §3.2 描述的市场化设计本身;本档 ① SRC 不在资产飞轮分期内(它是源工程资产,见上一段)。数据飞轮 §4 阶段四(收益回流)不在本档范围。溯源链(阶段一)也不在本档范围,但本档 ③ 对它有跨线前置(assetRefs 字段),已在上表与 §4 登记。

6. 不留孤儿:新结构与接口的交付清单

项目硬约束:新数据结构 / 接口 / 领域模型,连同入口、使用路径、失败模式、验收一并交付。本次涉及的项集中列此,已在的标已在、要新增的标状态。

结构 / 接口 入口 使用路径 失败模式 验收 状态
game_source_project(Java) studio create/modify/extend 落库 A11 按 base_version_id 取 base 源注入 status=2 孤儿显式建模;落库失败不动 currentVersion 已生产真用 已在
tier2_source_project_version + BackendStore save = run.persist_source_project 收口(additive/best-effort,已接生产);fetch 生产消费方(tier2 第二装载)未建、本线只由 harness 验 首次生成 save 落 manifest + 全文;将来第二装载按 (game_id, versionId) fetch 重建(后续线) 半落库孤儿会真产生(versionId 含墙钟时间戳→重投换 MinIO 前缀、不自愈);修法二选一见 §3.3(manifest-first pending 或 source_hash 派生 versionId);fetch 缺源显式失败不静默重生成 接线 + 真 MySQL/MinIO 往返 + schema 治理归属 + 孤儿清扫 job(触发/调度/owner/回收断言) 真实现已写,待接线 / 真跑 / 治理 / 孤儿修法(§3.2/§3.3)
寻址三元组 { id, versionId, sourceHash } 两面各自 save/fetch 跨档取源统一按三元组寻址 三元组语义漂移由 doc↔code 断言点守 两面键名对齐、不合表 语义已在,契约点待立
game_material 市场化 additive 列 register 扩展 可见范围状态机(私有→待审→公开/驳回)+ 授权类型 + 锁风标签 + 分成占位 additive 不破存量私有库;素材转公开须过审、不能一步翻公开 P-MAT 契约落地(素材中心 owner;详细 schema 由 READONLY 执行计划先落 contract-first) 待建(只读半)
素材审核门 待审→审核→公开/驳回 锁风标签由审核写;服务端「过审才进公开货架」强制 无审核门则公开 UGC 货架不上线 compliance/ip 审核流程 + 服务端强制点就绪 待建(只读半硬前置,compliance/ip owner)
货架浏览 / 公开选用端点 studio.yaml 新增 market 端点 跨创作者货架检索(只列过审公开)→ 免费/本人自有素材选用进草稿 可见范围 / 审核态服务端强制,前端非边界;选用后失效生成侧重解析剔除 只读半 staging 真链 待建(只读半)
血缘 assetRefs 归因 选素材通道(P-CRT-04) materialId 作流通半分成依据 非同款顺带产出,需选素材才有;字段归宿(嵌 lineage vs 顶层)未决(§4.1) 选用市场素材时正确记录 契约未落地:数据飞轮 §3.1 提案、随溯源链阶段一落 schema(跨线前置,非「已预留」)
采购单 / 授权单表 + trade ASSET 枚举 新增采购端点 → trade 入账 付费采购 → 素材作者 share_rate 分成 uk_source 幂等防重复分账;退款走冲正不删行 支付接通 + trade ASSET + 法务口径 后置(流通半,外部闸门)

待拍板与后续动作

设计层面还有几处需要在切执行计划前定夺:

  1. 半落库孤儿的修法二选一(§3.3):manifest-first(先落 pending manifest 再 put 对象,+ status 列 + 按状态回收)vs versionId 只由 source_hash 派生(去时间戳)。前者更干净(隐形孤儿变可见 pending 行)、代价是加一列;后者动 derive_version_id 这个和 LocalFsStore 共享的代码契约、影响面更大。无论哪条,孤儿清扫 job 都是本线必交项。建议在 W-ASSET-SRC 执行计划里据代码影响面拍,倾向 manifest-first。
  2. tier2_source_project_version 的 schema 治理路线(§3.2 第二步):Flyway 迁移 vs 登记为 tier2 自有受治理 schema,取决于 tier2 与 huijing 是否共用同一 MySQL 实例。这是部署事实驱动的选择,建议在 W-ASSET-SRC 执行计划里据实拍。
  3. assetRefs 的契约归宿(§4.1):嵌在拟新增的 lineage 顶层对象里(但 lineage 只在同款时挂载,原创用市场素材就无处安放),还是抬成 source-project.schema.json 的独立顶层字段 / 独立关联表。这条与溯源链阶段一一并拍,是只读半 assetRefs 归因能不能落地的前提。
  4. ②开工前的 assetContext 消费核实(§4.3):生成 worker(cheap-worker / tier2)当前 grep 无 assetContext / materialId 消费、Java 执行器注明它「仍 transient」。②执行计划开工前先核实到底流没流进生成;若没有,把「生成侧消费 + 归因回填」列进②范围,别按「零新增能力」排期。
  5. 寻址是否要做一层薄的跨档查询门面:本档主张不合表、只统一寻址语义。若产品后续需要「一个游戏跨档查它所有源版本」的统一入口,再评估是否加一层只读查询门面——但不要提前造,避免孤儿抽象。
  6. 执行计划分别切:W-ASSET-SRC(接线 / 治理 / 取回重建真跑 / 孤儿修法)、W-ASSET-MAT-VERIFY(mock 换 staging 真链 + assetContext 消费核实)、W-ASSET-MARKET-READONLY(P-MAT 契约增量 + 素材审核门 + 货架 + assetRefs 归因,依赖溯源链阶段一,不接购买分成)。流通半(MARKET-TRADE)等支付与法务前置就绪再启动。