suzmii
|
58da0e67db
|
test(游戏共创): 新增共创主题端到端验收脚本
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
- 新增 scripts/check-game-distribution-theme-e2e.mjs(约 1160 行):真库验收公开读 + 后台写 +
作品详情 themes 增量,复用 lineage 脚本的完整发布链路 helper(脚本头注释逐条标注出处),
覆盖 9 组共 54 条断言:
· 建主题→加根成员→公开读:公开列表只含 published(且不含 status 字段)、详情 roots 逐条与公开
目录同一投影(键集合 23=23)、memberCount 与 roots 长度一致
· 只允许根:第 1 代子作品当成员 → 409 THEME_MEMBER_NOT_ROOT;不存在的 gameId → 404 THEME_MEMBER_GAME_NOT_FOUND
· 跨主题多归属:同一个根加入两个主题,两条主题都能读到它,作品详情 themes 长度=2
· 作品详情 themes 口径:匿名与登录都发;**专门构造第 1 代衍生作品**(带 fork 声明 + 完整发布链路)
并断言它也返回根所属的两个主题(先取根再反查);无归属作品返回空数组
· 可见性:draft 主题公开 404(后台按 draft 过滤仍可见)、archived 公开 404 且作品详情 themes 同步消失、
改回 published 恢复;已发布但成员全不可见 → 200 + 空 roots + memberCount=0;
成员下架 → 从 roots 消失,重新公开(新版本审核通过)→ 同一行自动回到 roots 且仍只一行
· 分页:默认 20、limit=999 截断到 50、limit=20 游标翻页 3 页 54 条不重不漏、末页 nextCursor=null、
?cursor=abc → **400**(状态码符合契约)
· 后台权限:五条路由未登录 401;普通作者 token 403;成员 PUT/DELETE 免幂等键、重复 PUT 只留一行
且 createdAt 不变、DELETE 幂等(data 载荷逐字段相同,信封 meta.requestId 每请求不同属既有协议)
· 后台幂等:同键重放 replayed=true 且不产生第二个主题、同键换请求 409 THEME_IDEMPOTENCY_CONFLICT、
缺 Idempotency-Key → 400
· 三条公开路由匿名 200 且带 Cache-Control: no-store
- **实测 53 PASS / 1 FAIL**:唯一失败是已确认的产品缺口(仅报告、未改后端)——非法游标的状态码是
400 ✓,但稳定码 THEME_INVALID_CURSOR 不可达:api-server/...:5343-5346 为该码注册了 400 映射,
而模块侧只回纯文案「主题列表游标格式无效」(module-game-distribution/src/theme.rs:205-215),
不含分支前置所需的 `THEME_` 前缀,客户端实际拿到 code=BAD_REQUEST。脚本刻意保留这条红断言
(契约真值),并附 NOTE 说明成因与证据。
- package.json:新增 npm script check:game-distribution-theme-e2e
|
2026-10-06 03:44:31 +08:00 |
|
suzmii
|
742723a58c
|
feat(游戏共创): 共创主题(二)公开读路径(事务 + client + 两个公开路由 + 详情 themes 增量)
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m44s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m35s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m37s
Project CI / Backend tests (pull_request) Successful in 9m29s
Project CI / Frontend tests (pull_request) Successful in 3m7s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m7s
Project CI / Repository checks (pull_request) Successful in 6m48s
Project CI / Native shell tests (pull_request) Successful in 8m39s
- **事务**(`spacetime-module`):公共列表 `list_game_distribution_themes_tx`(匿名可读;先按 `published` 过滤、再排序切页——`created_at` 倒序 + `theme_id` 兜底;只对当页现算 `member_count`;不写库);主题详情 `get_game_distribution_theme_detail_tx`(不存在 / 未发布**同一句**「主题不存在」→ 404;`roots` 逐条复用公开目录同一份 `public_game_distribution_snapshot`;成员可见性委托 `game_distribution_theme_member_visible`,被滤掉的行**不删除**;已发布但可见成员为空 = Ok + 空 roots = 200);作品→主题 `list_game_distribution_theme_refs_for_root_tx`(入参任意代作品,先取**根**再按根反查成员、只保留 `published` 主题;排序复用 `sort_public_themes`)。**根解析只有一份实现**:抽成 `game_distribution_theme_root_of` 并让既有 `game_distribution_lineage_tx` 复用(测试钉住)。
- **client**:3 个新方法 + mapper 记录类型(`spacetime-client`)。
- **公开路由**(`api-server`):`GET /api/game-distribution/themes?limit=&cursor=`(匿名可读 + `no-store`;`limit` 缺省 20 / 上限 50 / 超界截断;非法游标 **400** 透传;末页 `nextCursor: null`)、`GET /api/game-distribution/themes/{theme_id}`(顶层扁平 `{themeId,name,summary,badge,memberCount,roots}`;不存在 / draft / archived → **404**,不回空壳、不带 `THEME_` 码)、既有公开作品详情追加 `themes`(匿名与登录**都**发、空数组恒发;`collected` 仅登录发的老口径不变;因按根反查,**第 N 代作品也能跳到主题页**)。
- **错误码**:新增 6 个 `THEME_*` 稳定码集中在 `map_spacetime_error` 映射(不落 axum 默认 422 纯文本);未登记码兜底 **400 `THEME_ERROR`**。
- **契约/parity**:5 条手拼响应构建器登记(`public_themes_payload` / `public_theme_detail_payload` / `public_theme_summary_payload` / `theme_reference_payload`,并给 `public_game_detail_payload` 加 `mustEmit: [themes]`)→ 58 组类型 + 10 个构建器一致。
- **测试 +13**:spacetime-module 4 条源码结构断言(根解析单一实现、先过滤再切页、投影不删行、详情只有两种失败且同一 404 文案、refs 走具名根索引 + 只保留 published)、api-server 7 条(路由挂载与 `no-store`、列表/详情响应形状与末页 null、`roots` 逐字节等于 `public_game_payload`、limit 缺省与截断、非法游标 400、404 与 6 个 THEME_ 码映射、作品详情 `themes` 恒发而 `collected` 仅登录)、client mapper 2 条。
- **门禁**:wasm build 0;`cargo check --all-targets` 0;`cargo test -p api-server game_distribution` **79 passed**;`cargo test -p module-game-distribution` **97 passed**;`cargo test -p spacetime-module` **278 passed** / 1 ignored;`cargo test -p spacetime-client` 34 passed;DTO parity **58 组 / 10 构建器**;`check:spacetime-schema` 0(96 tables);`check:encoding` 0(5399 files);`cargo fmt --all -- --check` 0;`git diff --check` 0。
已知限制(**已上报,待拍板**):主题详情的 `roots` 沿用页上限 50,`memberCount == roots.len()`,因此可见成员 > 50 时**静默截断且无「还有更多」标志**——设计文档未定义该行为,三个备选(去上限 / 加成员游标 / memberCount 报真实数)待定;本轮不改契约。
|
2026-10-06 02:15:03 +08:00 |
|
suzmii
|
2fa201e0dc
|
feat(游戏共创): 共创主题(一)数据模型与领域纯函数
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m30s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m12s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m44s
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
按 `docs/【技术方案】游戏共创与作品Fork-2026-10-03.md §3.10` 与里程碑《共创主题与作品树-2026-10-06》的清单 A/B/F 落地(本块**不含** procedure / 路由)。
- **表**:`game_distribution_theme`(`theme_id` 主键、`name`/`summary`/`badge`/`sort_order`/`status ∈ draft|published|archived`/`created_by_user_id`/`created_at`/`updated_at`)+ `game_distribution_theme_member`(确定性主键 `member_id = "{theme_id}:{root_game_id}"`,`theme_id`、`root_game_id` 各具名 btree 索引,`sort_order`、`created_at`);归档 / 下架只改公开投影,不删成员行。`migration.rs` 白名单登记两张表(随迁移导出/导入)。
- **生成绑定**:4 个 `game_distribution_theme*` 生成文件 + `module_bindings.rs` 入口(纯追加)。生成时**不覆盖工作树**(本仓共享工作树禁止 git 写命令):先 `spacetime generate --lang rust --out-dir <仓外临时目录>`,rustfmt 后只拷回本次相关文件,避免上一轮踩过的「rustfmt 漂移把别人文件一起改」。
- **领域纯函数**(`module-game-distribution/src/theme.rs`,风格照同目录 `collection.rs`):状态合法/公开可见判定、`game_distribution_theme_member_id`、成员可见性(**委托** `collection.rs` 的判定,不复制第二套)、`game_distribution_theme_root_acceptable(has_lineage_row)`、页大小(缺省 20 / 上限 50 / 0 取默认 / 超界截断)、游标编解码(`"{createdAtMicros}:{themeId}"`,Err 文案「主题列表游标格式无效」,已核对不含 404/409 关键词)、`sort_public_themes`(`created_at` 倒序 + `theme_id` 兜底)、`page_public_themes`(排序 → retain 游标之后 → 多取一条判 has_more;**先过滤再切页**的顺序语义写进文档注释)、`sort_theme_members`(`sort_order` 升序 + `member_id` 兜底)与 `page_theme_members`。
- **契约**:`shared-contracts` 新增 `GameDistributionThemeStatus` / `GameDistributionThemeReference` / `GameDistributionThemeSummary` / `GameDistributionThemeListResponse` / `GameDistributionThemeDetail` / `GameDistributionCreateThemeRequest` / `GameDistributionUpdateThemeRequest` / `GameDistributionUpsertThemeMemberRequest`,`GameDistributionGameSummary` 追加可选 `themes`(按 `collected` 先例只在详情路径下发);TS 镜像同步;parity 登记 **58 组**(响应构建器待路由落地后再登记,parity 要求函数真实存在)。
- **数据契约表目录**:补两张表小节,94 → **96**。
- **测试**:`theme.rs` 16 个单测(成员 id 确定性、状态三态与公开可见、成员可见性逐格与收藏判定等价、根判定、页大小边界、游标往返与非法输入、排序稳定性与同 `sort_order` 兜底、分页不重不漏)。
- **门禁**:wasm build 0;`cargo check --all-targets` 0;`cargo test -p module-game-distribution` **97 passed**;`cargo test -p spacetime-module` 274 passed / 1 ignored;DTO parity **58 组**;`check:spacetime-schema` 0(**96** tables);`check:server-rs-ddd` 0;`check:encoding` 0(5388 files);`cargo fmt --all -- --check` 0;`git diff --check` 0(8 个改动文件 0 删除行)。
|
2026-10-06 01:46:23 +08:00 |
|
suzmii
|
f09607d903
|
feat(游戏共创): 收录(收藏)列表补真分页(cursor + limit)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- 纯函数(`module-game-distribution/src/collection.rs`,与收藏另两条规则同文件):`encode/parse_game_distribution_collection_cursor`(`"{created_at_micros}:{collection_id}"`,复用仓库既有 `"{micros}:{id}"` 惯例;非法 → Err「收藏列表游标格式无效」→ 400)、`game_distribution_collection_page_limit`(0/缺省 → 20,超界 → 50)、`game_distribution_collection_page`(稳定全序排序 → 跳过游标之前 → 取 limit → 多取一条判 has_more,只对页内最后一条编码下一游标)
- 排序与顺序语义:`created_at` 倒序 + `collection_id` 升序兜底(主键、同用户内唯一 ⇒ 全序,翻页不重不漏);**先按可见性过滤、再排序切页**——游标位置必须落在已过滤序列上,否则被过滤的行会让下一页漏项(推理写进 tx 与纯函数注释);snapshot 只对最终页成员计算
- 模块:list 的 input 增 `limit` / `cursor`,result 增 `next_cursor`;api-server `my-collections` 加 `Query<MyCollectionsQuery>`,默认 20 / 上限 50(网格一屏 vs 单响应体积;与后台列表的 200 口径不必相等,理由写进注释),超界**截断**不报错,非法游标 400 透传不吞,日志带 `has_more`
- 契约:Rust DTO 增 `next_cursor`、TS 镜像同步、parity 登记(50 组一致)
- 文档:§3.4 的 `my-collections` 行补分页语义(默认/上限/游标格式/非法 400/末页 null/排序口径)
- 测试 13 条:纯函数 8(不足一页 → None;恰好整页不产生空游标;5 条 limit=2 连翻 3 页不重不漏;同 `created_at` 用 `collection_id` 兜底;limit=0 与超界;游标往返 + 7 种非法输入)、模块事务结构 2、api-server 3(默认与截断、`nextCursor` 透传、非法游标 → 400)
- 门禁:wasm build 0;`cargo check --all-targets` 0;`cargo test -p module-game-distribution` **81 passed**;`cargo test -p api-server game_distribution` **73 passed**;`cargo test -p spacetime-module` 274 passed / 1 ignored;DTO parity **50 组**;`check:spacetime-schema` 0(94 tables);`check:encoding` 0(5382 files);`cargo fmt --all -- --check` 0;`git diff --check` 0
|
2026-10-06 01:31:09 +08:00 |
|
suzmii
|
eb7349ef3d
|
test(游戏共创): 新增收藏(收录)端到端验收脚本
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- 新增 scripts/check-game-distribution-collection-e2e.mjs(约 750 行):真实 HTTP 验收收藏链路,
复用 lineage 脚本的发布链路 helper(脚本头注释逐条标注出处),覆盖 9 组共 46 条断言:
· 幂等与「只一行」:两次不同键都成功(replayed=false);同键重放 replayed=true;两种情况都断言
my-collections 长度=1 且该作品只出现一次(无收藏计数接口,按工单允许的列表去重与长度证明)
· 同键不同请求:换作品 → 409(「幂等键对应的请求摘要不一致」);**换用户 → 200 新收藏**,与
api-server handler 注释「换用户也是 409」不一致(收据键含 user_id,模块侧注释与实测一致),
脚本按实测断言并在 NOTE 与报告里标注
· DELETE 幂等:已收藏 / 未收藏 / 从未收藏且未公开 三种情形都 200 {collected:false} 且不发 replayed
· 鉴权与缓存:三条路由未登录都 401 且带 Cache-Control: no-store(证明挂载而非 404/405);
已登录但缺 Idempotency-Key 的 PUT → 400
· 状态门:草稿 PUT → 409「作品状态不允许收藏(未公开或已软删除)」(含「状态」不含「不存在」,
不泄露未公开作品存在性);不存在的 gameId → 404「作品不存在」
· 下架 → 重新公开:收藏后下架读侧过滤掉该作品,重新公开(新版本审核通过)后又回到列表且仍只一行
(证明下架不删行、只在读侧过滤)
· 公开详情 collected:匿名 23 键无 collected;已登录 24 键恰好多 collected=true;
去掉该键后与匿名逐字段一致;公开目录同作品条目与匿名详情键集合与取值完全一致
· 跨用户隔离:A 收藏后 B 的列表不含该作品、B 的 collected=false
- package.json:新增 npm script check:game-distribution-collection-e2e
- 实测:本地 dev 栈(SpacetimeDB 3110 / api-server 8188)46 项 46 PASS / 0 FAIL / 0 SKIP
|
2026-10-06 01:30:53 +08:00 |
|
suzmii
|
4b1f24a8fc
|
feat(游戏共创): 收录(收藏)契约与文档同步(块 3/3)
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
- `shared-contracts`:新增 `GameDistributionCollectionState { collected, replayed: Option<bool> }` 承载 PUT/DELETE 共用形状(PUT 才发 `replayed`);既有 `GameDistributionGameSummary` 增 `collected: Option<bool>`(公开详情登录时才带)——该类型只被 api-server 测试与 `GameDistributionListResponse` 引用,加字段不影响任何现有发送方
- `packages/shared` TS 镜像同步(`{ collected: boolean; replayed?: boolean }` 与 `GameDistributionGame.collected?: boolean`),并按既有约定登记进 `scripts/check-game-distribution-dto-parity.mjs`(PAIRS + 手拼响应构建器);注意 `json!` 字面量里不插注释行(parity 脚本抓顶层键时不剥注释,这个坑今天踩过一次)
- 技术方案:`§2.4` 入口矩阵补「收藏按钮 / 我的收藏(登录)与我的作品页同行风格」;`§3.4` 接口表补三条路由(含 404/409 语义、PUT 要求幂等键而 DELETE 不要求及理由、`my-collections` 只含公开作品且不删行)与公开详情 `collected` 的可见性口径;`§7` 把「收录/收藏」从范围外移除并标注已纳入落地
- 数据契约表目录:新增 `game_distribution_collection` 小节(主键确定性、两个索引、为什么不删行、为什么不建唯一索引),供 `check:spacetime-schema` 守卫通过(94 tables)
- 门禁:DTO parity 0(**49 组** Rust/TS 一致,较本轮前 +7);`check:spacetime-schema` 0(94 tables,对照新 merge-base `51cb05f4`);`check:encoding` 0;`git diff --check` 0
|
2026-10-06 01:14:05 +08:00 |
|
suzmii
|
bdcefe91d1
|
Merge remote-tracking branch 'origin/master' into feat/game-fork
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m20s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m3s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Backend tests (pull_request) Successful in 7m36s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m26s
Project CI / Repository checks (pull_request) Successful in 6m25s
Project CI / Native shell tests (pull_request) Successful in 9m34s
# Conflicts:
# deploy/container/nginx.conf
# deploy/nginx/genarrative-dev-http.conf
# deploy/nginx/genarrative.conf
# server-rs/crates/api-server/src/modules/game_distribution.rs
# src/components/game-distribution/GameDetailPage.tsx
# src/components/game-distribution/GameDistributionPages.test.tsx
# src/components/platform-entry/PlatformEntryActiveFlowShell.tsx
|
2026-10-06 00:19:40 +08:00 |
|
lhk229
|
51cb05f418
|
补充创作者主页与关注粉丝工程文档 (#638)
Project CI / AI game creator shell Rust crates (push) Successful in 5m56s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 6m36s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 7m37s
Project CI / Backend tests (push) Successful in 9m22s
Project CI / Frontend tests (push) Successful in 4m40s
Project CI / AI game creator shell web tests (push) Successful in 3m50s
Project CI / Repository checks (push) Successful in 6m30s
Project CI / Native shell tests (push) Successful in 8m32s
本 PR 补齐创作者主页与关注粉丝功能的产品合同和工程设计,供后续按里程碑实施;本次只修改文档。
桌面导航第四项进入自己的创作者主页,“我的”移到第五项。游戏详情可进入作者主页并关注/取消关注;自己或他人的关注、粉丝列表均公开可查看,点击用户头像或昵称可进入其主页。本人支持取消/重新关注、回关和移除粉丝,并明确两个关注方向独立。
工程设计包含关系表与 DTO、认证和事务边界、跨页状态同步、共享组件、深链及验收要求,拆分关系查询和页面交互两个里程碑。按用户要求,游戏列表沿用最多 48 项的现有限制,不增加游戏目录分页改造。
验证:
- 已合入最新 origin/master(21c79f54a);共享决策记录冲突保留双方新增内容。
- npm run check:encoding 通过。
- npm run check:doc-index 通过。
- git diff --check 与暂存差异检查通过。
- 相对 master 仅六份文档发生变化;没有业务代码或 schema 修改,未执行业务测试或部署。
Reviewed-on: #638
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
|
2026-10-05 22:05:11 +08:00 |
|
suzmii
|
126fb7bc06
|
test(游戏共创): 两个浏览器 e2e 的登录等待抗冷启动(Vite 首次编译)
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m3s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m53s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m31s
Project CI / Frontend tests (pull_request) Successful in 2m58s
Project CI / Backend tests (pull_request) Successful in 7m44s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m6s
Project CI / Repository checks (pull_request) Successful in 6m45s
Project CI / Native shell tests (pull_request) Successful in 9m32s
- rebase 到 origin/master 后的第一次实跑里,lineage 与(同源的)fork 脚本在浏览器段都卡在
`page.waitForSelector('.platform-account-entry', { timeout: 120_000 })` 并超时:
Vite dev server 重启后首次导航要现编译整张模块图,rebase 引入大量新代码时首屏会超过 120s。
证据:两次运行都在该行超时退出;随后用 Playwright 直接探针(同一 URL)页面正常渲染、
`.platform-account-entry` count=1、无 pageerror(仅两条未登录态 401 的资源错误),
紧接着再跑同一脚本即 99/99 全绿。
- 修法:登录等待改成 240s + state:attached,并在超时后重载一次再等(避免把「开发服务器还没编译完」
误判成页面坏了)。两处改动逐字相同,lineage 与 fork 脚本各一处。
- 实测(HEAD 3ae0d129e,固定端口栈 3110/8188):lineage 99/99、fork 48/48、project-bundle 72/72;
三条只读门禁 policy-parity / dto-parity / spacetime-schema 均 exit 0。
|
2026-10-05 17:52:16 +08:00 |
|
suzmii
|
3ae0d129ef
|
test(游戏共创): 一致性门禁把内容嗅探与 magic bytes 纳入比对(补最后一处规则空白)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- `scripts/check-project-bundle-policy-parity.mjs` 新增第 4 维:**凭据内容嗅探与嵌套包 magic bytes**。复核指出这两套「换名字也拦得住」的规则只靠常量表表达,过去门禁对 `sniff|PEM|AKIA|magic|secret` 零命中(只有看得见的规则才有守卫),本次补齐:
· `SECRET_CONTENT_TEXT_EXTENSIONS` / `SECRET_CONTENT_SIGNATURES`(字符串数组集合相等)、`SECRET_CONTENT_MAX_SNIFF_BYTES` / `NESTED_ARCHIVE_SNIFF_BYTES`(整数相等)、`SECRET_AWS_ACCESS_KEY_PREFIX` / `PEM_PRIVATE_KEY_LINE_PREFIX` / `PEM_PRIVATE_KEY_MARKER`(字符串逐字相等)——新增 `rustStringConst` helper 抓 `&str` 常量
· `nested_archive_format` 里的 `b"..."` magic 字面量按「服务端 ⊆ 客户端」比对(客户端可以先拦、不能漏拦),并用新的 `byteStringLiterals` helper 抽取;两侧空集合一律 throw
- **「故意改一侧会红」已验证(两处,均已原样恢复)**:① 从客户端 `BUNDLE_FORBIDDEN_PATH_CHARS` 去掉 `'?'`/`'*'` → 报两条「路径段禁止字符…客户端没有」;② 从客户端 `SECRET_CONTENT_SIGNATURES` 去掉 `ghp_` → 报「嗅探规则不一致:服务端 …=ghp_|github_pat_|…,客户端 …=github_pat_|…」
- 文档 §3.5.2 与 §5.2 同步门禁的四个覆盖维度(目录/前缀/后缀/全名 + 上限、路径形状、内容嗅探特征表、magic bytes),并把 §5.2 的门禁行从「P1-a」扩为「P1-a + 嗅探维度」
- 门禁:`check-project-bundle-policy-parity` 0(服务端 51 条规则全被客户端覆盖;嗅探:`ghp_/github_pat_/xoxa-/xoxb-/xoxp-` + `AKIA` + PEM 标记;magic:7z / PK / Rar! / gzip / ustar;窗口 512 字节两侧一致);`check:doc-index` 248 份 OK;`check:encoding` 5305 files OK;`git diff --check` 0
|
2026-10-05 17:42:46 +08:00 |
|
suzmii
|
df43ffd05a
|
test(游戏共创): 工程源包 e2e 补对抗用例(路径/凭据/嵌套包/符号链接/声明说谎)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- scripts/check-game-distribution-project-bundle-e2e.mjs 新增 A7 对抗段(脚本内自带裸 ZIP 写手,
可逐字节控制条目名、unix mode 与 central directory 声明值),逐条断言 422 + details.reason:
· A7-1 路径穿越:foo/../bar、/etc/passwd、C:/evil.txt、a\..\b、./x、a//b → InvalidPath
· A7-2 路径形状:a/secret.、a/secret(尾随空格)、src/a?.ts、a*b.ts → InvalidPath
· A7-3 大小写变体:Node_Modules/… → DependencyDirectoryNotAllowed;.GIT/HEAD → VersionControlDirectoryNotAllowed;.AGENT/x → LocalStateDirectoryNotAllowed
· A7-4 符号链接条目(external attrs=0o120777)→ SymlinkNotAllowed
· A7-5 嵌套包改名 deps.dat(zip magic)→ NestedArchiveNotAllowed
· A7-6 凭据:.aws/credentials、.ssh/id_ecdsa → CredentialDirectoryNotAllowed;.htpasswd、service-account-prod.json、terraform.tfstate、app.p8 → SensitiveFileNotAllowed
· A7-7 内容嗅探:无扩展名文件里的 PEM 私钥块 → SecretContentDetected
· A7-8 声明说谎:STORE 条目声明 1 字节、实际 4096 字节 → ReadFailed(失败关闭;非内存量测)
· A7 收尾:全部拒绝后该版本仍 bytes=0/sha256 空;对照包(.dat/文本/无扩展名/PNG)200 不被过度拦截
- 修正一处我此前的错误判断:`./x`/`a//b` 并非「被 zip crate 归一化后放行」,而是被校验器自身的
路径形状检查(空段/`.`)拒为 InvalidPath(zip-2.4.2 types.rs:537-555 只拒 NUL/根/`..` 逃逸,
且返回未归一化原串);脚本 NOTE 已按实测与源码改正,两条也改为严格 422 断言
- 实测:本地 dev 栈(SpacetimeDB 3110 / api-server 8188)72 项 72 PASS / 0 FAIL / 0 SKIP;
同栈回归 lineage-e2e 99/99(真实发行包上传,覆盖 package.rs 读取封顶改动)与
fork-authorization-e2e 48/48
|
2026-10-05 17:30:50 +08:00 |
|
suzmii
|
88368d0418
|
test(游戏共创): 一致性门禁补「路径形状」维度,并同步校验器文档
- `scripts/check-project-bundle-policy-parity.mjs` 新增第 3 维:服务端 `normalize_archive_path` 的拒绝形状必须被客户端打包器的三个常量逐 token 覆盖——**哨兵段**(`.` / `..`)与**路径段结尾字符**(空格 / 点)分别用 `rustStringArray` / `rustCharArray` 抽取,**路径段禁止字符**同时抓 `.contains('x')` 与 `matches!(expr, 'a' | 'b')` 两种写法;再单独钉「两侧都必须显式拒 `/` 开头与 `\`」两条独立分支(它们靠常量表覆盖不到)。**空集合一律 throw**,沿用既有「抽不到不许当绿灯」的写法
- 修掉我自己在第一版里写错的两处(由补强过程中的实测发现,并已用临时副本验证修法):① `BUNDLE_FORBIDDEN_PATH_SEGMENTS` 是 `&[&str]` 而非 `&[char]`,须用 `rustStringArray`(`".."` 也无法用 char 字面量表达);② 反斜杠分支的正则写成了 4 个字面反斜杠,而源码是 `contains('\\')`(2 个),已改为 `/contains\('\\\\'\)/u`
- **「故意破坏会红」已验证**:临时从客户端 `BUNDLE_FORBIDDEN_PATH_CHARS` 去掉 `'?'` 与 `'*'` → 门禁立即报两条「路径段禁止字符:服务端有「?」/「*」,客户端没有」,随后已原样恢复
- 文档 `§3.5.2/§3.2.3`:校验器清单同步到新规则(新增目录/前缀/后缀/全名 + 内容嗅探及其取舍理由、嵌套包扩展名并集与 magic 嗅探、读取层封顶与压缩比口径更正、路径形状维度),并新增**产品口径「知情同意」**一条——清单是黑名单,`docs/`、`*.pdf`、`notes.txt`、截图等不在任何拒绝集内会原样外发,发布面板必须写明「整个项目目录(除少数排除项)会原样公开」
- 门禁:policy parity 0(服务端 51 条全被客户端覆盖 + 路径形状维度 OK);`check:doc-index` 248 份 OK;`check:encoding` OK;`git diff --check` 0
|
2026-10-05 17:30:50 +08:00 |
|
suzmii
|
360cb9830a
|
test(游戏共创): 新增工程源包(M2b)端到端验收脚本
- 新增 scripts/check-game-distribution-project-bundle-e2e.mjs:真实 HTTP 验收 M2b 上行族与下行优先
· A1 合法工程源包整包 PUT → 200;版本私有 payload 的 projectBundleBytes/projectBundleSha256 与上传一致且不含对象键
· A2 >8 MiB 不可压缩负载两片续传:chunkBytes=8388608,偏移 8388608→9438176,upload-state 权威偏移逐片一致,complete 后 bytes/sha256 一致
· A3 含 .env → 422 PROJECT_BUNDLE_VALIDATION_FAILED,且该版本仍 bytes=0 / sha256 空
· A4 二次上传 → 409 PROJECT_BUNDLE_ALREADY_EXISTS
· A5 已公开且已有工程包再传 → 409 ALREADY_EXISTS(阶段门先判已存在);A5b 已公开但无工程包补传 → 409 PROJECT_BUNDLE_UPLOAD_NOT_ALLOWED
· A6 未带 Bearer → 401;另一作者 token → 404(实现按不存在处理,脚本按实现断言并标注与工单 403 的差异)
· B7/B8 发布后 fork-source source=project、sha256/bytes 一致、downloadPath 指向 project,下载校验 zip/length/sha256/解压条目
· B9 对照作品(无工程包)source 回落 package,/fork-source/project → 409
- package.json:新增 npm script check:game-distribution-project-bundle-e2e
- 复用 lineage 脚本的发布链路与 helper(脚本头注释标注出处),未抽公共模块以免改动其它脚本
- 实测:本地 dev 栈 46 项 46 PASS / 0 FAIL / 0 SKIP
|
2026-10-05 17:30:48 +08:00 |
|
suzmii
|
524ca890c6
|
feat(游戏共创): 工程源包规则与服务端对齐,并加机器 parity 门禁
规则对齐(以服务端为权威,`module-game-distribution/src/project_bundle.rs`):
- 凭据/隐私两侧补齐:客户端新增 `.git-credentials`、`id_rsa*`、`id_ed25519*`、`*.map`,
并把 `.env` 放宽成服务端同形的 `starts_with(".env")`(`.envrc` 之类一并排掉)。
反向无需补:客户端没有服务端缺的凭据类规则,比对一次后两侧凭据集合完全一致。
- 新增被漏掉的嵌套压缩包规则:服务端对任意层级 `*.zip` 一律拒收(解包阶段不递归校验,
等于绕过整份清单),客户端原来会把它打进包里 → 现在同样排除。
- `.agent` 由「项目根首层」对齐到**任意层级**(服务端口径;平台保留名两边一致比更宽松重要)。
- 客户端额外项保留并逐项写明理由:`game/dist`、`game/build`(AGC 脚手架构建输出)、
`.godot`(编辑器缓存,模板包指南同规则)、`exports`/`memory`(AGC 生成物与 Agent 记忆)。
- 规模上限统一到服务端数值:压缩包 ≤ 200 MiB(新增)、展开 ≤ 500 MiB、单文件 ≤ 64 MiB、
条目 ≤ 10 000、单文件 ≤ 包体 × 100(新增)。客户端先拦,不再出现「传到一半被 422 拒掉」。
- 顺手补上服务端已有的「大小写折叠后重名条目」防线:客户端提前失败关闭。
可机器比对的常量:规则集拆成 `BUNDLE_EXCLUDED_*` 常量、上限拆成 `PROJECT_BUNDLE_MAX_*`,
新增 `scripts/check-project-bundle-policy-parity.mjs` 逐 token/逐数值比对两侧(服务端每条规则
必须在客户端存在,客户端额外项只打印不算失败;抽不到 token 直接报错,避免假绿),并照既有
DTO parity 的形态接到 `package.json` 的 `check:*` 与 `lint` 链。
测试:客户端 17 条(新增 `.git-credentials`/`id_rsa`/`*.map`/`.envrc`/`.zip`/任意层级 `.agent`、
压缩包上限、压缩比上限各自一条;合同常量锚点改为与服务端逐项相等)。
|
2026-10-05 17:30:48 +08:00 |
|
suzmii
|
f40050afb1
|
收口 M3 复核遗留:文档如实口径、fork-source 状态说明、forkAuthorization 机器守卫、e2e 引用去行号
- 技术方案 §3.4:删掉「GET /games/{gameId} 追加 forkSourceAvailable」——实现里不存在该字段;改为如实口径:改造入口显隐只依据 forkAuthorization,真实可复刻形态由 /fork-source 的 source 回答,若 M2b 要区分「可源码级改造 / 只能参考」届时再加
- 实施计划(M1)§5:同步去掉 forkSourceAvailable 并注明「未实现、也不需要」,保留其余字段清单不动
- 技术方案 §2.6 时序图:说明字句从「GET …/fork-source 是待实现接口」改为「M2a 已实现(形状见 §3.4 登录用户表)」,并在图内加一行 Note 标注「图中字段名是 M2b 目标形状」;图属目标形态的免责声明保留
- parity 脚本:game_payload 增加 mustEmit ['forkAuthorization']——该字段 TS 侧可选(旧响应可省),但公开与作者响应必须发出,否则详情页授权徽章与改造入口会静默退化成「禁止共创」;public_game_payload 以 game_payload 为 base,因此一并受保护
- e2e 脚本头部:把全部行号引用(含已过期 2–4 行的那些)改为「文件 + 符号」引用(router()/lineage_read_or_not_found()/GameDistributionLineageNode/lineage.rs 常量与函数等),并删除「消费链路在未提交的 M2a WIP 里」这类会过期的措辞,改为「已在 M2a 落地」;只改注释,断言逻辑未动
|
2026-10-05 17:30:47 +08:00 |
|
suzmii
|
6759e56454
|
test(游戏共创): 族谱验收脚本补网页端「改造这个作品」入口断言
- scripts/check-game-distribution-lineage-e2e.mjs 新增 5 条浏览器断言,插入位置在族谱树断言之后、
软删除母版之前(母版一删该入口即消失,顺序不能反):
· 已公开且授权开放的母版详情页出现「改造这个作品」入口
· 展开面板:作品 ID 等于母版;可复制作品链接指向 /games/detail?id=<母版>;面板给出
「打开陶泥儿客户端 → 首页『从平台作品开始创作』→ 粘贴」引导,并给出成品包只能作为
可玩参考与素材、不能直接再次发布的口径
· 点「复制作品 ID」后只断言可观察 UI 反馈(按钮变为「作品 ID 已复制」):真实剪贴板内容
需要额外权限,不在自动化范围,属人工验收项
· 对照断言:已公开但授权为 forbidden 的作品详情页不出现该入口(也不出现「登录后可改造」)
· 展开态截图存临时目录并打印路径
- 入口文案常量做成可覆盖(E2E_REMIX_*),取值对齐 GameDetailPage.tsx:430-490(Commit 392cb3bb0)
- 删除后回看族谱改为导航到先前记录的 lineage URL(原实现用 page.reload,新增入口步骤会先切走页面)
- 实测:本脚本 99 项 99 PASS / 0 FAIL / 0 SKIP;复跑 check:game-distribution-fork-authorization-e2e
48/48 未回归
|
2026-10-05 17:30:47 +08:00 |
|
suzmii
|
9308029276
|
test(游戏共创): 新增创作族谱/衍生列表端到端验收脚本(含 Fork 取件通道)
- 新增 scripts/check-game-distribution-lineage-e2e.mjs:真实 HTTP + 浏览器验收 M3 与 M2a
· 404 规则:未公开草稿锚点与不存在的 gameId,/lineage 与 /derived 都断言 404 且不是成功信封空树,并断言 no-store
· 真实树:完整发布链路(建版本→传包→送审→管理员通过)把母版与带改编声明的子作品推公开,
断言 rootGameId/root/节点集合/generation/parentGameId/status/作者名读取自账号展示名/代际升序/
节点键集合恰好 7 个/响应不含对象键与素材键
· 衍生列表:锚点=母版时恰好含直接子作品且未截断
· 软删除降级:作者软删除母版后 nodes 不再含母版、root=null 但 rootGameId 仍回传、
子节点保留 generation/parentGameId、响应文本不含母版标题与作者名、已删锚点两个接口都 404
· Fork 取件通道(M2a):未公开作品 409、公开但授权 forbidden 403、元数据 sha256/bytes/versionId
与公开版本行一致、downloadPath 为同源相对路径、按该路径下载后校验 content-type/长度/sha256/ZIP 头、
未带 Bearer 401、另一名已登录作者可取件 200
· 浏览器:详情页「查看创作族谱」入口 → /games/lineage 树渲染(母版/当前作品/作者名)→
页面内触发父作品软删除 → 降级为「原作品已不可用」且父标题不再出现,删除前后各留截图
· 节点上限 200 未用真实 201 节点验证:改为源码常量钉住并在脚本里 NOTE 说明降级理由
- package.json:新增 npm script check:game-distribution-lineage-e2e,命名与既有 check:game-distribution-*-e2e 一致
- 实测(本地 dev 栈 + Chrome + 临时 playwright):本脚本 90 项 90 PASS / 0 FAIL;
同时复跑 check:game-distribution-fork-authorization-e2e 48/48 未回归
|
2026-10-05 17:30:46 +08:00 |
|
suzmii
|
8f87c826cf
|
feat(游戏共创): M2a 受鉴权内容下发通道(fork-source 元数据 + 整包取件)
- 契约:新增 `GameDistributionForkSourceKind` / `GameDistributionForkSource` / `GameDistributionForkSourceResponse`(shared-contracts + TS 镜像 + parity 登记),字段 `gameId / versionId / source / sha256 / bytes / downloadPath`,**不含对象键**
- 读取:spacetime-module 新增 `GameDistributionForkSourceInput`、扁平结果类型与 procedure `get_game_distribution_fork_source_and_return`;tx 把「行是否存在 / 能否作为来源(未删+已公开+有当前公开版本)/ 授权档位原值」分开回传——既有 owner-or-public 读会把「已软删除」与「未公开」都折成 None,HTTP 层就没法区分 404 与 409;`sha256` / `bytes` 取自版本行,不重算
- 客户端:spacetime-client 新增 record + mapper + `get_game_distribution_fork_source`(生成绑定把 `package_sha256` 折成 `package_sha_256`,与版本快照同约定)
- 路由:新增 `fork_sources` 子路由(`require_bearer_auth` + `add_no_store_response_headers`,**不叠加发布灰度**),两条:`GET …/fork-source`(元数据)与 `GET …/fork-source/package`(ZIP 字节)
- 校验:抽出纯函数 `fork_source_target` 供两个 handler 共用——404 `FORK_SOURCE_NOT_FOUND` / 409 `FORK_SOURCE_NOT_AVAILABLE` / 403 `FORK_NOT_AUTHORIZED`(未知档位按禁止解释);`available` 为真却缺版本元数据时失败关闭为 409
- 取件本体:复用发行网关的 `release_package_bytes`(整包进内存 + 进程内缓存,4 条 / 256 MiB),不新造 OSS 客户端;缓存值由 `Arc<Vec<u8>>` 改为 `Bytes`,整包交给响应体只加引用计数、不复制;响应**不做**引用归一化与 `RELEASE_STORAGE_BOOTSTRAP` 注入(下发原始构建产物,客户端按摘要校验后离线解压);`RELEASE_PACKAGE_CACHE` 顺手改用 `LazyLock`(初始值编译期已知)
- 路径:`downloadPath` 为同源相对路径,复用新抽出的 `is_path_safe_game_id`(与发行入口同一判据),绝不下发对象键
- 测试:新增 4 条——两条路由未带 Bearer → 401;`fork_source_target` 的 404 / 409 / 403 / 未知档位 / 缺元数据失败关闭逐项断言(409 先于 403);元数据 payload 的 sha256/bytes 取自行、downloadPath 形状、序列化后不含对象键;取件包头与长度(`application/zip`、`Content-Length` = 行 `package_bytes`、`Content-Disposition`、`no-store`)
- 文档:技术方案 §3.4 把两个接口从「规划」改成与实现一致的形状,并单独拆出「登录用户(Bearer,不叠加发布灰度)」小节;§3.5.1 的「下载通道(当前不存在)」改为 M2a 已实现,写明它只服务当前公开版本、不做归一化、大包续传仍待做
|
2026-10-05 17:30:45 +08:00 |
|
suzmii
|
39669763fe
|
实现游戏共创 M3:创作族谱树与衍生列表
- 新增 module-game-distribution/src/lineage.rs:锚点可读性、可见性过滤、稳定排序、上限截断的纯函数,附 15 个单测(复用 M1 的软删除判定,不重写第二套规则)
- 新增公开只读 procedure get_game_distribution_lineage / list_game_distribution_derived_games;锚点必须公开可读(未公开/已软删除/不存在一律按不存在处理),树内只出现未软删除且已公开的节点,不补 null 占位节点
- api-server 新增 GET /games/{id}/lineage 与 /games/{id}/derived(匿名可读、no-store),响应走结构化 DTO,节点键集合由测试钉死,杜绝下发对象键/素材键
- 作者自有游戏聚合追加 forkCount(与公开详情 forkCount 同口径),供「被改编 N」入口使用
- shared-contracts 与 TS 契约新增 LineageNode / LineageResponse / DerivedResponse 三型并登记 DTO 一致性检查
- 前端新增 /games/lineage 族谱页,登记 stage 类型、路由表、页面标题与壳层渲染分支;详情页共创卡新增「查看创作族谱」入口(仅已公开作品)
- 我的作品页新增「被改编 N」行内展开,列出直接子代的标题/作者/代际/状态,含空态与失败重试(仅已公开作品)
- 补前端组件/路由/标题/客户端用例与 api-server 路由、负载映射用例;同步里程碑验收标准与技术方案 §3.4 的锚点可读性口径
|
2026-10-05 17:30:45 +08:00 |
|
suzmii
|
1f243f4edf
|
test(游戏共创): 新增作品级共创授权端到端验收脚本
- 新增 scripts/check-game-distribution-fork-authorization-e2e.mjs(789 行):
真实 HTTP + 浏览器端到端验收作者侧共创授权链路 —— 从 .app/dev-stack.json 读栈地址;
开 game-distribution:publish 灰度;注册两个真实作者;上传封面并创建作品;
断言三态提升阶梯(forbidden→nonCommercial→full)、降级 409、过期 CAS 409、
未知档位 400、非作者 403、未带 Bearer 401、同 key 同 body 重放 replayed=true、
以及越权/非法请求无副作用;浏览器侧用 Playwright 在 /games/mine 断言入口与三态
编辑器(当前档位不可点、更高档位可点)并真跑一次网页提升,截图留档。
- package.json:新增 npm script check:game-distribution-fork-authorization-e2e,
命名与既有 check:game-distribution-*-e2e 保持一致。
- 实测:本地 dev 栈 + Chrome(E2E_PLAYWRIGHT_DIR 临时 playwright)跑 48 项断言
48 PASS / 0 FAIL;npm run check:encoding 通过。
|
2026-10-05 17:30:45 +08:00 |
|
suzmii
|
1e7ec6dbe7
|
实现游戏共创 M1:作品级共创授权与改编血缘
- game_distribution_game 表尾追加 fork_authorization(默认 forbidden,只升不降,旧行自动取默认)
- 新增 game_distribution_lineage 表,记录父作品、来源版本、根作品与代际,并按 parent/root/owner 建索引
- migration.rs 登记新表并注明血缘随迁移导出
- 新增 set_game_distribution_fork_authorization_and_return procedure 与幂等收据、期望值 CAS
- 创建作品时校验改编声明:来源存在、已公开、授权开放、来源版本等于当前公开版本;复用既有身份时拒绝携带血缘
- 公开与后台响应增量:forkAuthorization、forkCount、lineage、generation、forkedFromGameId、derivedCount
- 公开快照的作者名与头像改为读时联 user_account,不再落到「创作者」兜底文案
- api-server 新增 PUT /api/game-distribution/games/{game_id}/fork-authorization(Bearer、幂等键、期望值校验)
- 错误映射新增 FORK_ 前缀分支并置于通用子串分支之前,避免被误判成 409 或 404
- module-game-distribution 增加授权阶梯、代际计算与错误码,并补单测覆盖阶梯与错误码前缀
- 网页端详情页新增共创信息卡,我的作品页新增授权提升入口,补服务层调用与定向测试
- 重新生成 SpacetimeDB 绑定,登记 DTO 一致性脚本的共创类型
|
2026-10-05 17:30:44 +08:00 |
|
suzmii
|
3206f8f49b
|
修复在线游玩加载体验:加载期黑屏/空白与发行资源传输开销
- packages/shared 新增共享游戏加载面 PlatformGameLoadingSurface:封面/标题 + 动效进度 + 按时长推进的阶段文案(准备/资源/引擎)+ 慢加载提示,附用例
- 网页游玩页用共享加载面替换近黑壳上的一行小字,加载上限改为共享常量 PLATFORM_GAME_LOADING_TIMEOUT_MS
- 后台审核试玩补加载面、20 秒超时与「重新创建试玩会话」入口,新会话重置加载态;补两条用例
- 发行网关(api-server)对文本类发行资源按 Accept-Encoding 下发 gzip,并下发强 ETag、命中 If-None-Match 返回 304;补 5 条 Rust 用例
- E2E 脚本 check-game-distribution-ops-rollback-e2e 补压缩/Vary/ETag/304/不接受 gzip 四条断言
- 同步 pitfalls、开发运维文档、nginx README 与两份里程碑记录
|
2026-10-05 17:14:32 +08:00 |
|
kdletters
|
a377ece48e
|
修复 Mac release 构建的 Git 心跳
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m49s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m35s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m19s
Project CI / Backend tests (pull_request) Successful in 8m31s
Project CI / Frontend tests (pull_request) Successful in 3m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m6s
Project CI / Repository checks (pull_request) Successful in 5m21s
Project CI / Native shell tests (pull_request) Successful in 7m3s
为 macOS Jenkins Checkout 增加 SSH 保活与 fetch 心跳。\n增加 fetch 超时诊断并补充生产运维门禁。\n更新 Mac 构建运维说明。
|
2026-10-05 11:47:55 +08:00 |
|
suzmii
|
ab1045f72d
|
Merge remote-tracking branch 'origin/master' into feat/game-works-management
|
2026-10-04 18:28:11 +08:00 |
|
k88936
|
b5a39f425e
|
Merge remote-tracking branch 'origin/master' into test/silent-is-gold
Project CI / Backend tests (pull_request) Failing after 34s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m7s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m13s
Project CI / Repository checks (pull_request) Failing after 21s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m3s
Project CI / Frontend tests (pull_request) Successful in 2m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m4s
Project CI / Native shell tests (pull_request) Successful in 9m0s
|
2026-10-04 15:51:59 +08:00 |
|
k88936
|
84c30b680c
|
配置:CI 测试成功静默改用 cargo/node 原生 quiet
- .gitea/workflows/project-ci.yml:全局加 CARGO_TERM_QUIET=true,cargo test 成功只打点号、失败才列用例名。
- scripts/build-gitea-rust-cache.sh:预热脚本同步 CARGO_TERM_QUIET=true,与 workflow 的 Cargo 环境一致。
- package.json:agc:plugins:test 改用 node --test --test-reporter=dot。
- apps/ai-game-creator-shell/package.json:bundled-resources:test 改用 node --test --test-reporter=dot。
- scripts/check-native-shells.mjs:去掉每个 step 的成功进度标签,失败时才打印标签。
- scripts/project-ci-workflow.test.ts:移除 silent: true 守护用例。
- docs/project-memory/shared-memory/decision-log.md:移除本次静默决策条目。
- docs/project-memory/shared-memory/development-workflow.md:移除本次静默说明段落。
|
2026-10-04 15:49:34 +08:00 |
|
suzmii
|
2f6df57ce2
|
Merge remote-tracking branch 'origin/master' into feat/game-works-management
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m32s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m5s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 3m33s
Project CI / Frontend tests (pull_request) Successful in 3m39s
Project CI / Backend tests (pull_request) Successful in 6m49s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m39s
Project CI / Repository checks (pull_request) Successful in 6m43s
Project CI / Native shell tests (pull_request) Successful in 7m39s
# Conflicts:
# docs/project-memory/shared-memory/decision-log.md
# docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md
# server-rs/crates/api-server/src/modules/game_distribution.rs
# server-rs/crates/spacetime-client/src/active.rs
# server-rs/crates/spacetime-module/src/game_distribution.rs
# src/components/game-distribution/GameDistributionPages.test.tsx
|
2026-10-04 15:00:23 +08:00 |
|
k88936
|
8e304f2b22
|
配置:CI 测试固定 silent: true,统一静默成功用例输出
Project CI / Backend tests (pull_request) Failing after 35s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m37s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m43s
Project CI / Repository checks (pull_request) Failing after 26s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m46s
Project CI / Frontend tests (pull_request) Successful in 2m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m9s
Project CI / Native shell tests (pull_request) Successful in 5m44s
|
2026-10-04 13:38:51 +08:00 |
|
k88936
|
9ce43071ad
|
Add gamePlayClientId.ts to module and test exclusions
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m14s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m54s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m0s
Project CI / Backend tests (pull_request) Successful in 6m54s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Native shell tests (pull_request) Successful in 6m47s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m8s
Project CI / Repository checks (pull_request) Successful in 5m9s
|
2026-10-04 12:53:17 +08:00 |
|
k88936
|
57e259278d
|
Merge remote-tracking branch 'origin/master' into feat/play-count-with-cache
Project CI / Backend tests (pull_request) Failing after 36s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m28s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m41s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
# Conflicts:
# docs/project-memory/shared-memory/decision-log.md
|
2026-10-04 11:58:25 +08:00 |
|
suzmii
|
8abc35158c
|
Merge branch 'master' into feat/game-works-management
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m44s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m2s
Project CI / Frontend tests (pull_request) Successful in 2m21s
Project CI / Backend tests (pull_request) Successful in 6m47s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m51s
Project CI / Native shell tests (pull_request) Successful in 6m18s
Project CI / Repository checks (pull_request) Successful in 5m5s
|
2026-10-04 09:23:45 +08:00 |
|
suzmii
|
ad0430fcd2
|
补上收银台深链 /pay/<checkoutToken> 的前缀路由(Nginx 三模板 + Pingora + 门禁)
- 现状:只把 `/pay`、`/profile/payment` 加进 allowlist 只能让 check:nginx-spa-routes 变绿;
payment.rs 生成的 checkoutUrl 是 `/pay/<checkoutToken>`,深链仍落默认 location 的
try_files → 404。同一批漂移里 check:pingora-route-parity 也是红的(Pingora MAIN_SPA_PATHS
缺 /pay、/profile/payment),只是被 lint 链里先失败的门禁掩盖,修一条要跑到链尾
- 真相源:src/routing/activeAppPageRoutes.ts 新增 APP_PREFIX_ROUTE_ENTRIES
('/pay' → payment-checkout),resolveSelectionStageFromPath 改用它
- 门禁:scripts/check-nginx-spa-routes.mjs 要求三份模板都有锚定前缀 location
`location ~* "^/pay/[^/]+/?$"`(裸前缀仍由精确 location 负责;前缀 location 必须镜像精确
location 的维护闸与 try_files 回退);新增 scripts/check-nginx-spa-routes.test.mjs 正/反用例
(把前缀写成精确匹配或过宽裸前缀都会红),由 npm run check:nginx-spa-routes 一起执行;
check-pingora-route-parity 新增 MAIN_SPA_PREFIX_PATHS 与前端前缀路由的逐条比对
- 模板:deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、
deploy/container/nginx.conf 各加一条锚定前缀 location
- Pingora:MAIN_SPA_PATHS 补 /pay、/profile/payment;新增 MAIN_SPA_PREFIX_PATHS 与
is_main_spa_prefix_path(大小写不敏感,只认「前缀 + 恰好一段」),矩阵新增
pay_checkout_spa_fallback 用例,并给网关补一条前缀正/反单测
- 文档:Pingora 试点文档的路由表与门禁说明、deploy/nginx/README 与本地开发/生产运维文档
同步前缀路由口径与线上 curl 复验方式
- 本地实跑:node --test scripts/check-nginx-spa-routes.test.mjs(4 passed)、
node scripts/check-nginx-spa-routes.mjs(OK,14 SPA routes / 1 prefix routes / 3 templates)、
npm run check:pingora-route-parity(OK,25 routes)、
cargo test -p pingora-gateway -- pay_checkout_deep_link matches_nginx_route_parity_matrix(2 passed)
|
2026-10-04 03:25:54 +08:00 |
|
suzmii
|
72d6ea2180
|
Merge remote-tracking branch 'origin/master' into feat/game-works-management
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m5s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 3m28s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m5s
Project CI / Frontend tests (pull_request) Successful in 2m43s
Project CI / Backend tests (pull_request) Successful in 6m12s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m37s
Project CI / Repository checks (pull_request) Failing after 3m54s
Project CI / Native shell tests (pull_request) Successful in 5m59s
|
2026-10-03 17:06:04 +08:00 |
|
suzmii
|
1621c29cf0
|
Merge remote-tracking branch 'origin/master' into feat/game-works-management
|
2026-10-03 15:43:20 +08:00 |
|
k88936
|
0e313405d6
|
修复:生成绑定校验不再用 prettier 改写生成文件
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m54s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m36s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m44s
Project CI / Backend tests (pull_request) Successful in 5m46s
Project CI / Frontend tests (pull_request) Successful in 2m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m41s
Project CI / Native shell tests (pull_request) Successful in 6m39s
Project CI / Repository checks (pull_request) Successful in 6m12s
- scripts/check-generated-bindings.mjs:删掉生成后的 prettier 归一化,直接逐字节比较 ts-rs 原始输出
- .prettierignore 与 .eslintrc.cjs:四个 ts-rs 生成目录统一排除,禁止格式化工具二次改写
- .prettierrc.json:删除 contracts/generated 的 printWidth / singleQuote 覆盖
- .gitattributes:生成目录标 linguist-generated 并豁免 trailing-space,保证 git diff --check 通过
- 重新生成 AGC chat/generated 与 services/generated 的 51 个绑定,改为提交 ts-rs 原始输出(纯格式,语义不变)
- docs/project-memory:记录生成绑定不再经 prettier 的口径,修正旧流程说明
|
2026-10-03 15:40:21 +08:00 |
|
k88936
|
141e59c971
|
Merge remote-tracking branch 'origin/master' into refactor/turn-error-to-explicit-struct
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m0s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 2m45s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m52s
Project CI / Frontend tests (pull_request) Successful in 2m30s
Project CI / Backend tests (pull_request) Successful in 4m54s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m0s
Project CI / Native shell tests (pull_request) Successful in 7m38s
Project CI / Repository checks (pull_request) Successful in 6m34s
|
2026-10-03 14:50:04 +08:00 |
|
lhk229
|
ae2d1a3a21
|
修复 AGC 生成准备依赖全量素材库的问题 (#592)
Project CI / AI game creator shell Rust crates (push) Successful in 1m47s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 5m1s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 5m18s
Project CI / Frontend tests (push) Successful in 2m27s
Project CI / Backend tests (push) Successful in 5m49s
Project CI / Native shell tests (push) Successful in 6m21s
Project CI / Repository checks (push) Successful in 3m36s
Project CI / AI game creator shell web tests (push) Successful in 2m54s
修复已有项目和素材目录绑定时,AGC 生成准备仍读取完整账户素材库、导致无关素材读取失败阻断生成的问题。
- 新增站内与 External v1 的单目录 GET,按主键读取并校验账号归属;复用目录 DTO,不读取素材列表或创建默认目录。
- 已有 binding 和 partial 恢复改为确认目标目录;首次建绑直接沿用稳定幂等创建,不再读取全库。
- 目录缺失、越权、超时、响应身份不符或会话切换时停止,保留恢复证据,不自动重建、不换键重发生成;已有生成账本继续沿用冻结快照恢复。
- 既有素材库与目录创建、修改、删除 API 合同保持不变,持久化表结构不变。同步 OpenAPI、生成绑定及技术文档。
验证:
- AGC Rust:目录绑定 7 项、生成 94 项、资源编辑 60 项、抠图 5 项通过(目录绑定用例包含在生成组中)。
- api-server:External Editor 30 项、受保护路由矩阵 3 项通过。
- 真实隔离 SpacetimeDB:目录读取成功、空目录、缺失、越权、删除后缺失、只读不变,以及既有幂等/并发/孤儿检查通过。
- smoke 脚本测试 4 项通过;module 编译、生成绑定、87 张表 schema guard、DDD、编码、文档索引、定向 Rust 格式与 diff 检查通过。
- 已比较 OpenAPI:除新增单目录 GET 外,原有定义逐项保持一致。
验证限制:未完成独立 API `/healthz` 联调,本地隔离数据库与授权配置未就绪;启动尝试已停止并清理。未运行全量测试或真实付费生成。
发布顺序:先发布后端模块与 API,再发布新客户端;不提供全库回退。#574 的素材分页、筛选及按素材 ID 读取不在本 PR 范围内。
Closes #550
Reviewed-on: #592
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
|
2026-10-03 14:48:54 +08:00 |
|
k88936
|
3ab313a11a
|
修复:生成绑定校验在 Windows 上无法执行 npx.cmd
- check-generated-bindings.mjs:run() 的 spawnSync 增加 shell: process.platform === 'win32'
- Windows 上不带 shell 无法执行 .cmd/.bat(Node 修 CVE-2024-27980 后报 EINVAL),npxCommand 特意换成 npx.cmd 只把 ENOENT 换成了 EINVAL,prettier 这一步仍然跑不起来
- 与 scripts/database-backup-to-oss.mjs、lint-staged-rustfmt.mjs 等既有脚本的写法保持一致
- Linux 侧已跑 check:generated-bindings 验证;Windows 路径本机无法实测
|
2026-10-03 13:47:43 +08:00 |
|
k88936
|
6a4bcf4f31
|
重构:生成绑定校验不再依赖 readdirSync 的 recursive 选项
- check-generated-bindings.mjs:snapshot() 改用普通 fs.readdirSync(absolute)
- recursive 选项要 Node 18.17 / 20.1 才有;仓库没有 engines 约束,旧版本会静默退化成只读顶层,反而削弱嵌套目录的覆盖
- 四个生成目录都是平铺的(ts-rs 的 export_to 直接落文件),去掉后行为不变,已跑 check:generated-bindings 验证
|
2026-10-03 13:47:21 +08:00 |
|
k88936
|
7fb4dabb55
|
修复:生成绑定校验在目录缺失时不再静默通过
- check-generated-bindings.mjs:snapshot() 遇到目标目录不存在时由 console.warn + 返回空 Map 改为抛出错误
- 旧实现下,若 export_to 路径被改名或生成文件被整体删除,前后快照都是空 Map,diff 报不出任何问题,校验以"0 个文件"静默通过
- 现在目录缺失会立即失败并指出是哪个目录,不再给假绿
|
2026-10-03 13:47:02 +08:00 |
|
k88936
|
d7d09f310c
|
合并:把 origin/master 并入回合错误重构分支并保留双方行为
- 解决 direct_runtime/mod.rs 冲突:保留本分支 typed 回合错误重构(TurnError/EnqueueError 与 record_direct_codex_failure_facts 拆分),接入 master 的诊断 v3 口径(persist_agent_runtime_error 新签名 + typed error 字段)、prompt 反馈改截断不脱敏、项目写锁改名
- 解决 thread_manager/dispatch.rs 冲突:保留本分支 TurnError/TurnCompletion 换代与 finish_turn_failure 分类层,接住 master 的 tt= 日志改名与 cc 执行器整轮成功后补写 completed 终态
- 解决 direct_tools_mcp.rs 冲突:采用 master 版(本分支唯一改动是 external MCP 容量测试读 body,master 已有等价实现)
- 处理 direct_turn_failure.rs 删除/修改冲突:维持本分支删除,把 completed() 语义迁进 TurnCompletion::Completed 并补回对应测试
- 处理 runtime_driver/entrypoints.rs 修改/删除冲突:接受 master 退役自建 Agent Runtime 的删除(本分支只在该文件做 direct_now_ms 改名)
- 合并 decision-log.md:双方条目都保留
- 修复 auto-merge 漏掉的语义冲突:claude_code_cli.rs 的 direct_now_ms() 改名 now_ms();诊断测试改为断言 AGENT_RUNTIME_ERROR_SCHEMA_VERSION、detail 字段改 message;record_direct_codex_failure_facts 新增 typed_error 入参(TurnError 不序列化,落盘用 classify 投影出的 TurnFailure)
|
2026-10-03 13:12:10 +08:00 |
|
suzmii
|
783ca014ef
|
合并 master 到作品管理分支
|
2026-10-03 12:30:07 +08:00 |
|
k88936
|
80afa2a139
|
修复:生成绑定校验不再因首个目标失败就中止
- 原来每个目标里直接 process.exit,第一个目标(shared-contracts)失败时 AGC 目标根本不会被检查,得反复重跑才知道还有哪些绑定陈旧
- 改为记录 failed 后 continue,跑完全部目标再统一退出 1,两个绑定集的错误一次报齐
|
2026-10-02 22:31:02 +08:00 |
|
k88936
|
fff4e8ff68
|
修复:生成绑定校验的目录快照支持子目录并规范化路径
- snapshot() 原来只列目录下一层文件,绑定若放进子目录就两边快照都看不见,陈旧绑定会被漏掉
- 改用 readdirSync 的 recursive 列出全部条目,再按文件过滤并归一到 POSIX 分隔符,键形态与原来保持兼容
|
2026-10-02 22:30:50 +08:00 |
|
k88936
|
f593bd4bf6
|
修复:生成绑定校验对不存在的目录不再静默通过
- snapshot() 遇到配置目录不存在时原来直接返回空表,空表比空表会被当成"通过(0 个文件)"
- 目录写错或已删除时改为打印"目录不存在,跳过校验",避免整组绑定被悄悄跳过
|
2026-10-02 22:29:12 +08:00 |
|
k88936
|
6f2eed0dc2
|
修复:生成绑定校验无法启动 cargo/npx 时暴露真实原因
- spawnSync 连命令都拉不起来(如 ENOENT)时 status 为 null、error 非空,原来只报一句"未通过",看不到原因
- run() 改为在 result.error 时先打印"无法启动 <命令>:<原因>",spawn 失败与测试失败可区分
|
2026-10-02 22:29:08 +08:00 |
|
k88936
|
bfbcb1dd02
|
文档:修正生成绑定校验提示里缺少的 cargo 参数
- shared-contracts 提示补上 --manifest-path server-rs/Cargo.toml:仓库根没有 Cargo.toml,照抄提示会直接失败
- 两条提示都补上 --locked,与实际执行的 cargo 命令保持一致
|
2026-10-02 22:29:02 +08:00 |
|
k88936
|
09f007d519
|
修复生成绑定校验在 Windows 上启动不了 npx
- check-generated-bindings.mjs:prettier 归一化改用平台命令名 npx.cmd / npx,spawnSync 无 shell 时 Windows 解析不到裸 npx
|
2026-10-02 22:26:56 +08:00 |
|
k88936
|
b7224aecef
|
构建:生成绑定校验覆盖 AGC shell 生成目录
- check-generated-bindings 同时覆盖 shared-contracts 与 AGC 的 chat/generated、services/generated、ui-editor/types
- ts-rs 重新生成后先按仓库格式跑 prettier 再逐字节比较,消除"格式不同"的假漂移
- 校验失败时按目标给出对应的重新生成命令
|
2026-10-02 18:10:00 +08:00 |
|
suzmii
|
db344426f2
|
修复发行游戏在沙箱中访问存储报错
将运行期存储兼容层保存为独立 JavaScript 文件,通过 include_str 注入发行 HTML。
保持 opaque sandbox 隔离,仅提供当前运行实例的 localStorage 与 sessionStorage 兼容能力。
同步发行沙箱验收脚本和玩法运行契约文档。
|
2026-10-02 17:06:46 +08:00 |
|