diff --git a/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md b/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md index 512ac4bdd..c7d0f7c49 100644 --- a/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md +++ b/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md @@ -913,7 +913,7 @@ procedure 另要求**受信服务身份**(归属判定靠 `owner_user_id`, | 母版仍按请求值 / 缺省 `forbidden` | `creation_authorization_uses_the_request_for_masters_and_fails_closed_on_unknown`:`forbidden` / `nonCommercial` / `full` 原样落库;空串与未知取值失败关闭且文案带 `FORK_AUTHORIZATION_UNKNOWN:`。DTO 侧的缺省值(省略 = `forbidden` 且幂等摘要不变)由既有用例 `create_game_request_defaults_fork_authorization_without_changing_digest` 继续覆盖 | ✅ 已验证(`cargo test -p spacetime-module`、`cargo test -p api-server game_distribution` → 104 passed) | | 契约不变 | `GameDistributionCreateGameRequest.forkAuthorization` 保留且 serde 形状未动(改形状会动幂等摘要);无列/表/索引变更,schema 基线比对通过 | ✅ 已验证(`check:spacetime-schema`:98 表对 `9f4c7d76` 通过) | | 门禁 | wasm build 0;`cargo check --all-targets` 0(仅既有 `TEST_AGC_MODEL_DEFAULT_ID` dead_code 警告);`cargo test -p module-game-distribution` 0(119 passed);`cargo test -p api-server game_distribution` 0(104 passed);`cargo test -p spacetime-module` 0(301 passed / 1 ignored);`cargo test -p spacetime-client` 0(43 passed);`check-game-distribution-dto-parity` 0;`check-project-bundle-policy-parity` 0;`SPACETIME_SCHEMA_BASE_REF=9f4c7d76 check:spacetime-schema` 0;`check:encoding` 0;`cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` 0;`git diff --check` 0;另跑 `check:doc-index` 0 | ✅ 已验证 | -| 遗留:两个 e2e 脚本仍按旧语义 | `scripts/check-game-distribution-lineage-e2e.mjs` 富树段(`createPublishedNode` 的 `promoteTo` 自带 `from = 'forbidden'`,且 `richF1` 刻意留给 `forbidden`)与 `scripts/capture-game-lineage-visual.mjs`(同款 `promoteForkAuthorization`):改口径后衍生节点的创建档位已等于父档位,这些「再从 `from` 提升」的 PUT 现在**一律**回 409 `FORK_AUTHORIZATION_INHERITED`(不再只是 CAS 不符),需改成「按新继承档位断言 / 不再提升」。两脚本不属本次工作树权限,未改(工作树里已见 `scripts/**` owner 并行改写的改动) | ⏳ 待改(需 `scripts/**` owner) | +| 遗留:两个 e2e 脚本仍按旧语义 | `scripts/check-game-distribution-lineage-e2e.mjs` 富树段(`createPublishedNode` 的 `promoteTo` 自带 `from = 'forbidden'`,且 `richF1` 刻意留给 `forbidden`)与 `scripts/capture-game-lineage-visual.mjs`(同款 `promoteForkAuthorization`):改口径后衍生节点的创建档位已等于父档位,这些「再从 `from` 提升」的 PUT 现在**一律**回 409 `FORK_AUTHORIZATION_INHERITED`(不再只是 CAS 不符),需改成「按新继承档位断言 / 不再提升」。**2026-10-07 已由 `scripts/**` owner 改写**:衍生节点改为「探针 PUT 被拒(409 `FORK_AUTHORIZATION_INHERITED`)+ 档位不变」,母版仍断言可提升;同轮补上「核心改动说明落在版本摘要(`currentVersion.changeSummary`)」与贡献归集断言(`fa9ef9037`;lineage 188 项、capture 工具 67 项全绿)。 | ✅ 已改(2026-10-07) | | 客户端 UI 的衍生场景 | 已落地(后续提交):`src/components/game-distribution/MyGamesPage.tsx` 对衍生作品隐藏提升入口、改为只读展示「继承自父作品」(`4f888d8c9`,含用例「衍生作品的授权入口改为只读展示(授权继承父作品,不可单独提升)」);AGC 发布面板 `GameDistributionPublishPanel.tsx` 同轮改为只读展示「授权继承自父作品」 | ✅ 已接(由 `src/**`、`apps/**` owner 落地) | --- @@ -946,7 +946,27 @@ procedure 另要求**受信服务身份**(归属判定靠 `owner_user_id`, | 文档 | 技术方案 §2.3(授权模式 + 状态机)、§2.4(`/games/mine`、`/games/publish`、`/games/lineage`)、§2.6 时序图、§3.3 状态表、§3.4(`PUT` 行、`POST /games` 行、`/lineage` 行)、§3.9 第二条补充、§7 第 2 条;数据契约文档 `fork_authorization` 条目;里程碑实施记录;`decision-log.md` 同日条目 | ✅ 已改 | | 门禁(本工作树自跑,逐条 exit) | wasm build 0;`cargo check --all-targets` 0(仅既有 `TEST_AGC_MODEL_DEFAULT_ID` dead_code 警告);`cargo test -p module-game-distribution` 0(**134 passed**,+3);`cargo test -p api-server game_distribution` 0(107 passed);`cargo test -p spacetime-module` 0(**305 passed / 1 ignored**,+1);`cargo test -p spacetime-client` 0(43 passed);`check-game-distribution-dto-parity` 0;`check-project-bundle-policy-parity` 0;`SPACETIME_SCHEMA_BASE_REF=9f4c7d76 npm run check:spacetime-schema` 0(98 表);`npm run check:encoding` 0(5544 文件);`cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` 0;`git diff --check` 0 | ✅ 已验证 | | 改动清单(测试) | **新增**:`module-game-distribution` 的 2 条行为用例 + `errors.rs` 1 条前缀用例、`spacetime-module` 1 条结构用例;**扩展**:api-server 的 `fork_error_codes_are_reachable_from_module_messages`(加一条码 + 常量字面量断言)、`module-game-distribution` 的 `fork_errors_are_prefixed_with_their_machine_code`(加一条码)。**没有既有 Rust 用例断言过「衍生作品 PUT 提升成功」**——旧语义的断言在两个 e2e 脚本里(见 §5.3 遗留) | ✅ 已改 | -| 遗留:两个 e2e 脚本 | `scripts/check-game-distribution-lineage-e2e.mjs`、`scripts/capture-game-lineage-visual.mjs` 里对**衍生作品**的 PUT 现在一律 409 `FORK_AUTHORIZATION_INHERITED`,需按新语义改写(按新继承档位断言 / 只对母版提升)。两脚本不属本次工作树权限,未改(工作树里已见 `scripts/**` owner 并行的改动)。前端入口与只读展示已落地(见 §5.3 对应的两行) | ⏳ 待接(`scripts/**` owner) | +| 遗留:两个 e2e 脚本 | `scripts/check-game-distribution-lineage-e2e.mjs`、`scripts/capture-game-lineage-visual.mjs` 里对**衍生作品**的 PUT 现在一律 409 `FORK_AUTHORIZATION_INHERITED`,需按新语义改写(按新继承档位断言 / 只对母版提升)。**2026-10-07 已改**:见 §5.3 同一行(`fa9ef9037`)——衍生作品 PUT 一律 409 `FORK_AUTHORIZATION_INHERITED` 已成断言口径,母版提升语义未变。前端入口与只读展示已落地(见 §5.3 对应的两行) | ✅ 已改(2026-10-07) | + +--- + +### 5.6 端到端实测覆盖与工具(2026-10-07,`scripts/**` owner) + +在**本地真实栈**上跑通的实测(地址从 `.app/dev-stack.json` 读:api-server `8082` / web `3000` / SpacetimeDB `3101` / admin-web `3102`,库 `genarrative-game-creator-dev`),按「平台 web → 客户端取件建项 → 回平台看血缘」这条链路分段列。**「覆盖到哪一段」以本表为准**(其余段落要么只能 API 等价复现,要么尚未自动化)。 + +| 链路分段 | 覆盖方式 | 脚本 / 证据 | 状态 | +| --- | --- | --- | --- | +| 平台 web:共创列表 / 作品详情 / 族谱页 / 主题页 | 真实浏览器(Playwright 驱动 Chromium,落 DOM 断言 + 截图) | `scripts/check-game-distribution-lineage-e2e.mjs`(F 段)、`scripts/capture-game-lineage-visual.mjs`、`scripts/check-game-distribution-theme-e2e.mjs` | ✅ 已覆盖 | +| Fork 授权三态(禁止 / 非商用 / 全开放)+ 衍生继承与「不可再提升」 | HTTP 契约(+ 浏览器入口) | `scripts/check-game-distribution-fork-authorization-e2e.mjs`;`…lineage-e2e.mjs` H 段(探针 PUT → 409 `FORK_AUTHORIZATION_INHERITED` 且档位不变;母版仍可提升) | ✅ 已覆盖 | +| 族谱与衍生:`/games/{id}/lineage`(按根聚合、200 上限、父作品下架降级)、`/derived` | HTTP 契约 + 浏览器 | `scripts/check-game-distribution-lineage-e2e.mjs`(A–E、G 段) | ✅ 已覆盖 | +| Fork 取件:成品包通道 + **工程源包下行优先** `/fork-source`;工程包上行校验(禁项 / 分片 / 幂等 / 阶段门) | HTTP 契约(无浏览器) | `scripts/check-game-distribution-project-bundle-e2e.mjs`、`…lineage-e2e.mjs` G 段 | ✅ 已覆盖 | +| 收藏(收录)与「我的收藏」 | HTTP 契约 | `scripts/check-game-distribution-collection-e2e.mjs` | ✅ 已覆盖 | +| 共创主题(公开读 + 后台写 + 作品详情 `themes` 增量 + 只允许根) | HTTP 契约 | `scripts/check-game-distribution-theme-e2e.mjs` | ✅ 已覆盖 | +| 贡献归集(作者视角 `GET /games/{id}/contribution`) | HTTP 契约(作者 Bearer) | `scripts/check-game-distribution-lineage-e2e.mjs` I 段 | ✅ 已覆盖 | +| **真实 AGC 客户端里点一遍**:深链 `genarrative://fork?gameId=…` 或共创列表「Fork」→ 取件校验 → 生成工程(写 `.agent/fork-source.json`)→ 客户端发布并声明 `fork` | **未自动化**(仓库内无客户端 CDP/UI 自动化)。客户端为 Tauri + WebView2,可 attach 前提是**启动时**带 `--remote-debugging-port=9222`(`~/.config/genarrative/.env` 的 `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS`,仅对变量存在时**新启动**的实例生效;探活须 `curl --noproxy '*' http://127.0.0.1:9222/json/version` 应回 `Edg/…`) | 备用路径:**API 等价复现**同一链路 —— `/fork-source` 取件元数据 → 按 `downloadPath` 下载工程包/成品包 → 建项 → 首次发布携带 `fork` 声明 → 回平台断言血缘 / 代际 / 贡献 | ⏳ 待补 | +| 演示数据与视觉基线(供需求方验收) | 幂等灌数据 + 截图工具 | `scripts/seed-game-distribution-co-creation.mjs`(标题前缀 `共创演示·`,**判重不绑账号**:先查公开目录同标题再查本人 `my-games`;13 节点 / 3 棵树 / 4 作者 / 最大 3 代 / 13 张不同封面)、`scripts/capture-game-lineage-visual.mjs`(`E2E_CAPTURE_LINEAGE_URL` 开启「只截现有作品」模式) | ✅ 已覆盖 | + +实测固定口径(可直接复用):pinned 端口 `--api-port 8188 --bgfilter-worker-port 8189 --spacetime-port 3110 --admin-web-port 3111 --web-port 3112`;管理员 `admin / 123456`;Playwright 走临时目录(`E2E_PLAYWRIGHT_DIR`)+ 本机 Chromium(`E2E_CHROMIUM_EXECUTABLE`);断言口径一律按**符号**引用实现(不写行号),详见各脚本头部注释。 ---